Changeset 460 for TFP-WebServer


Ignore:
Timestamp:
Aug 11, 2023, 6:29:09 PM (17 months ago)
Author:
alloc
Message:

More OpenAPI specs added
OpenAPI specs cleanup to have everything validate fine
Added CORS support to API endpoints

Location:
TFP-WebServer
Files:
6 added
9 edited

Legend:

Unmodified
Added
Removed
  • TFP-WebServer/MarkersMod/src/Markers.openapi.yaml

    r459 r460  
    22info:
    33  title: Markers
    4   version: 1
     4  version: '1'
    55
    66components:
     
    8080                $ref: '#/components/schemas/MarkersList'
    8181              meta:
    82                 $ref: '#/components/schemas/ResultEnvelopeMeta'
     82                $ref: './openapi.yaml#/components/schemas/ResultEnvelopeMeta'
    8383            required:
    8484              - data
     
    9696                format: uuid
    9797              meta:
    98                 $ref: '#/components/schemas/ResultEnvelopeMeta'
     98                $ref: './openapi.yaml#/components/schemas/ResultEnvelopeMeta'
    9999            required:
    100100              - data
     
    110110        type: string
    111111        format: uuid
     112      description: ID of marker to fetch
    112113
    113114
     
    118119        - Map
    119120      summary: Markers list
    120       description: Fetch a list of the all defined map markers
     121      description: Fetch a list of all defined map markers
    121122      operationId: markers.get
    122123      responses:
     
    141142            Invalid request body, errorCode will be one of 'NO_OR_INVALID_X',
    142143            'NO_OR_INVALID_Y'
    143           $ref: '#/components/responses/HttpEmptyEnvelopedResponse'
     144          $ref: './openapi.yaml#/components/responses/HttpEmptyEnvelopedResponse'
    144145        403:
    145           $ref: '#/components/responses/Unauthorized'
     146          $ref: './openapi.yaml#/components/responses/Unauthorized'
    146147      security:
    147148        - apiTokenName: []
     
    164165        404:
    165166          description: Marker ID not found, errorCode will be 'ID_NOT_FOUND'
    166           $ref: '#/components/responses/HttpEmptyEnvelopedResponse'
     167          $ref: './openapi.yaml#/components/responses/HttpEmptyEnvelopedResponse'
    167168
    168169    put:
     
    184185            Invalid request body, errorCode will be one of 'NO_OR_INVALID_X',
    185186            'NO_OR_INVALID_Y'
    186           $ref: '#/components/responses/HttpEmptyEnvelopedResponse'
     187          $ref: './openapi.yaml#/components/responses/HttpEmptyEnvelopedResponse'
    187188        404:
    188189          description: Marker ID not found, errorCode will be 'ID_NOT_FOUND'
    189           $ref: '#/components/responses/HttpEmptyEnvelopedResponse'
     190          $ref: './openapi.yaml#/components/responses/HttpEmptyEnvelopedResponse'
    190191        403:
    191           $ref: '#/components/responses/Unauthorized'
     192          $ref: './openapi.yaml#/components/responses/Unauthorized'
    192193      security:
    193194        - apiTokenName: []
     
    206207        204:
    207208          description: Deleted marker
    208           $ref: '#/components/responses/HttpEmptyEnvelopedResponse'
     209          $ref: './openapi.yaml#/components/responses/HttpEmptyEnvelopedResponse'
    209210        404:
    210211          description: Marker ID not found
    211           $ref: '#/components/responses/HttpEmptyEnvelopedResponse'
     212          $ref: './openapi.yaml#/components/responses/HttpEmptyEnvelopedResponse'
    212213        403:
    213           $ref: '#/components/responses/Unauthorized'
     214          $ref: './openapi.yaml#/components/responses/Unauthorized'
    214215      security:
    215216        - apiTokenName: []
  • TFP-WebServer/WebServer/WebServer.csproj

    r459 r460  
    198198  </ItemGroup>
    199199  <ItemGroup>
     200    <EmbeddedResource Include="src\WebAPI\APIs\GameData\Item.openapi.yaml" />
     201    <EmbeddedResource Include="src\WebAPI\APIs\GameData\Mods.openapi.yaml" />
     202    <EmbeddedResource Include="src\WebAPI\APIs\Log.openapi.yaml" />
     203    <EmbeddedResource Include="src\WebAPI\APIs\Command.openapi.yaml" />
     204    <EmbeddedResource Include="src\WebAPI\APIs\Permissions\Blacklist.openapi.yaml" />
    200205    <EmbeddedResource Include="src\WebAPI\APIs\WorldState\Animal.openapi.yaml" />
    201206    <EmbeddedResource Include="src\WebAPI\OpenAPI.master.yaml" />
  • TFP-WebServer/WebServer/src/UrlHandlers/ApiHandler.cs

    r459 r460  
    5353                private static readonly UnityEngine.Profiling.CustomSampler apiHandlerSampler = UnityEngine.Profiling.CustomSampler.Create ("API_Handler");
    5454
     55                private bool HandleCors (RequestContext _context) {
     56                        _context.Request.Headers.TryGetValue ("Origin", out string origin);
     57                        _context.Response.AddHeader ("Access-Control-Allow-Origin", origin ?? "*");
     58
     59                        if (_context.Method != ERequestMethod.OPTIONS) {
     60                                return false;
     61                        }
     62
     63                        if (!_context.Request.Headers.TryGetValue ("Access-Control-Request-Method", out _)) {
     64                                return false;
     65                        }
     66
     67                        _context.Response.AddHeader ("Access-Control-Allow-Methods", "GET, PUT, POST, DELETE, OPTIONS, HEAD");
     68                        _context.Response.AddHeader ("Access-Control-Allow-Headers", "X-SDTD-API-TOKENNAME, X-SDTD-API-SECRET");
     69                        _context.Response.AddHeader ("Access-Control-Allow-Credentials", "true");
     70                        return true;
     71                }
     72
    5573                public override void HandleRequest (RequestContext _context) {
    5674
     
    7290                        }
    7391
     92                        // CORS specific stuff
     93                        if (HandleCors (_context)) {
     94                                return;
     95                        }
     96                        // CORS end
     97                       
    7498                        _context.RequestPath = subPath;
    7599
  • TFP-WebServer/WebServer/src/WebAPI/APIs/OpenAPI.openapi.yaml

    r459 r460  
    22info:
    33  title: OpenAPI
    4   version: 1
     4  version: '1'
    55
    66
     
    88  /api/OpenAPI/openapi.yaml:
    99    get:
     10      tags:
     11        - Meta
     12      summary: OpenAPI master document
     13      description: Fetch the OpenAPI master document, referencing all the individual WebAPI specs
    1014      operationId: OpenAPI.openapi
    1115      responses:
     
    1418            application/json: {}
    1519          description: OpenAPI master document
     20  /api/OpenAPI/{name}.openapi.yaml:
     21    get:
    1622      tags:
    1723        - Meta
    18   /api/OpenAPI/{name}.openapi.yaml:
    19     get:
     24      summary: OpenAPI spec for single API
     25      description: Fetch the OpenAPI spec of a single WebAPI
    2026      operationId: OpenAPI.getAPI
    2127      parameters:
     
    3541            application/json: {}
    3642          description: No OpenAPI spec found for the API name
    37       tags:
    38         - Meta
  • TFP-WebServer/WebServer/src/WebAPI/APIs/Permissions/Blacklist.cs

    r434 r460  
    100100                        }
    101101
    102                         bool validId = PlatformUserIdentifierAbs.TryFromCombinedString (id, out _userId);
    103                         if (!validId) {
     102                        if (PlatformUserIdentifierAbs.TryFromCombinedString (id, out _userId)) {
    104103                                SendEmptyResponse (_context, HttpStatusCode.BadRequest, _jsonInputData, "INVALID_USER");
     104                                return false;
    105105                        }
    106106
    107                         return validId;
     107                        return true;
    108108                }
    109109
  • TFP-WebServer/WebServer/src/WebAPI/APIs/WorldState/Animal.openapi.yaml

    r459 r460  
    22info:
    33  title: Animal
    4   version: 1
     4  version: '1'
    55
    66components:
     
    1010      properties:
    1111        id:
    12           $ref: '#/components/schemas/TypeEntityId'
     12          $ref: './openapi.yaml#/components/schemas/TypeEntityId'
    1313        name:
    1414          type: string
     
    1616            - animalStag
    1717        position:
    18           $ref: '#/components/schemas/TypeVector3i'
     18          $ref: './openapi.yaml#/components/schemas/TypeVector3i'
    1919      required:
    2020        - id
     
    4747                    $ref: '#/components/schemas/AnimalList'
    4848                  meta:
    49                     $ref: '#/components/schemas/ResultEnvelopeMeta'
     49                    $ref: './openapi.yaml#/components/schemas/ResultEnvelopeMeta'
    5050                required:
    5151                  - data
    5252                  - meta
    5353        403:
    54           $ref: '#/components/responses/Unauthorized'
     54          $ref: './openapi.yaml#/components/responses/Unauthorized'
    5555      security:
    5656        - apiTokenName: []
  • TFP-WebServer/WebServer/src/WebAPI/OpenAPI.master.yaml

    r459 r460  
     1openapi: 3.1.0
    12info:
    23  title: 7 Days To Die WebAPI
    3   version: 1.0.0
    4 openapi: 3.1.0
     4  version: '1.0.0'
    55servers:
    66  - url: /
     
    8181      description: 3D vector in full blocks
    8282
     83    TypeUserIdString:
     84      type: string
     85      pattern: '^\[a-zA-Z]+_[\w]+$'
     86      examples:
     87        - 'Steam_76561198021925107'
     88      description: User ID including platform
     89
     90    TypeUserIdObject:
     91      anyOf:
     92        - type: object
     93          properties:
     94            combinedString:
     95              $ref: '#/components/schemas/TypeUserIdString'
     96            platformId:
     97              type: string
     98              examples:
     99                - Steam
     100                - XBL
     101              description: Gaming platform the ID references
     102            userId:
     103              type: string
     104              examples:
     105                - '76561198021925107'
     106              description: Unique ID of the user within the given gaming platform
     107          required:
     108            - combinedString
     109            - platformId
     110            - userId
     111          description: User ID
     112        - type: 'null'
     113
    83114
    84115  responses:
     
    126157
    127158paths:
    128   allOf:
  • TFP-WebServer/WebServer/src/WebAPI/OpenApiHelpers.cs

    r459 r460  
    33using System.Reflection;
    44using System.Text;
     5using System.Text.RegularExpressions;
    56
    67namespace Webserver.WebAPI {
     
    1213                private const string masterDocRefLineEnd = "#/paths";
    1314
    14                 private readonly Dictionary<string, string> specs = new CaseInsensitiveStringDictionary<string> ();
     15                private struct OpenApiSpec {
     16                        public readonly List<string> ExportedPaths;
     17                        public readonly string Spec;
     18
     19                        public OpenApiSpec (string _spec, List<string> _exportedPaths = null) {
     20                                ExportedPaths = _exportedPaths;
     21                                Spec = _spec;
     22                        }
     23                }
     24
     25                private readonly Dictionary<string, OpenApiSpec> specs = new CaseInsensitiveStringDictionary<OpenApiSpec> ();
    1526
    1627                public OpenApiHelpers () {
     
    3041                        }
    3142
    32                         specs.Add (masterDocName, specText);
     43                        specs.Add (masterDocName, new OpenApiSpec(specText));
    3344                        Log.Out ($"[Web] Loaded main OpenAPI spec");
    3445                }
     
    4152                        StringBuilder sb = new StringBuilder (mainSpec);
    4253                       
    43                         foreach ((string apiSpecName, _) in specs) {
     54                        foreach ((string apiSpecName, OpenApiSpec spec) in specs) {
    4455                                if (apiSpecName.Equals (masterDocName)) {
    4556                                        continue;
    4657                                }
    4758
    48                                 sb.AppendLine ($"{masterDocRefLineBegin}./{apiSpecName}{masterDocRefLineEnd}");
    49                         }
    50 
    51                         specs[masterDocName] = sb.ToString ();
     59                                if ((spec.ExportedPaths?.Count ?? 0) < 1) {
     60                                        continue;
     61                                }
     62
     63                                foreach (string exportedPath in spec.ExportedPaths) {
     64                                        writePath (sb, apiSpecName, exportedPath);
     65                                }
     66                        }
     67
     68                        specs[masterDocName] = new OpenApiSpec(sb.ToString ());
    5269                       
    5370                        Log.Out ("[Web] OpenAPI preparation done");
     71                }
     72
     73                private void writePath (StringBuilder _sb, string _apiSpecName, string _exportedPath) {
     74                        _sb.AppendLine ($"  {_exportedPath}:");
     75                        _sb.Append ($"    $ref: './{_apiSpecName}#/paths/");
     76
     77                        for (int i = 0; i < _exportedPath.Length; i++) {
     78                                char c = _exportedPath[i];
     79
     80                                switch (c) {
     81                                        // JSON string escaped characters
     82                                        case '"':
     83                                                _sb.Append ("\\\"");
     84                                                break;
     85                                        case '\\':
     86                                                _sb.Append ("\\\\");
     87                                                break;
     88                                        case '\b':
     89                                                _sb.Append ("\\b");
     90                                                break;
     91                                        case '\f':
     92                                                _sb.Append ("\\f");
     93                                                break;
     94                                        case '\n':
     95                                                _sb.Append ("\\n");
     96                                                break;
     97                                        case '\r':
     98                                                _sb.Append ("\\r");
     99                                                break;
     100                                        case '\t':
     101                                                _sb.Append ("\\t");
     102                                                break;
     103                                        case (char)0x00:
     104                                                _sb.Append ("\\u0000");
     105                                                break;
     106                                        case (char)0x01:
     107                                                _sb.Append ("\\u0001");
     108                                                break;
     109                                        case (char)0x02:
     110                                                _sb.Append ("\\u0002");
     111                                                break;
     112                                        case (char)0x03:
     113                                                _sb.Append ("\\u0003");
     114                                                break;
     115                                        case (char)0x04:
     116                                                _sb.Append ("\\u0004");
     117                                                break;
     118                                        case (char)0x05:
     119                                                _sb.Append ("\\u0005");
     120                                                break;
     121                                        case (char)0x06:
     122                                                _sb.Append ("\\u0006");
     123                                                break;
     124                                        case (char)0x07:
     125                                                _sb.Append ("\\u0007");
     126                                                break;
     127                                        case (char)0x0b:
     128                                                _sb.Append ("\\u000b");
     129                                                break;
     130                                        case (char)0x0e:
     131                                                _sb.Append ("\\u000e");
     132                                                break;
     133                                        case (char)0x0f:
     134                                                _sb.Append ("\\u000f");
     135                                                break;
     136                                        case (char)0x10:
     137                                                _sb.Append ("\\u0010");
     138                                                break;
     139                                        case (char)0x11:
     140                                                _sb.Append ("\\u0011");
     141                                                break;
     142                                        case (char)0x12:
     143                                                _sb.Append ("\\u0012");
     144                                                break;
     145                                        case (char)0x13:
     146                                                _sb.Append ("\\u0013");
     147                                                break;
     148                                        case (char)0x14:
     149                                                _sb.Append ("\\u0014");
     150                                                break;
     151                                        case (char)0x15:
     152                                                _sb.Append ("\\u0015");
     153                                                break;
     154                                        case (char)0x16:
     155                                                _sb.Append ("\\u0016");
     156                                                break;
     157                                        case (char)0x17:
     158                                                _sb.Append ("\\u0017");
     159                                                break;
     160                                        case (char)0x18:
     161                                                _sb.Append ("\\u0018");
     162                                                break;
     163                                        case (char)0x19:
     164                                                _sb.Append ("\\u0019");
     165                                                break;
     166                                        case (char)0x1a:
     167                                                _sb.Append ("\\u001a");
     168                                                break;
     169                                        case (char)0x1b:
     170                                                _sb.Append ("\\u001b");
     171                                                break;
     172                                        case (char)0x1c:
     173                                                _sb.Append ("\\u001c");
     174                                                break;
     175                                        case (char)0x1d:
     176                                                _sb.Append ("\\u001d");
     177                                                break;
     178                                        case (char)0x1e:
     179                                                _sb.Append ("\\u001e");
     180                                                break;
     181                                        case (char)0x1f:
     182                                                _sb.Append ("\\u001f");
     183                                                break;
     184                                        // JSON Pointer specific
     185                                        case '/':
     186                                                _sb.Append ("~1");
     187                                                break;
     188                                        case '~':
     189                                                _sb.Append ("~0");
     190                                                break;
     191                                        // Non escaped characters
     192                                        default:
     193                                                _sb.Append (c);
     194                                                break;
     195                                }
     196                        }
     197                       
     198                        _sb.AppendLine ("'");
    54199                }
    55200
     
    65210
    66211                        Log.Out ($"[Web] Loaded OpenAPI spec for '{apiName}'");
    67                         specs.Add (apiSpecName, specText);
     212                        OpenApiSpec spec = new OpenApiSpec (specText, findExportedPaths (specText));
     213                        specs.Add (apiSpecName, spec);
     214                }
     215
     216                private static readonly Regex pathMatcher = new Regex ("^\\s{1,2}(/\\S+):.*$", RegexOptions.Compiled | RegexOptions.CultureInvariant);
     217                private List<string> findExportedPaths (string _spec) {
     218                        List<string> result = new List<string> ();
     219
     220                        using TextReader tr = new StringReader (_spec);
     221
     222                        string line;
     223                        bool inPaths = false;
     224                        while ((line = tr.ReadLine ()) != null) {
     225                                if (!inPaths) {
     226                                        if (line.StartsWith ("paths:")) {
     227                                                inPaths = true;
     228                                        }
     229                                } else {
     230                                        Match match = pathMatcher.Match (line);
     231                                        if (!match.Success) {
     232                                                continue;
     233                                        }
     234
     235                                        string path = match.Groups [1].Value;
     236                                        Log.Out ($"[Web]   Exports: {path}");
     237                                        result.Add (path);
     238                                }
     239                        }
     240
     241                        return result;
    68242                }
    69243
     
    73247                        }
    74248
    75                         return specs.TryGetValue (_name, out _specText);
     249                        if (!specs.TryGetValue (_name, out OpenApiSpec spec)) {
     250                                _specText = null;
     251                                return false;
     252                        }
     253
     254                        _specText = spec.Spec;
     255                        return true;
    76256                }
    77257        }
  • TFP-WebServer/WebServer/webroot/rapidoc.html

    r459 r460  
    77  </head>
    88  <body>
    9     <rapi-doc spec-url="/api/openapi/openapi.yaml" render-style="focused" show-header="false" show-method-in-nav-bar="as-colored-block" > </rapi-doc>
     9    <rapi-doc spec-url="/api/openapi/openapi.yaml" render-style="focused" show-header="false" show-method-in-nav-bar="as-colored-block" sort-tags="true" schema-description-expanded="true" > </rapi-doc>
    1010  </body>
    1111</html>
Note: See TracChangeset for help on using the changeset viewer.