mirror of
https://github.com/Smaug123/WoofWare.Myriad
synced 2025-10-08 13:38:39 +00:00
Add HTTP Swagger client generator (#250)
This commit is contained in:
@@ -56,6 +56,17 @@
|
|||||||
<Compile Include="GeneratedArgs.fs">
|
<Compile Include="GeneratedArgs.fs">
|
||||||
<MyriadFile>Args.fs</MyriadFile>
|
<MyriadFile>Args.fs</MyriadFile>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<None Include="swagger-gitea.json" />
|
||||||
|
<Compile Include="GeneratedSwaggerGitea.fs">
|
||||||
|
<MyriadFile>swagger-gitea.json</MyriadFile>
|
||||||
|
<MyriadParams>
|
||||||
|
<GenerateMockInternal>true</GenerateMockInternal>
|
||||||
|
<ClassName>Gitea</ClassName>
|
||||||
|
</MyriadParams>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Generated2SwaggerGitea.fs">
|
||||||
|
<MyriadFile>GeneratedSwaggerGitea.fs</MyriadFile>
|
||||||
|
</Compile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
42464
ConsumePlugin/Generated2SwaggerGitea.fs
Normal file
42464
ConsumePlugin/Generated2SwaggerGitea.fs
Normal file
File diff suppressed because it is too large
Load Diff
6432
ConsumePlugin/GeneratedSwaggerGitea.fs
Normal file
6432
ConsumePlugin/GeneratedSwaggerGitea.fs
Normal file
File diff suppressed because it is too large
Load Diff
21054
ConsumePlugin/swagger-gitea.json
Normal file
21054
ConsumePlugin/swagger-gitea.json
Normal file
File diff suppressed because it is too large
Load Diff
90
README.md
90
README.md
@@ -14,7 +14,8 @@ Currently implemented:
|
|||||||
* `JsonSerialize` (to stamp out `toJsonNode : 'T -> JsonNode` methods).
|
* `JsonSerialize` (to stamp out `toJsonNode : 'T -> JsonNode` methods).
|
||||||
* `HttpClient` (to stamp out a [RestEase](https://github.com/canton7/RestEase)-style HTTP client).
|
* `HttpClient` (to stamp out a [RestEase](https://github.com/canton7/RestEase)-style HTTP client).
|
||||||
* `GenerateMock` (to stamp out a record type corresponding to an interface, like a compile-time [Foq](https://github.com/fsprojects/Foq)).
|
* `GenerateMock` (to stamp out a record type corresponding to an interface, like a compile-time [Foq](https://github.com/fsprojects/Foq)).
|
||||||
* `ArgParser` (to stamp out a basic argument parser)
|
* `ArgParser` (to stamp out a basic argument parser).
|
||||||
|
* `SwaggerClient` (to stamp out an HTTP client for a Swagger API).
|
||||||
* `CreateCatamorphism` (to stamp out a non-stack-overflowing [catamorphism](https://fsharpforfunandprofit.com/posts/recursive-types-and-folds/) for a discriminated union).
|
* `CreateCatamorphism` (to stamp out a non-stack-overflowing [catamorphism](https://fsharpforfunandprofit.com/posts/recursive-types-and-folds/) for a discriminated union).
|
||||||
* `RemoveOptions` (to strip `option` modifiers from a type) - this one is particularly half-baked!
|
* `RemoveOptions` (to strip `option` modifiers from a type) - this one is particularly half-baked!
|
||||||
|
|
||||||
@@ -156,6 +157,10 @@ For an example of using both `JsonParse` and `JsonSerialize` together with compl
|
|||||||
Takes a record like this:
|
Takes a record like this:
|
||||||
|
|
||||||
```fsharp
|
```fsharp
|
||||||
|
type DryRunMode =
|
||||||
|
| [<ArgumentFlag true> Dry
|
||||||
|
| [<ArgumentFlag false> Wet
|
||||||
|
|
||||||
[<ArgParser>]
|
[<ArgParser>]
|
||||||
type Foo =
|
type Foo =
|
||||||
{
|
{
|
||||||
@@ -166,12 +171,16 @@ type Foo =
|
|||||||
B : Choice<int, int>
|
B : Choice<int, int>
|
||||||
[<ArgumentDefaultEnvironmentVariable "MY_ENV_VAR">]
|
[<ArgumentDefaultEnvironmentVariable "MY_ENV_VAR">]
|
||||||
BWithEnv : Choice<int, int>
|
BWithEnv : Choice<int, int>
|
||||||
|
[<ArgumentDefaultFunction>]
|
||||||
|
DryRun : DryRunMode
|
||||||
|
[<ArgumentLongForm "longer-form-replaces-c">]
|
||||||
C : float list
|
C : float list
|
||||||
// optionally:
|
// optionally:
|
||||||
[<PositionalArgs>]
|
[<PositionalArgs>]
|
||||||
Rest : string list // or e.g. `int list` if you want them parsed into a type too
|
Rest : string list // or e.g. `int list` if you want them parsed into a type too
|
||||||
}
|
}
|
||||||
static member DefaultB () = 4
|
static member DefaultB () = 4
|
||||||
|
static member DefaultDryRun () = DryRunMode.Wet
|
||||||
```
|
```
|
||||||
|
|
||||||
and stamps out a basic `parse` method of this signature:
|
and stamps out a basic `parse` method of this signature:
|
||||||
@@ -218,6 +227,85 @@ This is very bare-bones, but do raise GitHub issues if you like (or if you find
|
|||||||
|
|
||||||
It should work fine if you just want to compose a few primitive types, though.
|
It should work fine if you just want to compose a few primitive types, though.
|
||||||
|
|
||||||
|
## `SwaggerClient`
|
||||||
|
|
||||||
|
Takes a JSON-schema definition of a [Swagger API](https://swagger.io/), and stamps out a client like this:
|
||||||
|
|
||||||
|
```fsharp
|
||||||
|
/// A type which was defined in the Swagger spec
|
||||||
|
[<JsonParse true ; JsonSerialize true>]
|
||||||
|
type SwaggerType1 =
|
||||||
|
{
|
||||||
|
[<System.Text.Json.Serialization.JsonExtensionData>]
|
||||||
|
AdditionalProperties : System.Collections.Generic.Dictionary<string, System.Text.Json.Nodes.JsonNode>
|
||||||
|
Message : string
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Documentation from the Swagger spec
|
||||||
|
[<HttpClient false ; RestEase.BasePath "/api/v1">]
|
||||||
|
type IGitea =
|
||||||
|
/// Returns the Person actor for a user
|
||||||
|
[<RestEase.Get "/activitypub/user/{username}">]
|
||||||
|
abstract ActivitypubPerson :
|
||||||
|
[<RestEase.Path "username">] username : string * ?ct : System.Threading.CancellationToken ->
|
||||||
|
ActivityPub System.Threading.Tasks.Task
|
||||||
|
```
|
||||||
|
|
||||||
|
Notice that we automatically decorate the type with our `[<HttpClient>]` attribute, so if you choose to do so, you can chain another Myriad generated file off this one and you'll get a RestEase-style client stamped out.
|
||||||
|
(See below, searching on the string `"Generated2SwaggerGitea.fs"`, for an example.)
|
||||||
|
|
||||||
|
You don't need to `Content Include` or `EmbeddedResource Include` the JSON schema.
|
||||||
|
`None Include` will do; we only need the source to be available at build time.
|
||||||
|
|
||||||
|
You *do* need to include the following configuration:
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<Compile Include="GeneratedClient.fs">
|
||||||
|
<!-- This bit is normal: -->
|
||||||
|
<MyriadFile>swagger.json</MyriadFile>
|
||||||
|
<!-- This bit is new and required! -->
|
||||||
|
<MyriadParams>
|
||||||
|
<ClassName>GiteaClient</ClassName>
|
||||||
|
<!-- Optionally: -->
|
||||||
|
<GenerateMock>true</GenerateMock>
|
||||||
|
</MyriadParams>
|
||||||
|
</Compile>
|
||||||
|
```
|
||||||
|
|
||||||
|
The `<ClassName />` key tells us what to name the resulting interface (it gets an `I` prepended for you).
|
||||||
|
You can optionally also set `<GenerateMockVisibility>v</GenerateMockVisibility>` to add the `[<GenerateMock>]` attribute to the type
|
||||||
|
(where `v` should be `internal` or `public`, indicating "resulting mock type is internal" vs "is public"),
|
||||||
|
so that the following manoeuvre will result in a generated mock:
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<None Include="swagger-gitea.json" />
|
||||||
|
<Compile Include="GeneratedSwaggerGitea.fs">
|
||||||
|
<MyriadFile>swagger-gitea.json</MyriadFile>
|
||||||
|
<MyriadParams>
|
||||||
|
<GenerateMockVisibility>public</GenerateMockVisibility>
|
||||||
|
<ClassName>Gitea</ClassName>
|
||||||
|
</MyriadParams>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Generated2SwaggerGitea.fs">
|
||||||
|
<MyriadFile>GeneratedSwaggerGitea.fs</MyriadFile>
|
||||||
|
</Compile>
|
||||||
|
```
|
||||||
|
|
||||||
|
(Note that you do have to create the `GeneratedSwaggerGitea.fs` file manually before code generation happens. Myriad will throw if that file isn't there, because `Generated2SwaggerGitea.fs` depends on it so Myriad wants to compute its hash. Just make an empty file.)
|
||||||
|
|
||||||
|
### What's the point?
|
||||||
|
|
||||||
|
[`SwaggerProvider`](https://github.com/fsprojects/SwaggerProvider) is *absolutely magical*, but it's kind of witchcraft.
|
||||||
|
I fear no man, but that thing… it scares me.
|
||||||
|
|
||||||
|
Also, builds using `SwaggerProvider` appear to be inherently nondeterministic, even if the data source doesn't change.
|
||||||
|
|
||||||
|
## Limitations
|
||||||
|
|
||||||
|
Swagger API specs appear to be pretty cowboy in the wild.
|
||||||
|
I try to cope with invalid schemas I have seen, but I can't guarantee I do so correctly.
|
||||||
|
Definitely do perform integration tests and let me know of weird specs you encounter, and bits of the (very extensive) Swagger spec I have omitted!
|
||||||
|
|
||||||
## `RemoveOptions`
|
## `RemoveOptions`
|
||||||
|
|
||||||
Takes a record like this:
|
Takes a record like this:
|
||||||
|
84
WoofWare.Myriad.Plugins.Test/TestSwagger/TestSwaggerParse.fs
Normal file
84
WoofWare.Myriad.Plugins.Test/TestSwagger/TestSwaggerParse.fs
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
namespace WoofWare.Myriad.Plugins.Test
|
||||||
|
|
||||||
|
open System.Text.Json.Nodes
|
||||||
|
open NUnit.Framework
|
||||||
|
open FsUnitTyped
|
||||||
|
open WoofWare.Myriad.Plugins
|
||||||
|
|
||||||
|
[<TestFixture>]
|
||||||
|
module TestSwaggerParse =
|
||||||
|
[<Test>]
|
||||||
|
let ``Can parse parameters`` () : unit =
|
||||||
|
let s =
|
||||||
|
"""{
|
||||||
|
"tags": [
|
||||||
|
"organization"
|
||||||
|
],
|
||||||
|
"summary": "Check if a user is a member of an organization",
|
||||||
|
"operationId": "orgIsMember",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "name of the organization",
|
||||||
|
"name": "org",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "username of the user",
|
||||||
|
"name": "username",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"204": {
|
||||||
|
"description": "user is a member"
|
||||||
|
},
|
||||||
|
"303": {
|
||||||
|
"description": "redirection to /orgs/{org}/public_members/{username}"
|
||||||
|
},
|
||||||
|
"404": {
|
||||||
|
"description": "user is not a member"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|> JsonNode.Parse
|
||||||
|
|
||||||
|
s.AsObject ()
|
||||||
|
|> SwaggerEndpoint.Parse
|
||||||
|
|> shouldEqual
|
||||||
|
{
|
||||||
|
Consumes = None
|
||||||
|
Produces = None
|
||||||
|
Tags = [ "organization" ]
|
||||||
|
Summary = "Check if a user is a member of an organization"
|
||||||
|
OperationId = OperationId "orgIsMember"
|
||||||
|
Parameters =
|
||||||
|
[
|
||||||
|
{
|
||||||
|
Type = Definition.String
|
||||||
|
Description = Some "name of the organization"
|
||||||
|
Name = "org"
|
||||||
|
In = ParameterIn.Path "org"
|
||||||
|
Required = Some true
|
||||||
|
}
|
||||||
|
{
|
||||||
|
Type = Definition.String
|
||||||
|
Description = Some "username of the user"
|
||||||
|
Name = "username"
|
||||||
|
In = ParameterIn.Path "username"
|
||||||
|
Required = Some true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|> Some
|
||||||
|
Responses =
|
||||||
|
[
|
||||||
|
204, Definition.Unspecified
|
||||||
|
303, Definition.Unspecified
|
||||||
|
404, Definition.Unspecified
|
||||||
|
]
|
||||||
|
|> Map.ofList
|
||||||
|
}
|
@@ -33,6 +33,7 @@
|
|||||||
<Compile Include="TestCataGenerator\TestMyList.fs" />
|
<Compile Include="TestCataGenerator\TestMyList.fs" />
|
||||||
<Compile Include="TestCataGenerator\TestMyList2.fs" />
|
<Compile Include="TestCataGenerator\TestMyList2.fs" />
|
||||||
<Compile Include="TestArgParser\TestArgParser.fs" />
|
<Compile Include="TestArgParser\TestArgParser.fs" />
|
||||||
|
<Compile Include="TestSwagger\TestSwaggerParse.fs" />
|
||||||
<Compile Include="TestRemoveOptions.fs"/>
|
<Compile Include="TestRemoveOptions.fs"/>
|
||||||
<Compile Include="TestSurface.fs"/>
|
<Compile Include="TestSurface.fs"/>
|
||||||
<None Include="../.github/workflows/dotnet.yaml" />
|
<None Include="../.github/workflows/dotnet.yaml" />
|
||||||
|
@@ -12,3 +12,12 @@ module private List =
|
|||||||
)
|
)
|
||||||
|
|
||||||
List.rev xs, List.rev ys
|
List.rev xs, List.rev ys
|
||||||
|
|
||||||
|
let allSome<'a> (l : 'a option list) : 'a list option =
|
||||||
|
let rec go acc (l : 'a option list) =
|
||||||
|
match l with
|
||||||
|
| [] -> Some (List.rev acc)
|
||||||
|
| None :: _ -> None
|
||||||
|
| Some head :: tail -> go (head :: acc) tail
|
||||||
|
|
||||||
|
go [] l
|
||||||
|
@@ -1,17 +1,298 @@
|
|||||||
|
WoofWare.Myriad.Plugins.AdditionalProperties inherit obj, implements WoofWare.Myriad.Plugins.AdditionalProperties System.IEquatable, System.Collections.IStructuralEquatable - union type with 2 cases
|
||||||
|
WoofWare.Myriad.Plugins.AdditionalProperties+Constrained inherit WoofWare.Myriad.Plugins.AdditionalProperties
|
||||||
|
WoofWare.Myriad.Plugins.AdditionalProperties+Constrained.get_Item [method]: unit -> WoofWare.Myriad.Plugins.Definition
|
||||||
|
WoofWare.Myriad.Plugins.AdditionalProperties+Constrained.Item [property]: [read-only] WoofWare.Myriad.Plugins.Definition
|
||||||
|
WoofWare.Myriad.Plugins.AdditionalProperties+Tags inherit obj
|
||||||
|
WoofWare.Myriad.Plugins.AdditionalProperties+Tags.Constrained [static field]: int = 1
|
||||||
|
WoofWare.Myriad.Plugins.AdditionalProperties+Tags.Never [static field]: int = 0
|
||||||
|
WoofWare.Myriad.Plugins.AdditionalProperties.Equals [method]: (WoofWare.Myriad.Plugins.AdditionalProperties, System.Collections.IEqualityComparer) -> bool
|
||||||
|
WoofWare.Myriad.Plugins.AdditionalProperties.get_IsConstrained [method]: unit -> bool
|
||||||
|
WoofWare.Myriad.Plugins.AdditionalProperties.get_IsNever [method]: unit -> bool
|
||||||
|
WoofWare.Myriad.Plugins.AdditionalProperties.get_Never [static method]: unit -> WoofWare.Myriad.Plugins.AdditionalProperties
|
||||||
|
WoofWare.Myriad.Plugins.AdditionalProperties.get_Tag [method]: unit -> int
|
||||||
|
WoofWare.Myriad.Plugins.AdditionalProperties.IsConstrained [property]: [read-only] bool
|
||||||
|
WoofWare.Myriad.Plugins.AdditionalProperties.IsNever [property]: [read-only] bool
|
||||||
|
WoofWare.Myriad.Plugins.AdditionalProperties.Never [static property]: [read-only] WoofWare.Myriad.Plugins.AdditionalProperties
|
||||||
|
WoofWare.Myriad.Plugins.AdditionalProperties.NewConstrained [static method]: WoofWare.Myriad.Plugins.Definition -> WoofWare.Myriad.Plugins.AdditionalProperties
|
||||||
|
WoofWare.Myriad.Plugins.AdditionalProperties.Tag [property]: [read-only] int
|
||||||
WoofWare.Myriad.Plugins.ArgParserGenerator inherit obj, implements Myriad.Core.IMyriadGenerator
|
WoofWare.Myriad.Plugins.ArgParserGenerator inherit obj, implements Myriad.Core.IMyriadGenerator
|
||||||
WoofWare.Myriad.Plugins.ArgParserGenerator..ctor [constructor]: unit
|
WoofWare.Myriad.Plugins.ArgParserGenerator..ctor [constructor]: unit
|
||||||
|
WoofWare.Myriad.Plugins.ArrayTypeDefinition inherit obj, implements WoofWare.Myriad.Plugins.ArrayTypeDefinition System.IEquatable, System.Collections.IStructuralEquatable
|
||||||
|
WoofWare.Myriad.Plugins.ArrayTypeDefinition..ctor [constructor]: WoofWare.Myriad.Plugins.Definition
|
||||||
|
WoofWare.Myriad.Plugins.ArrayTypeDefinition.Equals [method]: (WoofWare.Myriad.Plugins.ArrayTypeDefinition, System.Collections.IEqualityComparer) -> bool
|
||||||
|
WoofWare.Myriad.Plugins.ArrayTypeDefinition.get_Items [method]: unit -> WoofWare.Myriad.Plugins.Definition
|
||||||
|
WoofWare.Myriad.Plugins.ArrayTypeDefinition.Items [property]: [read-only] WoofWare.Myriad.Plugins.Definition
|
||||||
|
WoofWare.Myriad.Plugins.ArrayTypeDefinition.Parse [static method]: System.Text.Json.Nodes.JsonNode -> WoofWare.Myriad.Plugins.ArrayTypeDefinition
|
||||||
WoofWare.Myriad.Plugins.CreateCatamorphismGenerator inherit obj, implements Myriad.Core.IMyriadGenerator
|
WoofWare.Myriad.Plugins.CreateCatamorphismGenerator inherit obj, implements Myriad.Core.IMyriadGenerator
|
||||||
WoofWare.Myriad.Plugins.CreateCatamorphismGenerator..ctor [constructor]: unit
|
WoofWare.Myriad.Plugins.CreateCatamorphismGenerator..ctor [constructor]: unit
|
||||||
|
WoofWare.Myriad.Plugins.Definition inherit obj, implements WoofWare.Myriad.Plugins.Definition System.IEquatable, System.Collections.IStructuralEquatable - union type with 8 cases
|
||||||
|
WoofWare.Myriad.Plugins.Definition+Array inherit WoofWare.Myriad.Plugins.Definition
|
||||||
|
WoofWare.Myriad.Plugins.Definition+Array.get_Item [method]: unit -> WoofWare.Myriad.Plugins.ArrayTypeDefinition
|
||||||
|
WoofWare.Myriad.Plugins.Definition+Array.Item [property]: [read-only] WoofWare.Myriad.Plugins.ArrayTypeDefinition
|
||||||
|
WoofWare.Myriad.Plugins.Definition+Handle inherit WoofWare.Myriad.Plugins.Definition
|
||||||
|
WoofWare.Myriad.Plugins.Definition+Handle.get_Item [method]: unit -> string
|
||||||
|
WoofWare.Myriad.Plugins.Definition+Handle.Item [property]: [read-only] string
|
||||||
|
WoofWare.Myriad.Plugins.Definition+Integer inherit WoofWare.Myriad.Plugins.Definition
|
||||||
|
WoofWare.Myriad.Plugins.Definition+Integer.format [property]: [read-only] string option
|
||||||
|
WoofWare.Myriad.Plugins.Definition+Integer.get_format [method]: unit -> string option
|
||||||
|
WoofWare.Myriad.Plugins.Definition+Object inherit WoofWare.Myriad.Plugins.Definition
|
||||||
|
WoofWare.Myriad.Plugins.Definition+Object.get_Item [method]: unit -> WoofWare.Myriad.Plugins.ObjectTypeDefinition
|
||||||
|
WoofWare.Myriad.Plugins.Definition+Object.Item [property]: [read-only] WoofWare.Myriad.Plugins.ObjectTypeDefinition
|
||||||
|
WoofWare.Myriad.Plugins.Definition+Tags inherit obj
|
||||||
|
WoofWare.Myriad.Plugins.Definition+Tags.Array [static field]: int = 2
|
||||||
|
WoofWare.Myriad.Plugins.Definition+Tags.Boolean [static field]: int = 4
|
||||||
|
WoofWare.Myriad.Plugins.Definition+Tags.File [static field]: int = 7
|
||||||
|
WoofWare.Myriad.Plugins.Definition+Tags.Handle [static field]: int = 0
|
||||||
|
WoofWare.Myriad.Plugins.Definition+Tags.Integer [static field]: int = 6
|
||||||
|
WoofWare.Myriad.Plugins.Definition+Tags.Object [static field]: int = 1
|
||||||
|
WoofWare.Myriad.Plugins.Definition+Tags.String [static field]: int = 3
|
||||||
|
WoofWare.Myriad.Plugins.Definition+Tags.Unspecified [static field]: int = 5
|
||||||
|
WoofWare.Myriad.Plugins.Definition.Boolean [static property]: [read-only] WoofWare.Myriad.Plugins.Definition
|
||||||
|
WoofWare.Myriad.Plugins.Definition.Equals [method]: (WoofWare.Myriad.Plugins.Definition, System.Collections.IEqualityComparer) -> bool
|
||||||
|
WoofWare.Myriad.Plugins.Definition.File [static property]: [read-only] WoofWare.Myriad.Plugins.Definition
|
||||||
|
WoofWare.Myriad.Plugins.Definition.get_Boolean [static method]: unit -> WoofWare.Myriad.Plugins.Definition
|
||||||
|
WoofWare.Myriad.Plugins.Definition.get_File [static method]: unit -> WoofWare.Myriad.Plugins.Definition
|
||||||
|
WoofWare.Myriad.Plugins.Definition.get_IsArray [method]: unit -> bool
|
||||||
|
WoofWare.Myriad.Plugins.Definition.get_IsBoolean [method]: unit -> bool
|
||||||
|
WoofWare.Myriad.Plugins.Definition.get_IsFile [method]: unit -> bool
|
||||||
|
WoofWare.Myriad.Plugins.Definition.get_IsHandle [method]: unit -> bool
|
||||||
|
WoofWare.Myriad.Plugins.Definition.get_IsInteger [method]: unit -> bool
|
||||||
|
WoofWare.Myriad.Plugins.Definition.get_IsObject [method]: unit -> bool
|
||||||
|
WoofWare.Myriad.Plugins.Definition.get_IsString [method]: unit -> bool
|
||||||
|
WoofWare.Myriad.Plugins.Definition.get_IsUnspecified [method]: unit -> bool
|
||||||
|
WoofWare.Myriad.Plugins.Definition.get_String [static method]: unit -> WoofWare.Myriad.Plugins.Definition
|
||||||
|
WoofWare.Myriad.Plugins.Definition.get_Tag [method]: unit -> int
|
||||||
|
WoofWare.Myriad.Plugins.Definition.get_Unspecified [static method]: unit -> WoofWare.Myriad.Plugins.Definition
|
||||||
|
WoofWare.Myriad.Plugins.Definition.IsArray [property]: [read-only] bool
|
||||||
|
WoofWare.Myriad.Plugins.Definition.IsBoolean [property]: [read-only] bool
|
||||||
|
WoofWare.Myriad.Plugins.Definition.IsFile [property]: [read-only] bool
|
||||||
|
WoofWare.Myriad.Plugins.Definition.IsHandle [property]: [read-only] bool
|
||||||
|
WoofWare.Myriad.Plugins.Definition.IsInteger [property]: [read-only] bool
|
||||||
|
WoofWare.Myriad.Plugins.Definition.IsObject [property]: [read-only] bool
|
||||||
|
WoofWare.Myriad.Plugins.Definition.IsString [property]: [read-only] bool
|
||||||
|
WoofWare.Myriad.Plugins.Definition.IsUnspecified [property]: [read-only] bool
|
||||||
|
WoofWare.Myriad.Plugins.Definition.NewArray [static method]: WoofWare.Myriad.Plugins.ArrayTypeDefinition -> WoofWare.Myriad.Plugins.Definition
|
||||||
|
WoofWare.Myriad.Plugins.Definition.NewHandle [static method]: string -> WoofWare.Myriad.Plugins.Definition
|
||||||
|
WoofWare.Myriad.Plugins.Definition.NewInteger [static method]: string option -> WoofWare.Myriad.Plugins.Definition
|
||||||
|
WoofWare.Myriad.Plugins.Definition.NewObject [static method]: WoofWare.Myriad.Plugins.ObjectTypeDefinition -> WoofWare.Myriad.Plugins.Definition
|
||||||
|
WoofWare.Myriad.Plugins.Definition.Parse [static method]: System.Text.Json.Nodes.JsonObject -> WoofWare.Myriad.Plugins.Definition
|
||||||
|
WoofWare.Myriad.Plugins.Definition.String [static property]: [read-only] WoofWare.Myriad.Plugins.Definition
|
||||||
|
WoofWare.Myriad.Plugins.Definition.Tag [property]: [read-only] int
|
||||||
|
WoofWare.Myriad.Plugins.Definition.Unspecified [static property]: [read-only] WoofWare.Myriad.Plugins.Definition
|
||||||
WoofWare.Myriad.Plugins.HttpClientGenerator inherit obj, implements Myriad.Core.IMyriadGenerator
|
WoofWare.Myriad.Plugins.HttpClientGenerator inherit obj, implements Myriad.Core.IMyriadGenerator
|
||||||
WoofWare.Myriad.Plugins.HttpClientGenerator..ctor [constructor]: unit
|
WoofWare.Myriad.Plugins.HttpClientGenerator..ctor [constructor]: unit
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod inherit obj, implements WoofWare.Myriad.Plugins.HttpMethod System.IEquatable, System.Collections.IStructuralEquatable, WoofWare.Myriad.Plugins.HttpMethod System.IComparable, System.IComparable, System.Collections.IStructuralComparable - union type with 8 cases
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod+Tags inherit obj
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod+Tags.Delete [static field]: int = 2
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod+Tags.Get [static field]: int = 0
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod+Tags.Head [static field]: int = 5
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod+Tags.Options [static field]: int = 4
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod+Tags.Patch [static field]: int = 3
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod+Tags.Post [static field]: int = 1
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod+Tags.Put [static field]: int = 6
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod+Tags.Trace [static field]: int = 7
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.Delete [static property]: [read-only] WoofWare.Myriad.Plugins.HttpMethod
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.Equals [method]: (WoofWare.Myriad.Plugins.HttpMethod, System.Collections.IEqualityComparer) -> bool
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.Get [static property]: [read-only] WoofWare.Myriad.Plugins.HttpMethod
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.get_Delete [static method]: unit -> WoofWare.Myriad.Plugins.HttpMethod
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.get_Get [static method]: unit -> WoofWare.Myriad.Plugins.HttpMethod
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.get_Head [static method]: unit -> WoofWare.Myriad.Plugins.HttpMethod
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.get_IsDelete [method]: unit -> bool
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.get_IsGet [method]: unit -> bool
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.get_IsHead [method]: unit -> bool
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.get_IsOptions [method]: unit -> bool
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.get_IsPatch [method]: unit -> bool
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.get_IsPost [method]: unit -> bool
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.get_IsPut [method]: unit -> bool
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.get_IsTrace [method]: unit -> bool
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.get_Options [static method]: unit -> WoofWare.Myriad.Plugins.HttpMethod
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.get_Patch [static method]: unit -> WoofWare.Myriad.Plugins.HttpMethod
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.get_Post [static method]: unit -> WoofWare.Myriad.Plugins.HttpMethod
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.get_Put [static method]: unit -> WoofWare.Myriad.Plugins.HttpMethod
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.get_Tag [method]: unit -> int
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.get_Trace [static method]: unit -> WoofWare.Myriad.Plugins.HttpMethod
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.Head [static property]: [read-only] WoofWare.Myriad.Plugins.HttpMethod
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.IsDelete [property]: [read-only] bool
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.IsGet [property]: [read-only] bool
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.IsHead [property]: [read-only] bool
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.IsOptions [property]: [read-only] bool
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.IsPatch [property]: [read-only] bool
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.IsPost [property]: [read-only] bool
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.IsPut [property]: [read-only] bool
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.IsTrace [property]: [read-only] bool
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.Options [static property]: [read-only] WoofWare.Myriad.Plugins.HttpMethod
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.Parse [static method]: string -> WoofWare.Myriad.Plugins.HttpMethod
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.Patch [static property]: [read-only] WoofWare.Myriad.Plugins.HttpMethod
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.Post [static property]: [read-only] WoofWare.Myriad.Plugins.HttpMethod
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.Put [static property]: [read-only] WoofWare.Myriad.Plugins.HttpMethod
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.Tag [property]: [read-only] int
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.ToDotNet [method]: unit -> System.Net.Http.HttpMethod
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.Trace [static property]: [read-only] WoofWare.Myriad.Plugins.HttpMethod
|
||||||
WoofWare.Myriad.Plugins.InterfaceMockGenerator inherit obj, implements Myriad.Core.IMyriadGenerator
|
WoofWare.Myriad.Plugins.InterfaceMockGenerator inherit obj, implements Myriad.Core.IMyriadGenerator
|
||||||
WoofWare.Myriad.Plugins.InterfaceMockGenerator..ctor [constructor]: unit
|
WoofWare.Myriad.Plugins.InterfaceMockGenerator..ctor [constructor]: unit
|
||||||
WoofWare.Myriad.Plugins.JsonParseGenerator inherit obj, implements Myriad.Core.IMyriadGenerator
|
WoofWare.Myriad.Plugins.JsonParseGenerator inherit obj, implements Myriad.Core.IMyriadGenerator
|
||||||
WoofWare.Myriad.Plugins.JsonParseGenerator..ctor [constructor]: unit
|
WoofWare.Myriad.Plugins.JsonParseGenerator..ctor [constructor]: unit
|
||||||
WoofWare.Myriad.Plugins.JsonSerializeGenerator inherit obj, implements Myriad.Core.IMyriadGenerator
|
WoofWare.Myriad.Plugins.JsonSerializeGenerator inherit obj, implements Myriad.Core.IMyriadGenerator
|
||||||
WoofWare.Myriad.Plugins.JsonSerializeGenerator..ctor [constructor]: unit
|
WoofWare.Myriad.Plugins.JsonSerializeGenerator..ctor [constructor]: unit
|
||||||
|
WoofWare.Myriad.Plugins.MimeType inherit obj, implements WoofWare.Myriad.Plugins.MimeType System.IEquatable, System.Collections.IStructuralEquatable, WoofWare.Myriad.Plugins.MimeType System.IComparable, System.IComparable, System.Collections.IStructuralComparable - union type with 1 cases
|
||||||
|
WoofWare.Myriad.Plugins.MimeType.Equals [method]: (WoofWare.Myriad.Plugins.MimeType, System.Collections.IEqualityComparer) -> bool
|
||||||
|
WoofWare.Myriad.Plugins.MimeType.get_Item [method]: unit -> string
|
||||||
|
WoofWare.Myriad.Plugins.MimeType.get_Tag [method]: unit -> int
|
||||||
|
WoofWare.Myriad.Plugins.MimeType.Item [property]: [read-only] string
|
||||||
|
WoofWare.Myriad.Plugins.MimeType.NewMimeType [static method]: string -> WoofWare.Myriad.Plugins.MimeType
|
||||||
|
WoofWare.Myriad.Plugins.MimeType.Tag [property]: [read-only] int
|
||||||
|
WoofWare.Myriad.Plugins.ObjectTypeDefinition inherit obj, implements WoofWare.Myriad.Plugins.ObjectTypeDefinition System.IEquatable, System.Collections.IStructuralEquatable
|
||||||
|
WoofWare.Myriad.Plugins.ObjectTypeDefinition..ctor [constructor]: (string option, Map<string, WoofWare.Myriad.Plugins.Definition> option, Map<string, System.Text.Json.Nodes.JsonNode>, string list option, WoofWare.Myriad.Plugins.AdditionalProperties option, System.Text.Json.Nodes.JsonObject option)
|
||||||
|
WoofWare.Myriad.Plugins.ObjectTypeDefinition.AdditionalProperties [property]: [read-only] WoofWare.Myriad.Plugins.AdditionalProperties option
|
||||||
|
WoofWare.Myriad.Plugins.ObjectTypeDefinition.Description [property]: [read-only] string option
|
||||||
|
WoofWare.Myriad.Plugins.ObjectTypeDefinition.Equals [method]: (WoofWare.Myriad.Plugins.ObjectTypeDefinition, System.Collections.IEqualityComparer) -> bool
|
||||||
|
WoofWare.Myriad.Plugins.ObjectTypeDefinition.Example [property]: [read-only] System.Text.Json.Nodes.JsonObject option
|
||||||
|
WoofWare.Myriad.Plugins.ObjectTypeDefinition.Extras [property]: [read-only] Map<string, System.Text.Json.Nodes.JsonNode>
|
||||||
|
WoofWare.Myriad.Plugins.ObjectTypeDefinition.get_AdditionalProperties [method]: unit -> WoofWare.Myriad.Plugins.AdditionalProperties option
|
||||||
|
WoofWare.Myriad.Plugins.ObjectTypeDefinition.get_Description [method]: unit -> string option
|
||||||
|
WoofWare.Myriad.Plugins.ObjectTypeDefinition.get_Example [method]: unit -> System.Text.Json.Nodes.JsonObject option
|
||||||
|
WoofWare.Myriad.Plugins.ObjectTypeDefinition.get_Extras [method]: unit -> Map<string, System.Text.Json.Nodes.JsonNode>
|
||||||
|
WoofWare.Myriad.Plugins.ObjectTypeDefinition.get_Properties [method]: unit -> Map<string, WoofWare.Myriad.Plugins.Definition> option
|
||||||
|
WoofWare.Myriad.Plugins.ObjectTypeDefinition.get_Required [method]: unit -> string list option
|
||||||
|
WoofWare.Myriad.Plugins.ObjectTypeDefinition.Parse [static method]: System.Text.Json.Nodes.JsonObject -> WoofWare.Myriad.Plugins.ObjectTypeDefinition
|
||||||
|
WoofWare.Myriad.Plugins.ObjectTypeDefinition.Properties [property]: [read-only] Map<string, WoofWare.Myriad.Plugins.Definition> option
|
||||||
|
WoofWare.Myriad.Plugins.ObjectTypeDefinition.Required [property]: [read-only] string list option
|
||||||
|
WoofWare.Myriad.Plugins.OperationId inherit obj, implements WoofWare.Myriad.Plugins.OperationId System.IEquatable, System.Collections.IStructuralEquatable, WoofWare.Myriad.Plugins.OperationId System.IComparable, System.IComparable, System.Collections.IStructuralComparable - union type with 1 cases
|
||||||
|
WoofWare.Myriad.Plugins.OperationId.Equals [method]: (WoofWare.Myriad.Plugins.OperationId, System.Collections.IEqualityComparer) -> bool
|
||||||
|
WoofWare.Myriad.Plugins.OperationId.get_Item [method]: unit -> string
|
||||||
|
WoofWare.Myriad.Plugins.OperationId.get_Tag [method]: unit -> int
|
||||||
|
WoofWare.Myriad.Plugins.OperationId.Item [property]: [read-only] string
|
||||||
|
WoofWare.Myriad.Plugins.OperationId.NewOperationId [static method]: string -> WoofWare.Myriad.Plugins.OperationId
|
||||||
|
WoofWare.Myriad.Plugins.OperationId.Tag [property]: [read-only] int
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn inherit obj, implements WoofWare.Myriad.Plugins.ParameterIn System.IEquatable, System.Collections.IStructuralEquatable, WoofWare.Myriad.Plugins.ParameterIn System.IComparable, System.IComparable, System.Collections.IStructuralComparable - union type with 4 cases
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn+Path inherit WoofWare.Myriad.Plugins.ParameterIn
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn+Path.get_name [method]: unit -> string
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn+Path.name [property]: [read-only] string
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn+Query inherit WoofWare.Myriad.Plugins.ParameterIn
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn+Query.get_name [method]: unit -> string
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn+Query.name [property]: [read-only] string
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn+Tags inherit obj
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn+Tags.Body [static field]: int = 2
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn+Tags.Path [static field]: int = 0
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn+Tags.Query [static field]: int = 1
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn+Tags.Unrecognised [static field]: int = 3
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn+Unrecognised inherit WoofWare.Myriad.Plugins.ParameterIn
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn+Unrecognised.get_name [method]: unit -> string
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn+Unrecognised.get_op [method]: unit -> string
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn+Unrecognised.name [property]: [read-only] string
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn+Unrecognised.op [property]: [read-only] string
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn.Body [static property]: [read-only] WoofWare.Myriad.Plugins.ParameterIn
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn.Equals [method]: (WoofWare.Myriad.Plugins.ParameterIn, System.Collections.IEqualityComparer) -> bool
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn.get_Body [static method]: unit -> WoofWare.Myriad.Plugins.ParameterIn
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn.get_IsBody [method]: unit -> bool
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn.get_IsPath [method]: unit -> bool
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn.get_IsQuery [method]: unit -> bool
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn.get_IsUnrecognised [method]: unit -> bool
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn.get_Tag [method]: unit -> int
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn.IsBody [property]: [read-only] bool
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn.IsPath [property]: [read-only] bool
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn.IsQuery [property]: [read-only] bool
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn.IsUnrecognised [property]: [read-only] bool
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn.NewPath [static method]: string -> WoofWare.Myriad.Plugins.ParameterIn
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn.NewQuery [static method]: string -> WoofWare.Myriad.Plugins.ParameterIn
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn.NewUnrecognised [static method]: (string, string) -> WoofWare.Myriad.Plugins.ParameterIn
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn.Tag [property]: [read-only] int
|
||||||
WoofWare.Myriad.Plugins.RemoveOptionsGenerator inherit obj, implements Myriad.Core.IMyriadGenerator
|
WoofWare.Myriad.Plugins.RemoveOptionsGenerator inherit obj, implements Myriad.Core.IMyriadGenerator
|
||||||
WoofWare.Myriad.Plugins.RemoveOptionsGenerator..ctor [constructor]: unit
|
WoofWare.Myriad.Plugins.RemoveOptionsGenerator..ctor [constructor]: unit
|
||||||
|
WoofWare.Myriad.Plugins.Response inherit obj, implements WoofWare.Myriad.Plugins.Response System.IEquatable, System.Collections.IStructuralEquatable
|
||||||
|
WoofWare.Myriad.Plugins.Response..ctor [constructor]: (string, WoofWare.Myriad.Plugins.Definition)
|
||||||
|
WoofWare.Myriad.Plugins.Response.Description [property]: [read-only] string
|
||||||
|
WoofWare.Myriad.Plugins.Response.Equals [method]: (WoofWare.Myriad.Plugins.Response, System.Collections.IEqualityComparer) -> bool
|
||||||
|
WoofWare.Myriad.Plugins.Response.get_Description [method]: unit -> string
|
||||||
|
WoofWare.Myriad.Plugins.Response.get_Schema [method]: unit -> WoofWare.Myriad.Plugins.Definition
|
||||||
|
WoofWare.Myriad.Plugins.Response.Parse [static method]: System.Text.Json.Nodes.JsonObject -> WoofWare.Myriad.Plugins.Response
|
||||||
|
WoofWare.Myriad.Plugins.Response.Schema [property]: [read-only] WoofWare.Myriad.Plugins.Definition
|
||||||
|
WoofWare.Myriad.Plugins.Scheme inherit obj, implements WoofWare.Myriad.Plugins.Scheme System.IEquatable, System.Collections.IStructuralEquatable, WoofWare.Myriad.Plugins.Scheme System.IComparable, System.IComparable, System.Collections.IStructuralComparable - union type with 1 cases
|
||||||
|
WoofWare.Myriad.Plugins.Scheme.Equals [method]: (WoofWare.Myriad.Plugins.Scheme, System.Collections.IEqualityComparer) -> bool
|
||||||
|
WoofWare.Myriad.Plugins.Scheme.get_Item [method]: unit -> string
|
||||||
|
WoofWare.Myriad.Plugins.Scheme.get_Tag [method]: unit -> int
|
||||||
|
WoofWare.Myriad.Plugins.Scheme.Item [property]: [read-only] string
|
||||||
|
WoofWare.Myriad.Plugins.Scheme.NewScheme [static method]: string -> WoofWare.Myriad.Plugins.Scheme
|
||||||
|
WoofWare.Myriad.Plugins.Scheme.Tag [property]: [read-only] int
|
||||||
|
WoofWare.Myriad.Plugins.Swagger inherit obj, implements WoofWare.Myriad.Plugins.Swagger System.IEquatable, System.Collections.IStructuralEquatable
|
||||||
|
WoofWare.Myriad.Plugins.Swagger..ctor [constructor]: (WoofWare.Myriad.Plugins.MimeType list, WoofWare.Myriad.Plugins.MimeType list, WoofWare.Myriad.Plugins.Scheme list, System.Version, WoofWare.Myriad.Plugins.SwaggerInfo, string, Map<string, Map<WoofWare.Myriad.Plugins.HttpMethod, WoofWare.Myriad.Plugins.SwaggerEndpoint>>, Map<string, WoofWare.Myriad.Plugins.Definition>, Map<string, WoofWare.Myriad.Plugins.Response>)
|
||||||
|
WoofWare.Myriad.Plugins.Swagger.BasePath [property]: [read-only] string
|
||||||
|
WoofWare.Myriad.Plugins.Swagger.Consumes [property]: [read-only] WoofWare.Myriad.Plugins.MimeType list
|
||||||
|
WoofWare.Myriad.Plugins.Swagger.Definitions [property]: [read-only] Map<string, WoofWare.Myriad.Plugins.Definition>
|
||||||
|
WoofWare.Myriad.Plugins.Swagger.Equals [method]: (WoofWare.Myriad.Plugins.Swagger, System.Collections.IEqualityComparer) -> bool
|
||||||
|
WoofWare.Myriad.Plugins.Swagger.get_BasePath [method]: unit -> string
|
||||||
|
WoofWare.Myriad.Plugins.Swagger.get_Consumes [method]: unit -> WoofWare.Myriad.Plugins.MimeType list
|
||||||
|
WoofWare.Myriad.Plugins.Swagger.get_Definitions [method]: unit -> Map<string, WoofWare.Myriad.Plugins.Definition>
|
||||||
|
WoofWare.Myriad.Plugins.Swagger.get_Info [method]: unit -> WoofWare.Myriad.Plugins.SwaggerInfo
|
||||||
|
WoofWare.Myriad.Plugins.Swagger.get_Paths [method]: unit -> Map<string, Map<WoofWare.Myriad.Plugins.HttpMethod, WoofWare.Myriad.Plugins.SwaggerEndpoint>>
|
||||||
|
WoofWare.Myriad.Plugins.Swagger.get_Produces [method]: unit -> WoofWare.Myriad.Plugins.MimeType list
|
||||||
|
WoofWare.Myriad.Plugins.Swagger.get_Responses [method]: unit -> Map<string, WoofWare.Myriad.Plugins.Response>
|
||||||
|
WoofWare.Myriad.Plugins.Swagger.get_Schemes [method]: unit -> WoofWare.Myriad.Plugins.Scheme list
|
||||||
|
WoofWare.Myriad.Plugins.Swagger.get_Swagger [method]: unit -> System.Version
|
||||||
|
WoofWare.Myriad.Plugins.Swagger.Info [property]: [read-only] WoofWare.Myriad.Plugins.SwaggerInfo
|
||||||
|
WoofWare.Myriad.Plugins.Swagger.Paths [property]: [read-only] Map<string, Map<WoofWare.Myriad.Plugins.HttpMethod, WoofWare.Myriad.Plugins.SwaggerEndpoint>>
|
||||||
|
WoofWare.Myriad.Plugins.Swagger.Produces [property]: [read-only] WoofWare.Myriad.Plugins.MimeType list
|
||||||
|
WoofWare.Myriad.Plugins.Swagger.Responses [property]: [read-only] Map<string, WoofWare.Myriad.Plugins.Response>
|
||||||
|
WoofWare.Myriad.Plugins.Swagger.Schemes [property]: [read-only] WoofWare.Myriad.Plugins.Scheme list
|
||||||
|
WoofWare.Myriad.Plugins.Swagger.Swagger [property]: [read-only] System.Version
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerClientGenerator inherit obj, implements Myriad.Core.IMyriadGenerator
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerClientGenerator..ctor [constructor]: unit
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerEndpoint inherit obj, implements WoofWare.Myriad.Plugins.SwaggerEndpoint System.IEquatable, System.Collections.IStructuralEquatable
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerEndpoint..ctor [constructor]: (WoofWare.Myriad.Plugins.MimeType list option, WoofWare.Myriad.Plugins.MimeType list option, string list, string, WoofWare.Myriad.Plugins.OperationId, WoofWare.Myriad.Plugins.SwaggerParameter list option, Map<int, WoofWare.Myriad.Plugins.Definition>)
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerEndpoint.Consumes [property]: [read-only] WoofWare.Myriad.Plugins.MimeType list option
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerEndpoint.Equals [method]: (WoofWare.Myriad.Plugins.SwaggerEndpoint, System.Collections.IEqualityComparer) -> bool
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerEndpoint.get_Consumes [method]: unit -> WoofWare.Myriad.Plugins.MimeType list option
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerEndpoint.get_OperationId [method]: unit -> WoofWare.Myriad.Plugins.OperationId
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerEndpoint.get_Parameters [method]: unit -> WoofWare.Myriad.Plugins.SwaggerParameter list option
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerEndpoint.get_Produces [method]: unit -> WoofWare.Myriad.Plugins.MimeType list option
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerEndpoint.get_Responses [method]: unit -> Map<int, WoofWare.Myriad.Plugins.Definition>
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerEndpoint.get_Summary [method]: unit -> string
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerEndpoint.get_Tags [method]: unit -> string list
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerEndpoint.OperationId [property]: [read-only] WoofWare.Myriad.Plugins.OperationId
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerEndpoint.Parameters [property]: [read-only] WoofWare.Myriad.Plugins.SwaggerParameter list option
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerEndpoint.Parse [static method]: System.Text.Json.Nodes.JsonObject -> WoofWare.Myriad.Plugins.SwaggerEndpoint
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerEndpoint.Produces [property]: [read-only] WoofWare.Myriad.Plugins.MimeType list option
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerEndpoint.Responses [property]: [read-only] Map<int, WoofWare.Myriad.Plugins.Definition>
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerEndpoint.Summary [property]: [read-only] string
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerEndpoint.Tags [property]: [read-only] string list
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerInfo inherit obj, implements WoofWare.Myriad.Plugins.SwaggerInfo System.IEquatable, System.Collections.IStructuralEquatable
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerInfo..ctor [constructor]: (string, string, WoofWare.Myriad.Plugins.SwaggerLicense, System.Version)
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerInfo.Description [property]: [read-only] string
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerInfo.Equals [method]: (WoofWare.Myriad.Plugins.SwaggerInfo, System.Collections.IEqualityComparer) -> bool
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerInfo.get_Description [method]: unit -> string
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerInfo.get_License [method]: unit -> WoofWare.Myriad.Plugins.SwaggerLicense
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerInfo.get_Title [method]: unit -> string
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerInfo.get_Version [method]: unit -> System.Version
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerInfo.License [property]: [read-only] WoofWare.Myriad.Plugins.SwaggerLicense
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerInfo.Parse [static method]: System.Text.Json.Nodes.JsonObject -> WoofWare.Myriad.Plugins.SwaggerInfo
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerInfo.Title [property]: [read-only] string
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerInfo.Version [property]: [read-only] System.Version
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerLicense inherit obj, implements WoofWare.Myriad.Plugins.SwaggerLicense System.IEquatable, System.Collections.IStructuralEquatable
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerLicense..ctor [constructor]: (string, System.Uri option, string option)
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerLicense.Equals [method]: (WoofWare.Myriad.Plugins.SwaggerLicense, System.Collections.IEqualityComparer) -> bool
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerLicense.get_Identifier [method]: unit -> string option
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerLicense.get_Name [method]: unit -> string
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerLicense.get_Url [method]: unit -> System.Uri option
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerLicense.Identifier [property]: [read-only] string option
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerLicense.Name [property]: [read-only] string
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerLicense.Parse [static method]: System.Text.Json.Nodes.JsonObject -> WoofWare.Myriad.Plugins.SwaggerLicense
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerLicense.Url [property]: [read-only] System.Uri option
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerModule inherit obj
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerModule.parse [static method]: string -> WoofWare.Myriad.Plugins.Swagger
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerParameter inherit obj, implements WoofWare.Myriad.Plugins.SwaggerParameter System.IEquatable, System.Collections.IStructuralEquatable
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerParameter..ctor [constructor]: (WoofWare.Myriad.Plugins.Definition, string option, WoofWare.Myriad.Plugins.ParameterIn, string, bool option)
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerParameter.Description [property]: [read-only] string option
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerParameter.Equals [method]: (WoofWare.Myriad.Plugins.SwaggerParameter, System.Collections.IEqualityComparer) -> bool
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerParameter.get_Description [method]: unit -> string option
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerParameter.get_In [method]: unit -> WoofWare.Myriad.Plugins.ParameterIn
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerParameter.get_Name [method]: unit -> string
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerParameter.get_Required [method]: unit -> bool option
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerParameter.get_Type [method]: unit -> WoofWare.Myriad.Plugins.Definition
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerParameter.In [property]: [read-only] WoofWare.Myriad.Plugins.ParameterIn
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerParameter.Name [property]: [read-only] string
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerParameter.Parse [static method]: System.Text.Json.Nodes.JsonObject -> WoofWare.Myriad.Plugins.SwaggerParameter
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerParameter.Required [property]: [read-only] bool option
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerParameter.Type [property]: [read-only] WoofWare.Myriad.Plugins.Definition
|
||||||
WoofWare.Myriad.Plugins.SynFieldData`1 inherit obj
|
WoofWare.Myriad.Plugins.SynFieldData`1 inherit obj
|
||||||
WoofWare.Myriad.Plugins.SynFieldData`1..ctor [constructor]: (Fantomas.FCS.Syntax.SynAttribute list, 'Ident, Fantomas.FCS.Syntax.SynType)
|
WoofWare.Myriad.Plugins.SynFieldData`1..ctor [constructor]: (Fantomas.FCS.Syntax.SynAttribute list, 'Ident, Fantomas.FCS.Syntax.SynType)
|
||||||
WoofWare.Myriad.Plugins.SynFieldData`1.Attrs [property]: [read-only] Fantomas.FCS.Syntax.SynAttribute list
|
WoofWare.Myriad.Plugins.SynFieldData`1.Attrs [property]: [read-only] Fantomas.FCS.Syntax.SynAttribute list
|
||||||
|
576
WoofWare.Myriad.Plugins/Swagger.fs
Normal file
576
WoofWare.Myriad.Plugins/Swagger.fs
Normal file
@@ -0,0 +1,576 @@
|
|||||||
|
namespace WoofWare.Myriad.Plugins
|
||||||
|
|
||||||
|
open System
|
||||||
|
open System.Text.Json.Nodes
|
||||||
|
|
||||||
|
[<AutoOpen>]
|
||||||
|
module internal JsonHelpers =
|
||||||
|
let inline asString (n : JsonNode) (key : string) : string =
|
||||||
|
match n.[key] with
|
||||||
|
| null -> failwith $"Expected node to have a key '%s{key}', but it did not: %s{n.ToJsonString ()}"
|
||||||
|
| s -> s.GetValue<string> ()
|
||||||
|
|
||||||
|
[<RequiresExplicitTypeArguments>]
|
||||||
|
let inline asOpt<'ret> (n : JsonNode) (key : string) : 'ret option =
|
||||||
|
match n.[key] with
|
||||||
|
| null -> None
|
||||||
|
| s -> s.GetValue<'ret> () |> Some
|
||||||
|
|
||||||
|
let inline asObj (n : JsonNode) (key : string) : JsonObject =
|
||||||
|
match n.[key] with
|
||||||
|
| null -> failwith $"Expected node to have a key '%s{key}', but it did not: %s{n.ToJsonString ()}"
|
||||||
|
| o -> o.AsObject ()
|
||||||
|
|
||||||
|
let inline asObjOpt (n : JsonNode) (key : string) : JsonObject option =
|
||||||
|
match n.[key] with
|
||||||
|
| null -> None
|
||||||
|
| o -> o.AsObject () |> Some
|
||||||
|
|
||||||
|
let inline asArr (n : JsonNode) (key : string) : JsonArray =
|
||||||
|
match n.[key] with
|
||||||
|
| null -> failwith $"Expected node to have a key '%s{key}', but it did not: %s{n.ToJsonString ()}"
|
||||||
|
| o -> o.AsArray ()
|
||||||
|
|
||||||
|
let inline asArrOpt (n : JsonNode) (key : string) : JsonArray option =
|
||||||
|
match n.[key] with
|
||||||
|
| null -> None
|
||||||
|
| o -> o.AsArray () |> Some
|
||||||
|
|
||||||
|
[<RequiresExplicitTypeArguments>]
|
||||||
|
let inline asArr'<'v> (n : JsonNode) (key : string) : 'v list =
|
||||||
|
match n.[key] with
|
||||||
|
| null -> failwith $"Expected node to have a key '%s{key}', but it did not: %s{n.ToJsonString ()}"
|
||||||
|
| o -> o.AsArray () |> Seq.map (fun v -> v.GetValue<'v> ()) |> Seq.toList
|
||||||
|
|
||||||
|
[<RequiresExplicitTypeArguments>]
|
||||||
|
let inline asArrOpt'<'v> (n : JsonNode) (key : string) : 'v list option =
|
||||||
|
match n.[key] with
|
||||||
|
| null -> None
|
||||||
|
| o -> o.AsArray () |> Seq.map (fun v -> v.GetValue<'v> ()) |> Seq.toList |> Some
|
||||||
|
|
||||||
|
/// A MIME type, like "application/json"
|
||||||
|
type MimeType =
|
||||||
|
/// A MIME type, like "application/json"
|
||||||
|
| MimeType of string
|
||||||
|
|
||||||
|
/// A URL scheme, like "https"
|
||||||
|
type Scheme =
|
||||||
|
/// A URL scheme, like "https"
|
||||||
|
| Scheme of string
|
||||||
|
|
||||||
|
/// "Licence information for the exposed API", whatever that means.
|
||||||
|
type SwaggerLicense =
|
||||||
|
{
|
||||||
|
/// "The license name used for the API", whatever that means.
|
||||||
|
Name : string
|
||||||
|
/// Link to the license used. Mutually exclusive with `Identifier`.
|
||||||
|
Url : Uri option
|
||||||
|
/// SPDX license identifier. Mutually exclusive with `Url`.
|
||||||
|
Identifier : string option
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Render a JsonObject into the strongly-typed version, performing sanity
|
||||||
|
/// checks and throwing on input that can't be parsed.
|
||||||
|
static member Parse (node : JsonObject) : SwaggerLicense =
|
||||||
|
let name = asString node "name"
|
||||||
|
let url = asOpt<string> node "url" |> Option.map Uri
|
||||||
|
let identifier = asOpt<string> node "identifier"
|
||||||
|
|
||||||
|
match url, identifier with
|
||||||
|
| Some _, Some _ -> failwith "Invalid license spec: cannot supply both URL and identifier"
|
||||||
|
| _, _ -> ()
|
||||||
|
|
||||||
|
{
|
||||||
|
Name = name
|
||||||
|
Url = url
|
||||||
|
Identifier = identifier
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Overall information about the API described by this Swagger spec.
|
||||||
|
type SwaggerInfo =
|
||||||
|
{
|
||||||
|
/// Human-readable description of what this Swagger API is for.
|
||||||
|
/// Supports GitHub-flavoured markdown, apparently.
|
||||||
|
Description : string
|
||||||
|
/// Human-readable title of the service to which this is an API.
|
||||||
|
Title : string
|
||||||
|
/// The license applying to this schema. It's very unclear what this means.
|
||||||
|
/// The spec just says:
|
||||||
|
/// "Licence information for the exposed API"
|
||||||
|
License : SwaggerLicense
|
||||||
|
/// The version of this API (not the version of Swagger or the file defining the API!).
|
||||||
|
/// Strictly speaking this can be anything, but I am assuming it's roughly
|
||||||
|
/// SemVer.
|
||||||
|
Version : Version
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Render a JsonObject into the strongly-typed version, performing sanity
|
||||||
|
/// checks and throwing on input that can't be parsed.
|
||||||
|
static member Parse (node : JsonObject) : SwaggerInfo =
|
||||||
|
let description = asString node "description"
|
||||||
|
let title = asString node "title"
|
||||||
|
let version = asString node "version" |> Version.Parse
|
||||||
|
let license = asObj node "license" |> SwaggerLicense.Parse
|
||||||
|
|
||||||
|
{
|
||||||
|
Description = description
|
||||||
|
Title = title
|
||||||
|
License = license
|
||||||
|
Version = version
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An "optional unique string used to describe an operation".
|
||||||
|
/// If present, these are assumed to be unique among all operations described
|
||||||
|
/// in the API.
|
||||||
|
type OperationId =
|
||||||
|
/// An "optional unique string used to describe an operation".
|
||||||
|
/// If present, these are assumed to be unique among all operations described
|
||||||
|
/// in the API.
|
||||||
|
| OperationId of string
|
||||||
|
|
||||||
|
/// Round-trip string representation.
|
||||||
|
override this.ToString () =
|
||||||
|
match this with
|
||||||
|
| OperationId.OperationId s -> s
|
||||||
|
|
||||||
|
/// Constraints on the `additionalProperties` (in the JSON schema sense).
|
||||||
|
/// "Additional properties" are properties of a JSON object which were not
|
||||||
|
/// listed in the schema.
|
||||||
|
type AdditionalProperties =
|
||||||
|
/// No additional properties are allowed: all properties must have been
|
||||||
|
/// mentioned in the schema.
|
||||||
|
| Never
|
||||||
|
/// Additional properties are permitted, but if they exist, they must
|
||||||
|
/// match this schema definition.
|
||||||
|
| Constrained of Definition
|
||||||
|
|
||||||
|
/// The Swagger schema lets you define types. An ObjectTypeDefinition
|
||||||
|
/// is specifically the information about types defined as `"type": "object"`.
|
||||||
|
and ObjectTypeDefinition =
|
||||||
|
{
|
||||||
|
/// Human-readable description of the purpose of this type.
|
||||||
|
Description : string option
|
||||||
|
/// Fields which any object must have to satisfy this type.
|
||||||
|
Properties : Map<string, Definition> option
|
||||||
|
/// Extra properties in the type description. In Gitea, these are
|
||||||
|
/// (for example) "x-go-package":"code.gitea.io/gitea/modules/structs".
|
||||||
|
Extras : Map<string, JsonNode>
|
||||||
|
/// List of fields which are required; all other fields are optional.
|
||||||
|
Required : string list option
|
||||||
|
/// Constraints, if any, placed on fields which are not mentioned in
|
||||||
|
/// the schema. If absent, there are no constraints.
|
||||||
|
AdditionalProperties : AdditionalProperties option
|
||||||
|
/// Example of an object which satisfies this schema.
|
||||||
|
Example : JsonObject option
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Render a JsonObject into the strongly-typed version, performing sanity
|
||||||
|
/// checks and throwing on input that can't be parsed.
|
||||||
|
static member Parse (node : JsonObject) : ObjectTypeDefinition =
|
||||||
|
let description =
|
||||||
|
match asOpt<string> node "description", asOpt<string> node "title" with
|
||||||
|
| None, None -> None
|
||||||
|
| Some v, None
|
||||||
|
| None, Some v -> Some v
|
||||||
|
| Some v1, Some v2 -> failwith "both description and title were given"
|
||||||
|
|
||||||
|
let additionalProperties =
|
||||||
|
match node.["additionalProperties"] with
|
||||||
|
| null -> None
|
||||||
|
| :? JsonValue as p ->
|
||||||
|
if not (p.GetValue<bool> ()) then
|
||||||
|
Some AdditionalProperties.Never
|
||||||
|
else
|
||||||
|
failwith $"additionalProperties should be 'false' or an object, but was: %s{p.ToJsonString ()}"
|
||||||
|
| p ->
|
||||||
|
let p = p.AsObject ()
|
||||||
|
Definition.Parse p |> AdditionalProperties.Constrained |> Some
|
||||||
|
|
||||||
|
let properties =
|
||||||
|
match node.["properties"] with
|
||||||
|
| null -> None
|
||||||
|
| p ->
|
||||||
|
p.AsObject ()
|
||||||
|
|> Seq.map (fun (KeyValue (key, value)) ->
|
||||||
|
let value = value.AsObject ()
|
||||||
|
key, Definition.Parse value
|
||||||
|
)
|
||||||
|
|> Map.ofSeq
|
||||||
|
|> Some
|
||||||
|
|
||||||
|
let example = asObjOpt node "example"
|
||||||
|
|
||||||
|
let required = asArrOpt'<string> node "required"
|
||||||
|
|
||||||
|
let extras =
|
||||||
|
node.AsObject ()
|
||||||
|
|> Seq.choose (fun (KeyValue (key, value)) ->
|
||||||
|
match key with
|
||||||
|
| "type"
|
||||||
|
| "description"
|
||||||
|
| "title"
|
||||||
|
| "additionalProperties"
|
||||||
|
| "example"
|
||||||
|
| "required"
|
||||||
|
| "properties" -> None
|
||||||
|
| _ -> Some (key, value)
|
||||||
|
)
|
||||||
|
|> Map.ofSeq
|
||||||
|
|
||||||
|
{
|
||||||
|
Description = description
|
||||||
|
Properties = properties
|
||||||
|
AdditionalProperties = additionalProperties
|
||||||
|
Required = required
|
||||||
|
Extras = extras
|
||||||
|
Example = example
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The Swagger schema lets you define types. An ArrayTypeDefinition
|
||||||
|
/// is specifically the information about types defined as `"type": "array"`.
|
||||||
|
and ArrayTypeDefinition =
|
||||||
|
{
|
||||||
|
/// The type is `'a array`; this field describes `'a`.
|
||||||
|
Items : Definition
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Render a JsonNode into the strongly-typed version, performing sanity
|
||||||
|
/// checks and throwing on input that can't be parsed.
|
||||||
|
static member Parse (n : JsonNode) : ArrayTypeDefinition =
|
||||||
|
let items = asObj n "items" |> Definition.Parse
|
||||||
|
|
||||||
|
{
|
||||||
|
Items = items
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Any definition of a type in the Swagger document. This is basically any
|
||||||
|
/// information associated with the `"type": "blah"` field.
|
||||||
|
and Definition =
|
||||||
|
/// For example, if `"$ref": "#/responses/Blah", then this is "#/responses/Blah".
|
||||||
|
| Handle of string
|
||||||
|
/// A type definition with "type": "object".
|
||||||
|
| Object of ObjectTypeDefinition
|
||||||
|
/// A type definition with "type": "array".
|
||||||
|
| Array of ArrayTypeDefinition
|
||||||
|
/// A type definition with "type": "string".
|
||||||
|
| String
|
||||||
|
/// A type definition with "type": "boolean".
|
||||||
|
| Boolean
|
||||||
|
/// A response without a body has no "schema" specified.
|
||||||
|
| Unspecified
|
||||||
|
/// A type definition with "type": "integer".
|
||||||
|
/// The format is an optional hint which could be e.g. "int64" or "int32".
|
||||||
|
/// https://swagger.io/docs/specification/data-models/data-types/#numbers
|
||||||
|
| Integer of format : string option
|
||||||
|
/// Not a JSON schema type, but a Swagger 2.0 type.
|
||||||
|
| File
|
||||||
|
|
||||||
|
/// Render a JsonObject into this strongly-typed specification.
|
||||||
|
static member Parse (n : JsonObject) : Definition =
|
||||||
|
match n.["$ref"] |> Option.ofObj with
|
||||||
|
| Some ref -> Definition.Handle (ref.GetValue<string> ())
|
||||||
|
| None ->
|
||||||
|
|
||||||
|
let ty = asOpt<string> n "type"
|
||||||
|
|
||||||
|
match ty with
|
||||||
|
| None -> Definition.Unspecified
|
||||||
|
| Some "object" -> ObjectTypeDefinition.Parse n |> Definition.Object
|
||||||
|
| Some "array" -> ArrayTypeDefinition.Parse n |> Definition.Array
|
||||||
|
| Some "string" -> Definition.String
|
||||||
|
| Some "boolean" -> Definition.Boolean
|
||||||
|
| Some "file" -> Definition.File
|
||||||
|
| Some "integer" ->
|
||||||
|
let format = asOpt<string> n "format"
|
||||||
|
Definition.Integer format
|
||||||
|
| Some ty -> failwith $"Unrecognised type: %s{ty}"
|
||||||
|
|
||||||
|
/// REST APIs allow their parameters to be passed in various ways. This describes
|
||||||
|
/// how one single parameter is passed.
|
||||||
|
type ParameterIn =
|
||||||
|
/// The parameter is interpolated into the path, e.g. "/foo/{blah}".
|
||||||
|
/// The "name" is what we replace in the path: e.g. "/foo/{person}" would
|
||||||
|
/// have a name of "person".
|
||||||
|
| Path of name : string
|
||||||
|
/// The parameter is appended to the URL's query params, e.g. "?<name>=blah"
|
||||||
|
| Query of name : string
|
||||||
|
/// The parameter is passed in the body of the HTTP request.
|
||||||
|
| Body
|
||||||
|
/// Some spec that WoofWare.Myriad doesn't support.
|
||||||
|
| Unrecognised of op : string * name : string
|
||||||
|
|
||||||
|
/// Description of a single input parameter to an endpoint.
|
||||||
|
type SwaggerParameter =
|
||||||
|
{
|
||||||
|
/// The type schema to which this parameter must conform.
|
||||||
|
Type : Definition
|
||||||
|
/// Optional human-readable description of this parameter.
|
||||||
|
Description : string option
|
||||||
|
/// How this parameter is passed.
|
||||||
|
In : ParameterIn
|
||||||
|
/// Name of this parameter. For most `In` values, this name is the
|
||||||
|
/// name of the parameter as supplied to the API at runtime, and in WoofWare's
|
||||||
|
/// strongly-typed domain types this information is also contained in the `In` field.
|
||||||
|
/// For `Body` parameters, this is purely for dev-time information.
|
||||||
|
Name : string
|
||||||
|
/// Whether this parameter is required for validation to succeed.
|
||||||
|
/// I think this defaults to "no".
|
||||||
|
Required : bool option
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Render a JsonObject into this strongly-typed specification.
|
||||||
|
static member Parse (node : JsonObject) : SwaggerParameter =
|
||||||
|
let ty =
|
||||||
|
match asObjOpt node "schema" with
|
||||||
|
| None -> Definition.Parse node
|
||||||
|
| Some node -> Definition.Parse node
|
||||||
|
|
||||||
|
let description = asOpt<string> node "description"
|
||||||
|
let name = asString node "name"
|
||||||
|
|
||||||
|
let paramIn =
|
||||||
|
match asString node "in" with
|
||||||
|
| "path" -> ParameterIn.Path name
|
||||||
|
| "query" -> ParameterIn.Query name
|
||||||
|
| "body" -> ParameterIn.Body
|
||||||
|
| f -> ParameterIn.Unrecognised (f, name)
|
||||||
|
|
||||||
|
let required = asOpt<bool> node "required"
|
||||||
|
|
||||||
|
{
|
||||||
|
Type = ty
|
||||||
|
Description = description
|
||||||
|
In = paramIn
|
||||||
|
Name = name
|
||||||
|
Required = required
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An "endpoint" is basically a single HTTP verb, applied to some path.
|
||||||
|
type SwaggerEndpoint =
|
||||||
|
{
|
||||||
|
/// The MIME types we should send our request body in.
|
||||||
|
/// This overrides (does not extend) any global definitions on the spec itself.
|
||||||
|
Consumes : MimeType list option
|
||||||
|
/// The MIME types we should expect to receive in response to this request.
|
||||||
|
/// This overrides (does not extend) any global definitions on the spec itself.
|
||||||
|
Produces : MimeType list option
|
||||||
|
/// Arbitrary list of [tags](https://swagger.io/docs/specification/2-0/grouping-operations-with-tags/).
|
||||||
|
Tags : string list
|
||||||
|
/// Human-readable description of the endpoint.
|
||||||
|
Summary : string
|
||||||
|
/// Arbitrary identifier of this endpoint; this must be unique across *all* endpoints
|
||||||
|
/// in this entire spec.
|
||||||
|
OperationId : OperationId
|
||||||
|
/// Parameters that must be supplied at HTTP-request-time to the endpoint.
|
||||||
|
/// (Each parameter knows how it needs to be supplied: e.g. if it's a query parameter or
|
||||||
|
/// if it's interpolated into the path.)
|
||||||
|
Parameters : SwaggerParameter list option
|
||||||
|
/// Map of HTTP response code to the type that we expect to receive in the body if we
|
||||||
|
/// get that response code back.
|
||||||
|
Responses : Map<int, Definition>
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Render a JsonObject into this strongly-typed specification.
|
||||||
|
static member Parse (r : JsonObject) : SwaggerEndpoint =
|
||||||
|
let produces = asArrOpt'<string> r "produces" |> Option.map (List.map MimeType)
|
||||||
|
let consumes = asArrOpt'<string> r "consumes" |> Option.map (List.map MimeType)
|
||||||
|
let tags = asArr'<string> r "tags"
|
||||||
|
let summary = asString r "summary"
|
||||||
|
let operationId = asString r "operationId" |> OperationId
|
||||||
|
|
||||||
|
let responses =
|
||||||
|
asObj r "responses"
|
||||||
|
|> Seq.map (fun (KeyValue (key, value)) ->
|
||||||
|
let value = value.AsObject ()
|
||||||
|
Int32.Parse key, Definition.Parse value
|
||||||
|
)
|
||||||
|
|> Map.ofSeq
|
||||||
|
|
||||||
|
let parameters =
|
||||||
|
asArrOpt r "parameters"
|
||||||
|
|> Option.map (fun pars ->
|
||||||
|
pars
|
||||||
|
|> Seq.map (fun par -> par.AsObject () |> SwaggerParameter.Parse)
|
||||||
|
|> Seq.toList
|
||||||
|
)
|
||||||
|
|
||||||
|
{
|
||||||
|
Produces = produces
|
||||||
|
Consumes = consumes
|
||||||
|
Tags = tags
|
||||||
|
Summary = summary
|
||||||
|
OperationId = operationId
|
||||||
|
Parameters = parameters
|
||||||
|
Responses = responses
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Specifies the form a response to an endpoint will take if it's complying with this spec.
|
||||||
|
type Response =
|
||||||
|
{
|
||||||
|
/// Human-readable description.
|
||||||
|
Description : string
|
||||||
|
/// Specification of the type to which responses will conform under this spec.
|
||||||
|
Schema : Definition
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Render a JsonObject into this strongly-typed specification.
|
||||||
|
static member Parse (r : JsonObject) : Response =
|
||||||
|
let desc = asString r "description"
|
||||||
|
|
||||||
|
let schema =
|
||||||
|
match asObjOpt r "schema" with
|
||||||
|
| None -> Definition.Unspecified
|
||||||
|
| Some s -> Definition.Parse s
|
||||||
|
|
||||||
|
{
|
||||||
|
Description = desc
|
||||||
|
Schema = schema
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An HTTP method. This is System.Net.Http.HttpMethod, but
|
||||||
|
/// a proper discriminated union.
|
||||||
|
type HttpMethod =
|
||||||
|
/// HTTP Get
|
||||||
|
| Get
|
||||||
|
/// HTTP Post
|
||||||
|
| Post
|
||||||
|
/// HTTP Delete
|
||||||
|
| Delete
|
||||||
|
/// HTTP Patch
|
||||||
|
| Patch
|
||||||
|
/// HTTP Options
|
||||||
|
| Options
|
||||||
|
/// HTTP Head
|
||||||
|
| Head
|
||||||
|
/// HTTP Put
|
||||||
|
| Put
|
||||||
|
/// HTTP Trace
|
||||||
|
| Trace
|
||||||
|
|
||||||
|
/// Convert to the standard library's enum type.
|
||||||
|
member this.ToDotNet () : System.Net.Http.HttpMethod =
|
||||||
|
match this with
|
||||||
|
| HttpMethod.Get -> System.Net.Http.HttpMethod.Get
|
||||||
|
| HttpMethod.Post -> System.Net.Http.HttpMethod.Post
|
||||||
|
| HttpMethod.Delete -> System.Net.Http.HttpMethod.Delete
|
||||||
|
| HttpMethod.Patch -> System.Net.Http.HttpMethod.Patch
|
||||||
|
| HttpMethod.Options -> System.Net.Http.HttpMethod.Options
|
||||||
|
| HttpMethod.Head -> System.Net.Http.HttpMethod.Head
|
||||||
|
| HttpMethod.Put -> System.Net.Http.HttpMethod.Put
|
||||||
|
| HttpMethod.Trace -> System.Net.Http.HttpMethod.Trace
|
||||||
|
|
||||||
|
/// Human-readable string representation.
|
||||||
|
override this.ToString () : string =
|
||||||
|
match this with
|
||||||
|
| HttpMethod.Get -> "Get"
|
||||||
|
| HttpMethod.Post -> "Post"
|
||||||
|
| HttpMethod.Delete -> "Delete"
|
||||||
|
| HttpMethod.Patch -> "Post"
|
||||||
|
| HttpMethod.Options -> "Options"
|
||||||
|
| HttpMethod.Head -> "Head"
|
||||||
|
| HttpMethod.Put -> "Put"
|
||||||
|
| HttpMethod.Trace -> "Trace"
|
||||||
|
|
||||||
|
/// Throws on invalid inputs.
|
||||||
|
static member Parse (s : string) : HttpMethod =
|
||||||
|
if String.Equals (s, "get", StringComparison.OrdinalIgnoreCase) then
|
||||||
|
HttpMethod.Get
|
||||||
|
elif String.Equals (s, "post", StringComparison.OrdinalIgnoreCase) then
|
||||||
|
HttpMethod.Post
|
||||||
|
elif String.Equals (s, "patch", StringComparison.OrdinalIgnoreCase) then
|
||||||
|
HttpMethod.Patch
|
||||||
|
elif String.Equals (s, "delete", StringComparison.OrdinalIgnoreCase) then
|
||||||
|
HttpMethod.Delete
|
||||||
|
elif String.Equals (s, "head", StringComparison.OrdinalIgnoreCase) then
|
||||||
|
HttpMethod.Head
|
||||||
|
elif String.Equals (s, "options", StringComparison.OrdinalIgnoreCase) then
|
||||||
|
HttpMethod.Options
|
||||||
|
elif String.Equals (s, "put", StringComparison.OrdinalIgnoreCase) then
|
||||||
|
HttpMethod.Put
|
||||||
|
else
|
||||||
|
failwith $"Unrecognised method: %s{s}"
|
||||||
|
|
||||||
|
/// A Swagger API specification.
|
||||||
|
type Swagger =
|
||||||
|
{
|
||||||
|
/// Global collection of MIME types which any endpoint expects to consume its inputs in.
|
||||||
|
/// This may be overridden on any individual endpoint by that endpoint.
|
||||||
|
Consumes : MimeType list
|
||||||
|
/// Global collection of MIME types which any endpoint will produce.
|
||||||
|
/// This may be overridden on any individual endpoint by that endpoint.
|
||||||
|
Produces : MimeType list
|
||||||
|
/// HTTP or HTTPS, for example. Indicates which scheme to access the API on.
|
||||||
|
Schemes : Scheme list
|
||||||
|
/// The version of OpenAPI this specification is written against.
|
||||||
|
/// (As of this writing, we only support 2.0.)
|
||||||
|
Swagger : Version
|
||||||
|
/// General information about this API.
|
||||||
|
Info : SwaggerInfo
|
||||||
|
/// Path under the URI host, which should be prefixed (with trailing slash if necessary)
|
||||||
|
/// to all requests.
|
||||||
|
BasePath : string
|
||||||
|
/// Map from relative path to "what is served at that path".
|
||||||
|
Paths : Map<string, Map<HttpMethod, SwaggerEndpoint>>
|
||||||
|
/// Types defined in the schema. Requests may use these definitions just like in any other JSON schema.
|
||||||
|
/// Key is a domain type name, e.g. "APIError".
|
||||||
|
Definitions : Map<string, Definition>
|
||||||
|
/// Types of each response.
|
||||||
|
/// Key is a domain type name, e.g. "AccessToken".
|
||||||
|
Responses : Map<string, Response>
|
||||||
|
}
|
||||||
|
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
module Swagger =
|
||||||
|
/// Parse a JSON-schema-based specification of a Swagger 2.0 API and
|
||||||
|
/// build the strongly-typed version. Throws on invalid inputs.
|
||||||
|
let parse (s : string) : Swagger =
|
||||||
|
let node = JsonNode.Parse s
|
||||||
|
let consumes = asArr'<string> node "consumes" |> List.map MimeType
|
||||||
|
let produces = asArr'<string> node "produces" |> List.map MimeType
|
||||||
|
let schemes = asArr'<string> node "schemes" |> List.map Scheme
|
||||||
|
let swagger = asString node "swagger" |> Version.Parse
|
||||||
|
let info = asObj node "info" |> SwaggerInfo.Parse
|
||||||
|
let basePath = asString node "basePath"
|
||||||
|
|
||||||
|
let definitions =
|
||||||
|
asObj node "definitions"
|
||||||
|
|> Seq.map (fun (KeyValue (key, value)) ->
|
||||||
|
let value = value.AsObject ()
|
||||||
|
key, Definition.Parse value
|
||||||
|
)
|
||||||
|
|> Map.ofSeq
|
||||||
|
|
||||||
|
let paths =
|
||||||
|
asObj node "paths"
|
||||||
|
|> Seq.map (fun (KeyValue (key, value)) ->
|
||||||
|
let contents =
|
||||||
|
value.AsObject ()
|
||||||
|
|> Seq.map (fun (KeyValue (endpoint, contents)) ->
|
||||||
|
let contents = contents.AsObject ()
|
||||||
|
HttpMethod.Parse endpoint, SwaggerEndpoint.Parse contents
|
||||||
|
)
|
||||||
|
|> Map.ofSeq
|
||||||
|
|
||||||
|
key, contents
|
||||||
|
)
|
||||||
|
|> Map.ofSeq
|
||||||
|
|
||||||
|
let responses =
|
||||||
|
asObj node "responses"
|
||||||
|
|> Seq.map (fun (KeyValue (key, value)) ->
|
||||||
|
let value = value.AsObject ()
|
||||||
|
key, Response.Parse value
|
||||||
|
)
|
||||||
|
|> Map.ofSeq
|
||||||
|
|
||||||
|
{
|
||||||
|
Consumes = consumes
|
||||||
|
Produces = produces
|
||||||
|
Schemes = schemes
|
||||||
|
Swagger = swagger
|
||||||
|
Info = info
|
||||||
|
BasePath = basePath
|
||||||
|
Paths = paths
|
||||||
|
Definitions = definitions
|
||||||
|
Responses = responses
|
||||||
|
}
|
739
WoofWare.Myriad.Plugins/SwaggerClientGenerator.fs
Normal file
739
WoofWare.Myriad.Plugins/SwaggerClientGenerator.fs
Normal file
@@ -0,0 +1,739 @@
|
|||||||
|
namespace WoofWare.Myriad.Plugins
|
||||||
|
|
||||||
|
open System.Collections.Generic
|
||||||
|
open System.IO
|
||||||
|
open System.Threading
|
||||||
|
open Fantomas.FCS.Syntax
|
||||||
|
open Fantomas.FCS.Xml
|
||||||
|
open Fantomas.FCS.Text.Range
|
||||||
|
|
||||||
|
type internal SwaggerClientConfig =
|
||||||
|
{
|
||||||
|
/// Additionally create a mock with `InterfaceMockGenerator`, with the given boolean arg.
|
||||||
|
/// (`None` means "no mock".)
|
||||||
|
CreateMock : bool option
|
||||||
|
ClassName : string
|
||||||
|
}
|
||||||
|
|
||||||
|
type internal Produces =
|
||||||
|
// TODO: this will cope with decoding JSON, plain text, etc
|
||||||
|
| Produces of string
|
||||||
|
|
||||||
|
type internal Endpoint =
|
||||||
|
{
|
||||||
|
DocString : PreXmlDoc
|
||||||
|
Produces : Produces
|
||||||
|
ReturnType : Definition
|
||||||
|
Method : WoofWare.Myriad.Plugins.HttpMethod
|
||||||
|
Operation : OperationId
|
||||||
|
Parameters : SwaggerParameter list
|
||||||
|
Endpoint : string
|
||||||
|
}
|
||||||
|
|
||||||
|
type internal TypeEntry =
|
||||||
|
{
|
||||||
|
/// If we had to define a type for this, here it is.
|
||||||
|
FSharpDefinition : SynTypeDefn option
|
||||||
|
/// SynType you use in e.g. a type annotation to refer to this type in F# code.
|
||||||
|
Signature : SynType
|
||||||
|
}
|
||||||
|
|
||||||
|
type internal Types =
|
||||||
|
{
|
||||||
|
ByHandle : IReadOnlyDictionary<string, TypeEntry>
|
||||||
|
ByDefinition : IReadOnlyDictionary<Definition, TypeEntry>
|
||||||
|
}
|
||||||
|
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
module internal SwaggerClientGenerator =
|
||||||
|
let outputFile = FileInfo "/tmp/output.txt"
|
||||||
|
|
||||||
|
// do
|
||||||
|
// use _ = File.Create outputFile.FullName
|
||||||
|
// ()
|
||||||
|
|
||||||
|
let log (line : string) =
|
||||||
|
// use w = outputFile.AppendText ()
|
||||||
|
// w.WriteLine line
|
||||||
|
()
|
||||||
|
|
||||||
|
let renderType (types : Types) (defn : Definition) : SynType option =
|
||||||
|
match types.ByDefinition.TryGetValue defn with
|
||||||
|
| true, v -> Some v.Signature
|
||||||
|
| false, _ ->
|
||||||
|
|
||||||
|
match defn with
|
||||||
|
| Definition.Handle h ->
|
||||||
|
match types.ByHandle.TryGetValue h with
|
||||||
|
| false, _ -> None
|
||||||
|
| true, v -> Some v.Signature
|
||||||
|
| Definition.Object _ -> failwith "should not hit"
|
||||||
|
| Definition.Array _ -> failwith "should not hit"
|
||||||
|
| Definition.Unspecified -> failwith "should not hit"
|
||||||
|
| Definition.String -> SynType.string |> Some
|
||||||
|
| Definition.Boolean -> SynType.bool |> Some
|
||||||
|
| Definition.Integer _ -> SynType.int |> Some
|
||||||
|
| Definition.File -> SynType.createLongIdent' [ "System" ; "IO" ; "Stream" ] |> Some
|
||||||
|
|
||||||
|
/// Returns None if we lacked the information required to do this.
|
||||||
|
/// bigCache is a map of e.g. {"securityDefinition": {Defn : F# type}}.
|
||||||
|
let rec defnToType
|
||||||
|
(anonymousTypeCount : int ref)
|
||||||
|
(handlesMap : Dictionary<string, TypeEntry>)
|
||||||
|
(bigCache : Dictionary<string, Dictionary<Definition, TypeEntry>>)
|
||||||
|
(thisKey : string)
|
||||||
|
(typeName : string option)
|
||||||
|
(d : Definition)
|
||||||
|
: TypeEntry option
|
||||||
|
=
|
||||||
|
let cache =
|
||||||
|
match bigCache.TryGetValue thisKey with
|
||||||
|
| false, _ ->
|
||||||
|
let d = Dictionary ()
|
||||||
|
bigCache.Add (thisKey, d)
|
||||||
|
d
|
||||||
|
| true, d -> d
|
||||||
|
|
||||||
|
let handleKey =
|
||||||
|
match typeName with
|
||||||
|
| None -> None
|
||||||
|
| Some typeName -> $"#/%s{thisKey}/%s{typeName}" |> Some
|
||||||
|
|
||||||
|
match handleKey with
|
||||||
|
| Some hk when handlesMap.ContainsKey hk ->
|
||||||
|
let result = handlesMap.[hk]
|
||||||
|
cache.[d] <- result
|
||||||
|
Some result
|
||||||
|
|
||||||
|
| _ ->
|
||||||
|
|
||||||
|
match cache.TryGetValue d with
|
||||||
|
| true, v ->
|
||||||
|
match handleKey with
|
||||||
|
| None -> ()
|
||||||
|
| Some key -> handlesMap.Add (key, v)
|
||||||
|
|
||||||
|
Some v
|
||||||
|
| false, _ ->
|
||||||
|
|
||||||
|
let result =
|
||||||
|
match d with
|
||||||
|
| Definition.Object obj ->
|
||||||
|
let requiredFields = obj.Required |> Option.defaultValue [] |> Set.ofList
|
||||||
|
|
||||||
|
let namedProperties =
|
||||||
|
obj.Properties
|
||||||
|
|> Option.map Seq.cast
|
||||||
|
|> Option.defaultValue Seq.empty
|
||||||
|
|> Seq.map (fun (KeyValue (fieldName, defn)) ->
|
||||||
|
// TODO this is a horrible hack and is incomplete, e.g. if we contain an array of ourself
|
||||||
|
// Special case for when this is a reference to this very type
|
||||||
|
let isOurself =
|
||||||
|
match defn with
|
||||||
|
| Definition.Handle h ->
|
||||||
|
match h.Split '/' with
|
||||||
|
| [| "#" ; location ; ty |] when location = thisKey && Some ty = typeName ->
|
||||||
|
SynType.named ty |> Some
|
||||||
|
| _ -> None
|
||||||
|
| _ -> None
|
||||||
|
|
||||||
|
let jsonPropertyName =
|
||||||
|
SynExpr.CreateConst (fieldName : string)
|
||||||
|
|> SynAttribute.create (
|
||||||
|
SynLongIdent.createS'
|
||||||
|
[ "System" ; "Text" ; "Json" ; "Serialization" ; "JsonPropertyName" ]
|
||||||
|
)
|
||||||
|
|
||||||
|
match isOurself with
|
||||||
|
| Some alreadyDone ->
|
||||||
|
let ty =
|
||||||
|
if Set.contains fieldName requiredFields then
|
||||||
|
alreadyDone
|
||||||
|
else
|
||||||
|
SynType.option alreadyDone
|
||||||
|
|
||||||
|
{
|
||||||
|
Attrs = [ jsonPropertyName ]
|
||||||
|
Type = ty
|
||||||
|
Ident = Some (Ident.createSanitisedTypeName fieldName)
|
||||||
|
}
|
||||||
|
|> SynField.make
|
||||||
|
|> Some
|
||||||
|
| None ->
|
||||||
|
|
||||||
|
let defn' = defnToType anonymousTypeCount handlesMap bigCache thisKey None defn
|
||||||
|
|
||||||
|
match defn' with
|
||||||
|
| None -> None
|
||||||
|
| Some defn' ->
|
||||||
|
let ty =
|
||||||
|
if Set.contains fieldName requiredFields then
|
||||||
|
defn'.Signature
|
||||||
|
else
|
||||||
|
defn'.Signature |> SynType.option
|
||||||
|
|
||||||
|
{
|
||||||
|
Attrs = [ jsonPropertyName ]
|
||||||
|
Ident = Ident.createSanitisedTypeName fieldName |> Some
|
||||||
|
Type = ty
|
||||||
|
}
|
||||||
|
|> SynField.make
|
||||||
|
|> Some
|
||||||
|
)
|
||||||
|
|> Seq.toList
|
||||||
|
|
||||||
|
let additionalProperties =
|
||||||
|
match obj.AdditionalProperties with
|
||||||
|
| None ->
|
||||||
|
{
|
||||||
|
Attrs =
|
||||||
|
[
|
||||||
|
SynAttribute.create
|
||||||
|
(SynLongIdent.createS'
|
||||||
|
[ "System" ; "Text" ; "Json" ; "Serialization" ; "JsonExtensionData" ])
|
||||||
|
(SynExpr.CreateConst ())
|
||||||
|
]
|
||||||
|
Ident = Ident.create "AdditionalProperties" |> Some
|
||||||
|
Type =
|
||||||
|
SynType.app'
|
||||||
|
(SynType.createLongIdent' [ "System" ; "Collections" ; "Generic" ; "Dictionary" ])
|
||||||
|
[
|
||||||
|
SynType.string
|
||||||
|
SynType.createLongIdent' [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonNode" ]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|> SynField.make
|
||||||
|
|> List.singleton
|
||||||
|
|> Some
|
||||||
|
| Some AdditionalProperties.Never -> Some []
|
||||||
|
| Some (AdditionalProperties.Constrained defn) ->
|
||||||
|
let defn' = defnToType anonymousTypeCount handlesMap bigCache thisKey None defn
|
||||||
|
|
||||||
|
match defn' with
|
||||||
|
| None -> None
|
||||||
|
| Some defn' ->
|
||||||
|
{
|
||||||
|
Attrs =
|
||||||
|
[
|
||||||
|
SynAttribute.create
|
||||||
|
(SynLongIdent.createS'
|
||||||
|
[ "System" ; "Text" ; "Json" ; "Serialization" ; "JsonExtensionData" ])
|
||||||
|
(SynExpr.CreateConst ())
|
||||||
|
]
|
||||||
|
Ident = Ident.create "AdditionalProperties" |> Some
|
||||||
|
Type =
|
||||||
|
SynType.app'
|
||||||
|
(SynType.createLongIdent'
|
||||||
|
[ "System" ; "Collections" ; "Generic" ; "Dictionary" ])
|
||||||
|
[ SynType.string ; defn'.Signature ]
|
||||||
|
}
|
||||||
|
|> SynField.make
|
||||||
|
|> List.singleton
|
||||||
|
|> Some
|
||||||
|
|
||||||
|
match additionalProperties with
|
||||||
|
| None -> None
|
||||||
|
| Some additionalProperties ->
|
||||||
|
|
||||||
|
match List.allSome namedProperties with
|
||||||
|
| None -> None
|
||||||
|
| Some namedProperties ->
|
||||||
|
|
||||||
|
let fSharpTypeName =
|
||||||
|
match typeName with
|
||||||
|
| None -> $"Type%i{Interlocked.Increment anonymousTypeCount}"
|
||||||
|
| Some typeName -> typeName
|
||||||
|
|
||||||
|
let properties = additionalProperties @ namedProperties
|
||||||
|
|
||||||
|
let properties =
|
||||||
|
if properties.IsEmpty then
|
||||||
|
// sigh, they didn't give us any properties at all; let's make one up
|
||||||
|
{
|
||||||
|
Attrs = []
|
||||||
|
Ident = Some (Ident.create "_SchemaUnspecified")
|
||||||
|
Type = SynType.obj
|
||||||
|
}
|
||||||
|
|> SynField.make
|
||||||
|
|> List.singleton
|
||||||
|
else
|
||||||
|
properties
|
||||||
|
|
||||||
|
let defn =
|
||||||
|
let sci =
|
||||||
|
SynComponentInfo.create (Ident.createSanitisedTypeName fSharpTypeName)
|
||||||
|
|> SynComponentInfo.addAttributes
|
||||||
|
[
|
||||||
|
SynAttribute.create (SynLongIdent.createS' [ "JsonParse" ]) (SynExpr.CreateConst true)
|
||||||
|
SynAttribute.create
|
||||||
|
(SynLongIdent.createS' [ "JsonSerialize" ])
|
||||||
|
(SynExpr.CreateConst true)
|
||||||
|
]
|
||||||
|
|> fun sci ->
|
||||||
|
match obj.Description with
|
||||||
|
| None -> sci
|
||||||
|
| Some doc -> sci |> SynComponentInfo.withDocString (PreXmlDoc.create doc)
|
||||||
|
|
||||||
|
properties |> SynTypeDefnRepr.record |> SynTypeDefn.create sci
|
||||||
|
|
||||||
|
let defn =
|
||||||
|
{
|
||||||
|
Signature = SynType.named fSharpTypeName
|
||||||
|
FSharpDefinition = Some defn
|
||||||
|
}
|
||||||
|
|
||||||
|
defn |> Some
|
||||||
|
|
||||||
|
| Definition.Array elt ->
|
||||||
|
let child = defnToType anonymousTypeCount handlesMap bigCache thisKey None elt.Items
|
||||||
|
|
||||||
|
match child with
|
||||||
|
| None -> None
|
||||||
|
| Some child ->
|
||||||
|
let defn =
|
||||||
|
{
|
||||||
|
Signature = SynType.list child.Signature
|
||||||
|
FSharpDefinition = None
|
||||||
|
}
|
||||||
|
|
||||||
|
Some defn
|
||||||
|
| Definition.String ->
|
||||||
|
{
|
||||||
|
Signature = SynType.string
|
||||||
|
FSharpDefinition = None
|
||||||
|
}
|
||||||
|
|> Some
|
||||||
|
| Definition.Boolean ->
|
||||||
|
{
|
||||||
|
Signature = SynType.bool
|
||||||
|
FSharpDefinition = None
|
||||||
|
}
|
||||||
|
|> Some
|
||||||
|
| Definition.Unspecified ->
|
||||||
|
{
|
||||||
|
Signature = SynType.unit
|
||||||
|
FSharpDefinition = None
|
||||||
|
}
|
||||||
|
|> Some
|
||||||
|
| Definition.Integer _ ->
|
||||||
|
{
|
||||||
|
Signature = SynType.createLongIdent' [ "int" ]
|
||||||
|
FSharpDefinition = None
|
||||||
|
}
|
||||||
|
|> Some
|
||||||
|
| Definition.File ->
|
||||||
|
{
|
||||||
|
Signature = SynType.createLongIdent' [ "System" ; "IO" ; "Stream" ]
|
||||||
|
FSharpDefinition = None
|
||||||
|
}
|
||||||
|
|> Some
|
||||||
|
| Definition.Handle s ->
|
||||||
|
let split = s.Split '/' |> List.ofArray
|
||||||
|
|
||||||
|
match split with
|
||||||
|
| [ "#" ; _location ; _handle ] ->
|
||||||
|
match handlesMap.TryGetValue s with
|
||||||
|
| false, _ -> None
|
||||||
|
| true, computed ->
|
||||||
|
let defn =
|
||||||
|
{
|
||||||
|
FSharpDefinition = None
|
||||||
|
Signature = computed.Signature
|
||||||
|
}
|
||||||
|
|
||||||
|
defn |> Some
|
||||||
|
| _ -> failwith $"we don't know how to deal with object handle %s{s}"
|
||||||
|
|
||||||
|
match result with
|
||||||
|
| None -> None
|
||||||
|
| Some result ->
|
||||||
|
|
||||||
|
match handleKey with
|
||||||
|
| None -> ()
|
||||||
|
| Some handleKey -> handlesMap.Add (handleKey, result)
|
||||||
|
|
||||||
|
cache.Add (d, result)
|
||||||
|
Some result
|
||||||
|
|
||||||
|
let instantiateRequiredTypes (types : Types) : SynModuleDecl =
|
||||||
|
types.ByDefinition
|
||||||
|
|> Seq.choose (fun (KeyValue (_defn, typeEntry)) -> typeEntry.FSharpDefinition)
|
||||||
|
|> Seq.toList
|
||||||
|
|> SynModuleDecl.createTypes
|
||||||
|
|
||||||
|
type private IsIn =
|
||||||
|
| Path of str : string
|
||||||
|
| Query of str : string
|
||||||
|
| Body
|
||||||
|
|
||||||
|
let computeType
|
||||||
|
(options : SwaggerClientConfig)
|
||||||
|
(basePath : string)
|
||||||
|
(types : Types)
|
||||||
|
(clientDocString : PreXmlDoc)
|
||||||
|
(endpoints : Endpoint list)
|
||||||
|
: SynModuleDecl list
|
||||||
|
=
|
||||||
|
endpoints
|
||||||
|
|> List.choose (fun ep ->
|
||||||
|
let name = (Ident.createSanitisedTypeName (ep.Operation.ToString ())).idText
|
||||||
|
|
||||||
|
match renderType types ep.ReturnType with
|
||||||
|
| None ->
|
||||||
|
log $"Skipping %O{ep.Operation}: Couldn't render return type: %O{ep.ReturnType}"
|
||||||
|
None
|
||||||
|
| Some returnType ->
|
||||||
|
|
||||||
|
let pars =
|
||||||
|
ep.Parameters
|
||||||
|
|> List.map (fun par ->
|
||||||
|
let inParam =
|
||||||
|
match par.In with
|
||||||
|
| ParameterIn.Unrecognised (f, name) ->
|
||||||
|
log
|
||||||
|
$"Skipping %O{ep.Operation} at %s{ep.Endpoint}: unrecognised In parameter %s{f} with name %s{name}"
|
||||||
|
|
||||||
|
None
|
||||||
|
| ParameterIn.Body -> Some IsIn.Body
|
||||||
|
| ParameterIn.Query name -> Some (IsIn.Query name)
|
||||||
|
| ParameterIn.Path name -> Some (IsIn.Path name)
|
||||||
|
|
||||||
|
match inParam with
|
||||||
|
| None -> None
|
||||||
|
| Some inParam ->
|
||||||
|
|
||||||
|
match renderType types par.Type with
|
||||||
|
| None ->
|
||||||
|
// Couldn't render the return type
|
||||||
|
// failwith "Did not have a type here"
|
||||||
|
log $"Skipping %O{ep.Operation}: Couldn't render parameter: %O{par.Type}"
|
||||||
|
None
|
||||||
|
| Some v -> Some (Ident.createSanitisedParamName par.Name, inParam, v)
|
||||||
|
)
|
||||||
|
|> List.allSome
|
||||||
|
|
||||||
|
match pars with
|
||||||
|
| None -> None
|
||||||
|
| Some pars ->
|
||||||
|
|
||||||
|
let arity =
|
||||||
|
SynValInfo.SynValInfo (
|
||||||
|
[
|
||||||
|
ep.Parameters
|
||||||
|
|> List.map (fun par ->
|
||||||
|
let name = par.Name |> Ident.create |> Some
|
||||||
|
SynArgInfo.SynArgInfo ([], false, name)
|
||||||
|
)
|
||||||
|
|> fun l -> l @ [ SynArgInfo.SynArgInfo ([], true, Some (Ident.create "ct")) ]
|
||||||
|
],
|
||||||
|
SynArgInfo.SynArgInfo ([], false, None)
|
||||||
|
)
|
||||||
|
|
||||||
|
let domain =
|
||||||
|
let ctParam =
|
||||||
|
SynType.signatureParamOfType
|
||||||
|
[]
|
||||||
|
(SynType.createLongIdent' [ "System" ; "Threading" ; "CancellationToken" ])
|
||||||
|
true
|
||||||
|
(Some (Ident.create "ct"))
|
||||||
|
|
||||||
|
let argParams =
|
||||||
|
pars
|
||||||
|
|> List.map (fun (ident, isIn, t) ->
|
||||||
|
let attr : SynAttribute list =
|
||||||
|
match isIn with
|
||||||
|
| IsIn.Path name ->
|
||||||
|
SynAttribute.create
|
||||||
|
(SynLongIdent.createS' [ "RestEase" ; "Path" ])
|
||||||
|
(SynExpr.CreateConst name)
|
||||||
|
|> List.singleton
|
||||||
|
| IsIn.Query name ->
|
||||||
|
SynAttribute.create
|
||||||
|
(SynLongIdent.createS' [ "RestEase" ; "Query" ])
|
||||||
|
(SynExpr.CreateConst name)
|
||||||
|
|> List.singleton
|
||||||
|
| IsIn.Body ->
|
||||||
|
SynAttribute.create
|
||||||
|
(SynLongIdent.createS' [ "RestEase" ; "Body" ])
|
||||||
|
(SynExpr.CreateConst ())
|
||||||
|
|> List.singleton
|
||||||
|
|
||||||
|
SynType.signatureParamOfType attr t false (Some ident)
|
||||||
|
)
|
||||||
|
|
||||||
|
SynType.tupleNoParen (argParams @ [ ctParam ]) |> Option.get
|
||||||
|
|
||||||
|
let attrs =
|
||||||
|
[
|
||||||
|
SynAttribute.create
|
||||||
|
(SynLongIdent.createS' [ "RestEase" ; ep.Method.ToString () ])
|
||||||
|
// Gitea, at least, starts with a `/`, which `Uri` then takes to indicate an absolute path.
|
||||||
|
(SynExpr.CreateConst (ep.Endpoint.TrimStart '/'))
|
||||||
|
|
||||||
|
match ep.Produces with
|
||||||
|
| Produces.Produces contentType ->
|
||||||
|
SynAttribute.create
|
||||||
|
(SynLongIdent.createS' [ "RestEase" ; "Header" ])
|
||||||
|
// Gitea, at least, starts with a `/`, which `Uri` then takes to indicate an absolute path.
|
||||||
|
(SynExpr.tuple [ SynExpr.CreateConst "Content-Type" ; SynExpr.CreateConst contentType ])
|
||||||
|
]
|
||||||
|
|
||||||
|
returnType
|
||||||
|
|> SynType.task
|
||||||
|
|> SynType.toFun [ domain ]
|
||||||
|
|> SynMemberDefn.abstractMember attrs (SynIdent.createS name) None arity ep.DocString
|
||||||
|
|> Some
|
||||||
|
)
|
||||||
|
|> SynTypeDefnRepr.interfaceType
|
||||||
|
|> SynTypeDefn.create (
|
||||||
|
let attrs =
|
||||||
|
[
|
||||||
|
yield SynAttribute.create (SynLongIdent.createS' [ "HttpClient" ]) (SynExpr.CreateConst false)
|
||||||
|
yield
|
||||||
|
SynAttribute.create
|
||||||
|
(SynLongIdent.createS' [ "RestEase" ; "BasePath" ])
|
||||||
|
(SynExpr.CreateConst basePath)
|
||||||
|
match options.CreateMock with
|
||||||
|
| None -> ()
|
||||||
|
| Some createMockValue ->
|
||||||
|
yield
|
||||||
|
SynAttribute.create
|
||||||
|
(SynLongIdent.createS' [ "GenerateMock" ])
|
||||||
|
(SynExpr.CreateConst createMockValue)
|
||||||
|
]
|
||||||
|
|
||||||
|
SynComponentInfo.create (Ident.create ("I" + options.ClassName))
|
||||||
|
|> SynComponentInfo.withDocString clientDocString
|
||||||
|
|> SynComponentInfo.addAttributes attrs
|
||||||
|
)
|
||||||
|
|> List.singleton
|
||||||
|
|> SynModuleDecl.createTypes
|
||||||
|
|> List.singleton
|
||||||
|
|
||||||
|
open Myriad.Core
|
||||||
|
|
||||||
|
/// Myriad generator that stamps out an interface and class to access a Swagger-specified API.
|
||||||
|
[<MyriadGenerator("swagger-client")>]
|
||||||
|
type SwaggerClientGenerator () =
|
||||||
|
|
||||||
|
interface IMyriadGenerator with
|
||||||
|
member _.ValidInputExtensions = [ ".json" ]
|
||||||
|
|
||||||
|
member _.Generate (context : GeneratorContext) =
|
||||||
|
let contents = File.ReadAllText context.InputFilename |> Swagger.parse
|
||||||
|
|
||||||
|
let scheme =
|
||||||
|
let preferred = Scheme "https"
|
||||||
|
|
||||||
|
if List.isEmpty contents.Schemes then
|
||||||
|
failwith "no schemes specified in API spec!"
|
||||||
|
|
||||||
|
if List.contains preferred contents.Schemes then
|
||||||
|
preferred
|
||||||
|
else
|
||||||
|
List.head contents.Schemes
|
||||||
|
|
||||||
|
let clientDocstring = contents.Info.Description |> PreXmlDoc.create
|
||||||
|
|
||||||
|
let basePath = contents.BasePath
|
||||||
|
|
||||||
|
let typeDefs =
|
||||||
|
let bigCache = Dictionary<_, Dictionary<_, _>> ()
|
||||||
|
|
||||||
|
let countAll () =
|
||||||
|
(0, bigCache) ||> Seq.fold (fun count (KeyValue (_, v)) -> count + v.Count)
|
||||||
|
|
||||||
|
let byHandle = Dictionary ()
|
||||||
|
let anonymousTypeCount = ref 0
|
||||||
|
|
||||||
|
let rec go (contents : ((string * Definition) * string) list) =
|
||||||
|
let lastRound = countAll ()
|
||||||
|
|
||||||
|
contents
|
||||||
|
|> List.filter (fun ((name, defn), defnClass) ->
|
||||||
|
let doIt =
|
||||||
|
SwaggerClientGenerator.defnToType
|
||||||
|
anonymousTypeCount
|
||||||
|
byHandle
|
||||||
|
bigCache
|
||||||
|
defnClass
|
||||||
|
(Some name)
|
||||||
|
defn
|
||||||
|
|
||||||
|
match doIt with
|
||||||
|
| None -> true
|
||||||
|
| Some _ -> false
|
||||||
|
)
|
||||||
|
|> fun remaining ->
|
||||||
|
if not remaining.IsEmpty then
|
||||||
|
let currentCount = countAll ()
|
||||||
|
|
||||||
|
if currentCount = lastRound then
|
||||||
|
for (name, remaining), kind in remaining do
|
||||||
|
SwaggerClientGenerator.log $"Remaining: %s{name} (%s{kind})"
|
||||||
|
|
||||||
|
SwaggerClientGenerator.log "--------"
|
||||||
|
|
||||||
|
for KeyValue (handle, defn) in byHandle do
|
||||||
|
SwaggerClientGenerator.log $"Known: %s{handle} %O{defn}"
|
||||||
|
|
||||||
|
// TODO: ohh noooooo the Gitea spec is genuinely circular,
|
||||||
|
// it's impossible to construct a Repository type
|
||||||
|
// we're going to have to somehow detect this case and break the cycle
|
||||||
|
// by artificially making a property optional
|
||||||
|
// :sob: Gitea why are you like this
|
||||||
|
// failwith "Made no further progress rendering types"
|
||||||
|
()
|
||||||
|
else
|
||||||
|
go remaining
|
||||||
|
|
||||||
|
seq {
|
||||||
|
for defnClass in [ "definitions" ; "responses" ] do
|
||||||
|
match defnClass with
|
||||||
|
| "definitions" ->
|
||||||
|
for KeyValue (k, v) in contents.Definitions do
|
||||||
|
yield (k, v), defnClass
|
||||||
|
| "responses" ->
|
||||||
|
for KeyValue (k, v) in contents.Responses do
|
||||||
|
yield (k, v.Schema), defnClass
|
||||||
|
| _ -> failwith "oh no"
|
||||||
|
}
|
||||||
|
|> Seq.toList
|
||||||
|
|> go
|
||||||
|
|
||||||
|
let result = Dictionary ()
|
||||||
|
|
||||||
|
for KeyValue (_container, types) in bigCache do
|
||||||
|
for KeyValue (defn, rendered) in types do
|
||||||
|
result.TryAdd (defn, rendered) |> ignore<bool>
|
||||||
|
|
||||||
|
{
|
||||||
|
ByHandle = byHandle
|
||||||
|
ByDefinition = result :> IReadOnlyDictionary<_, _>
|
||||||
|
}
|
||||||
|
|
||||||
|
let summary =
|
||||||
|
contents.Paths
|
||||||
|
|> Seq.collect (fun (KeyValue (path, endpoints)) ->
|
||||||
|
endpoints
|
||||||
|
|> Seq.choose (fun (KeyValue (method, endpoint)) ->
|
||||||
|
let docstring = endpoint.Summary |> PreXmlDoc.create
|
||||||
|
|
||||||
|
let produces =
|
||||||
|
match endpoint.Produces with
|
||||||
|
| None -> Produces "json"
|
||||||
|
| Some [] -> failwith $"API specified empty Produces: %s{path} (%O{method})"
|
||||||
|
| Some [ MimeType "application/json" ] -> Produces "json"
|
||||||
|
| Some [ MimeType (StartsWith "text/" t) ] -> Produces t
|
||||||
|
| Some [ MimeType s ] ->
|
||||||
|
failwithf
|
||||||
|
$"we don't support non-JSON Produces right now, got: %s{s} (%s{path} %O{method})"
|
||||||
|
| Some (_ :: _) ->
|
||||||
|
failwith $"we don't support multiple Produces right now, at %s{path} (%O{method})"
|
||||||
|
|
||||||
|
let returnType =
|
||||||
|
endpoint.Responses
|
||||||
|
|> Seq.choose (fun (KeyValue (response, defn)) ->
|
||||||
|
if 200 <= response && response < 300 then
|
||||||
|
Some defn
|
||||||
|
else
|
||||||
|
None
|
||||||
|
)
|
||||||
|
|> Seq.toList
|
||||||
|
|
||||||
|
let returnType =
|
||||||
|
match returnType with
|
||||||
|
| [ t ] -> Some t
|
||||||
|
| [] -> failwith $"got no successful response results, %s{path} %O{method}"
|
||||||
|
| _ ->
|
||||||
|
SwaggerClientGenerator.log
|
||||||
|
$"Ignoring %s{path} %O{method} due to multiple success responses"
|
||||||
|
// can't be bothered to work out how to deal with multiple success
|
||||||
|
// results right now
|
||||||
|
None
|
||||||
|
|
||||||
|
match returnType with
|
||||||
|
| None -> None
|
||||||
|
| Some returnType ->
|
||||||
|
|
||||||
|
{
|
||||||
|
Method = method
|
||||||
|
Produces = produces
|
||||||
|
DocString = docstring
|
||||||
|
ReturnType = returnType
|
||||||
|
Operation = endpoint.OperationId
|
||||||
|
Parameters = endpoint.Parameters |> Option.defaultValue []
|
||||||
|
Endpoint = path
|
||||||
|
}
|
||||||
|
|> Some
|
||||||
|
)
|
||||||
|
|> Seq.toList
|
||||||
|
)
|
||||||
|
|> Seq.toList
|
||||||
|
|
||||||
|
let config =
|
||||||
|
// Bug in Myriad, their arg parsing is borked.
|
||||||
|
let pars =
|
||||||
|
context.AdditionalParameters
|
||||||
|
|> Seq.map (fun (KeyValue (k, v)) -> k, v)
|
||||||
|
|> Seq.toList
|
||||||
|
|
||||||
|
let pars =
|
||||||
|
match pars with
|
||||||
|
| [] ->
|
||||||
|
failwith "No parameters given. You must supply the <ClassName /> parameter in <MyriadParams />."
|
||||||
|
| [ key, value ] ->
|
||||||
|
let semicolon = value.IndexOf ';'
|
||||||
|
|
||||||
|
if semicolon >= 0 then
|
||||||
|
let equals = value.IndexOf ('=', semicolon)
|
||||||
|
|
||||||
|
[
|
||||||
|
key, value.Substring (0, semicolon)
|
||||||
|
value.Substring (semicolon + 1, equals - semicolon - 1), value.Substring (equals + 1)
|
||||||
|
]
|
||||||
|
else
|
||||||
|
[ key, value ]
|
||||||
|
| rest -> rest
|
||||||
|
|> List.map (fun (key, value) -> key.ToUpperInvariant (), value)
|
||||||
|
|> Map.ofList
|
||||||
|
|
||||||
|
let createMock =
|
||||||
|
match Map.tryFind "GENERATEMOCKVISIBILITY" pars with
|
||||||
|
| None -> None
|
||||||
|
| Some v ->
|
||||||
|
match v.ToLowerInvariant () with
|
||||||
|
| "internal" -> Some true
|
||||||
|
| "public" -> Some false
|
||||||
|
| _ ->
|
||||||
|
failwith
|
||||||
|
$"Expected GenerateMockVisibility parameter to be 'internal' or 'public', but was: '%s{v.ToLowerInvariant ()}'"
|
||||||
|
|
||||||
|
let className =
|
||||||
|
match Map.tryFind "CLASSNAME" pars with
|
||||||
|
| None -> failwith "You must supply the <ClassName /> parameter in <MyriadParams />."
|
||||||
|
| Some v -> v
|
||||||
|
|
||||||
|
{
|
||||||
|
CreateMock = createMock
|
||||||
|
ClassName = className
|
||||||
|
}
|
||||||
|
|
||||||
|
let ty =
|
||||||
|
SwaggerClientGenerator.computeType config basePath typeDefs clientDocstring summary
|
||||||
|
|
||||||
|
[
|
||||||
|
yield
|
||||||
|
SynModuleDecl.Open (
|
||||||
|
SynOpenDeclTarget.ModuleOrNamespace (
|
||||||
|
SynLongIdent.createS' [ "WoofWare" ; "Myriad" ; "Plugins" ],
|
||||||
|
range0
|
||||||
|
),
|
||||||
|
range0
|
||||||
|
)
|
||||||
|
yield SwaggerClientGenerator.instantiateRequiredTypes typeDefs
|
||||||
|
yield! ty
|
||||||
|
]
|
||||||
|
|> SynModuleOrNamespace.createNamespace [ Ident.create config.ClassName ]
|
||||||
|
|> List.singleton
|
||||||
|
|> Output.Ast
|
@@ -374,3 +374,6 @@ module internal SynExpr =
|
|||||||
|> paren
|
|> paren
|
||||||
|
|
||||||
let assign (lhs : SynLongIdent) (rhs : SynExpr) : SynExpr = SynExpr.LongIdentSet (lhs, rhs, range0)
|
let assign (lhs : SynLongIdent) (rhs : SynExpr) : SynExpr = SynExpr.LongIdentSet (lhs, rhs, range0)
|
||||||
|
|
||||||
|
let assignIndex (lhs : SynExpr) (index : SynExpr) (rhs : SynExpr) : SynExpr =
|
||||||
|
SynExpr.DotIndexedSet (lhs, index, rhs, range0, range0, range0)
|
||||||
|
11
WoofWare.Myriad.Plugins/Text.fs
Normal file
11
WoofWare.Myriad.Plugins/Text.fs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
namespace WoofWare.Myriad.Plugins
|
||||||
|
|
||||||
|
open System
|
||||||
|
|
||||||
|
[<AutoOpen>]
|
||||||
|
module internal Text =
|
||||||
|
let (|StartsWith|_|) (prefix : string) (s : string) : string option =
|
||||||
|
if s.StartsWith (prefix, StringComparison.Ordinal) then
|
||||||
|
Some (s.Substring prefix.Length)
|
||||||
|
else
|
||||||
|
None
|
@@ -25,6 +25,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="List.fs"/>
|
<Compile Include="List.fs"/>
|
||||||
|
<Compile Include="Text.fs" />
|
||||||
<Compile Include="Teq.fs" />
|
<Compile Include="Teq.fs" />
|
||||||
<Compile Include="Primitives.fs" />
|
<Compile Include="Primitives.fs" />
|
||||||
<Compile Include="SynExpr\SynAttributes.fs" />
|
<Compile Include="SynExpr\SynAttributes.fs" />
|
||||||
@@ -58,6 +59,8 @@
|
|||||||
<Compile Include="HttpClientGenerator.fs"/>
|
<Compile Include="HttpClientGenerator.fs"/>
|
||||||
<Compile Include="CataGenerator.fs" />
|
<Compile Include="CataGenerator.fs" />
|
||||||
<Compile Include="ArgParserGenerator.fs" />
|
<Compile Include="ArgParserGenerator.fs" />
|
||||||
|
<Compile Include="Swagger.fs" />
|
||||||
|
<Compile Include="SwaggerClientGenerator.fs" />
|
||||||
<None Include="TeqLicence.txt" />
|
<None Include="TeqLicence.txt" />
|
||||||
<EmbeddedResource Include="version.json"/>
|
<EmbeddedResource Include="version.json"/>
|
||||||
<EmbeddedResource Include="SurfaceBaseline.txt"/>
|
<EmbeddedResource Include="SurfaceBaseline.txt"/>
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"version": "3.0",
|
"version": "3.1",
|
||||||
"publicReleaseRefSpec": [
|
"publicReleaseRefSpec": [
|
||||||
"^refs/heads/main$"
|
"^refs/heads/main$"
|
||||||
],
|
],
|
||||||
|
Reference in New Issue
Block a user