intellifactory / sitelets

Sitelets for ASP.NET Core
1 stars 2 forks source link

Add OpenApi Middleware for SwaggerUI support #6

Open mrtank opened 3 years ago

mrtank commented 3 years ago

Using Sitelet's Endpoint's type it's possible to generate an OpenAPI documentation for simple cases. The documentation for OpenAPI documentation can be found here. With SwaggerUI it's possible to provide a documentation endpoint and offer test calls against the API.

.UseOpenApi(endpointsType: Type, config: OpenApiIntegration.GenerateOpenApiConfig)

is an extension method on IEndpointRouteBuilder, which configures the site to route /swagger.json calls to provide the generated OpenAPI documentation for the user.

    type GenerateOpenApiConfig = {
            Version: string
            Title: string
            ServerUrls: string []
            SerializerOptions: JsonSerializerOptions
        }

config contains the required Info Object's Version and Title fields, ServerUrls and JsonSerializerOptions for ISerializerDataContractResolver. JsonSerializerOptions requires https://github.com/Tarmil/FSharp.SystemTextJson.

To set up SwaggerUI .UseSwaggerUI needs to point to /swagger.json.

Example code on an IApplicationBuilder to setup /swagger endpoint for SwaggerUI

open System.Text.Json
open System.Text.Json.Serialization
open Microsoft.AspNetCore.Builder
...
let servers = [| "http://localhost:5000"; "https://localhost:5001" |]
let serializerOptions = 
    let options = JsonSerializerOptions (PropertyNamingPolicy=JsonNamingPolicy.CamelCase)
    options.Converters.Add( JsonStringEnumConverter())
    options.Converters.Add(
        JsonFSharpConverter(
            JsonUnionEncoding.ExternalTag
            ||| JsonUnionEncoding.NamedFields
            ||| JsonUnionEncoding.UnwrapFieldlessTags
            ||| JsonUnionEncoding.UnwrapOption))
    options
...
app
    .UseRouting()
    .UseEndpoints(fun e ->
        e.UseOpenApi(typeof<MyEndpoint>, {
            Version = "1.0.0.0"
            Title = "Title"
            ServerUrls = servers
            SerializerOptions = serializerOptions
            }))
    .UseSwaggerUI(fun c -> c.SwaggerEndpoint("/swagger.json", "My API V1"))
    ...

Documentation generation is restricted to an Endpoint type of Discriminated Union, with primitive system type union fields. int, float, string.

Example Endpoint type:

type Endpoint =
    | [<EndPoint "GET /echo">] Data of msg:string * value:int
    | [<EndPoint "GET /actionresult">] ActionResult
    | [<EndPoint "GET /task/str">] Task1
    | [<EndPoint "GET /task/actionresult">] Task2
    | [<EndPoint "GET /task/enumerable">] Task3
    | [<EndPoint "GET /enum">] Enumerable1
    | [<EndPoint "GET /enum/async">] Enumerable2
    | Test1
    | Test2

Where no annotation defines the Operation (for example Get/Post), the default is Get. Parameter name defaults to it's Type's .Name.

mrtank commented 3 years ago

for the examples the following nuget versions were used>

    <PackageReference Include="FSharp.SystemTextJson" Version="0.17.4" />
    <PackageReference Include="Microsoft.OpenApi" Version="1.2.3" />
    <PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="6.2.1" />
    <PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="6.2.1" />