Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
135 changes: 135 additions & 0 deletions docs/Customization.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
# Customizations

OpenAPI and Swagger type providers one or several provided API client (depending on the value of `IgnoreControllerPrefix`).
Each provided API client is subclass of `ProvidedApiClientBase` that allow you control and customize HTTP calls.

```fsharp
type ProvidedApiClientBase(httpClient: HttpClient) =
member val HttpClient = httpClient with get, set

abstract member Serialize: obj -> string
abstract member Deserialize: string * Type -> obj
```

Snippet shows only most important parts of `ProvidedApiClientBase`, the full source code provide default implementation for `Serialize` & `Deserialize` methods that tightly coupled with [Newtonsoft.Json](https://www.nuget.org/packages/Newtonsoft.Json/).

**Key features:**
1. You can provide your own instance of `HttpClient` during API client construction and control HTTP request execution (If you will not provide `HttpClient`, type provider create default one for you).
2. `Serialize` and `Deserialize` methods are abstract. If you are not happy with default `JsonSerializerSettings` you can override them and configure `Newtonsoft.Json` as you like.

## Request interception

Since you control `HttpClient` you are able to use [Outgoing request middleware](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/http-requests?view=aspnetcore-3.0#outgoing-request-middleware) implement your own `DelegatingHandler` and intercept all HTTP request generated by provided API client.

```fsharp {highlight:['8-13','18-20']}
open System
open System.Net.Http
open SwaggerProvider

let [<Literal>] Schema = "https://petstore.swagger.io/v2/swagger.json"
type PetStore = OpenApiClientProvider<Schema, PreferAsync=true>

type LoggingHandler(messageHandler) =
inherit DelegatingHandler(messageHandler)
override __.SendAsync(request, cancellationToken) =
// Put break point here is want to debug HTTP calls
printfn "[%A]: %A" request.Method request.RequestUri
base.SendAsync(request, cancellationToken)

[<EntryPoint>]
let main argv =
let baseAddress = Uri("https://petstore.swagger.io/v2/")
let handler1 = new HttpClientHandler (UseCookies = false)
let handler2 = new LoggingHandler(handler1)
use httpClient = new HttpClient(handler2, true, BaseAddress=baseAddress)
let client = PetStore.Client(httpClient)
async {
let pet = PetStore.Pet(Id = Some(24L), Name = "Shani")
do! client.AddPet(pet)
let! myPet = client.GetPetById(24L)
printfn "Waw, my name is %A" myPet.Name
}
|> Async.RunSynchronously
0
```

## Authentication

Authentication is just a special case `Request interception`. Your custom `DelegatingHandler` is fully responsible for management of authentication information (attach Authentication Header, authentication cookie, invalidate it and etc.).

```fsharp {highlight:['4-6']}
type AuthHandler(messageHandler) =
inherit DelegatingHandler(messageHandler)
override __.SendAsync(request, cancellationToken) =
// Invalidate your token if it expired
request.Headers.Authorization <-
Headers.AuthenticationHeaderValue("Bearer", "Your OAuth token");
base.SendAsync(request, cancellationToken)
```

if your token does not require invalidation, you can use `HttpClient.DefaultRequestHeaders` to add your `Authorization` header to all requests

```fsharp {highlight:['4-5']}
let baseAddress = Uri("https://petstore.swagger.io/v2/")
let handler = new HttpClientHandler (UseCookies = false)
use httpClient = new HttpClient(handler, true, BaseAddress=baseAddress)
httpClient.DefaultRequestHeaders.Authorization <-
Headers.AuthenticationHeaderValue("Bearer", "Your OAuth token")
let client = PetStore.Client(httpClient)
```

## Serialization

Serialization is also quite flexible. All you need it to define your own type for API client that will be subclass of API client generated by type provider and override `Serialize` and `Deserialize` members.

<Note type="note">

Serializer is configurable but not replaceable! Type provider emit type with [JsonPropertyAttribute](https://www.newtonsoft.com/json/help/html/T_Newtonsoft_Json_JsonPropertyAttribute.htm) on properties for seamless serialization.

</Note>

```fsharp {highlight:['9-10', '24-28', '33-34']}
open System
open SwaggerProvider
open Newtonsoft.Json

let [<Literal>] Schema = "https://petstore.swagger.io/v2/swagger.json"
type PetStore = OpenApiClientProvider<Schema, PreferAsync=true>

type MyApiClient() =
// inherit from provided API client
inherit PetStore.Client()

let jsonSerializerSettings =
let settings =
JsonSerializerSettings(
NullValueHandling = NullValueHandling.Ignore,
Formatting = Formatting.None)
[ // Your custom JsonConverter's for F# types
Swagger.Serialization.OptionConverter () :> JsonConverter
Swagger.Serialization.ByteArrayConverter () :> JsonConverter
]
|> List.iter settings.Converters.Add
settings

// Overload default implementation of Serialize & Deserialize
override __.Serialize(value:obj): string =
JsonConvert.SerializeObject(value, jsonSerializerSettings)
override __.Deserialize(value, retTy:Type): obj =
JsonConvert.DeserializeObject(value, retTy, jsonSerializerSettings)


[<EntryPoint>]
let main argv =
// Instantiate your API client
let client = MyApiClient()
async {
let pet = PetStore.Pet(Id = Some(24L), Name = "Shani")
do! client.AddPet(pet)
let! myPet = client.GetPetById(24L)
printfn "Waw, my name is %A" myPet.Name
}
|> Async.RunSynchronously
0

```
65 changes: 65 additions & 0 deletions docs/OpenApiClientProvider.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# OpenAPI Client Provider

OpenApiClientProvider is generative F# Type Provider, build on top of [Microsoft.OpenApi.Readers](https://www.nuget.org/packages/Microsoft.OpenApi.Readers/) schema parser that supports 3.0 and 2.0 schema formats.

```fsharp
open SwaggerProvider

let [<Literal>] Schema = "https://petstore.swagger.io/v2/swagger.json"
type PetStore = OpenApiClientProvider<Schema>
let client = PetStore.Client()
```

## Parameters

`OpenApiClientProvider` supports following configuration parametes

| Parameter | Description |
|-----------|-------------|
| `Schema` | Url or Path to Swagger schema file. |
| `IgnoreOperationId` | Do not use `operationsId` and generate method names using `path` only. Default value `false`. |
| `IgnoreControllerPrefix` | Do not parse `operationsId` as `<controllerName>_<methodName>` and generate one client class for all operations. Default value `true`. |
| `PreferNullable` | Provide `Nullable<_>` for not required properties, instead of `Option<_>`. Defaults value `false`. |
| `PreferAsync` | Generate async actions of type `Async<'T>` instead of `Task<'T>`. Defaults value `false`. |

More configuration scenarios are described in [Customization section](/Customization)

## Sample

Sample uses [TaskBuilder.fs](https://github.com/rspeele/TaskBuilder.fs) (F# computation expression builder for System.Threading.Tasks) that will become part of [Fsharp.Core.dll] one day [[WIP, RFC FS-1072] task support](https://github.com/dotnet/fsharp/pull/6811).

```fsharp
open System
open System.Net.Http
open FSharp.Control.Tasks.V2
open SwaggerProvider

let [<Literal>] Schema = "https://petstore.swagger.io/v2/swagger.json"
// By default provided methods return Task<'a>
// and uses Option<'a> for optional params
type PetStore = OpenApiClientProvider<Schema>

[<EntryPoint>]
let main argv =
// `UseCookies = false` is required if you use Cookie Parameters
let handler = new HttpClientHandler (UseCookies = false)
// `BaseAddress` uri should ends with '/' because TP generate relative uri
let baseUri = Uri("https://petstore.swagger.io/v2/")
use httpClient = new HttpClient(handler, true, BaseAddress=baseUri)
// You can provide your instance of `HttpClient` to provided api client
// or change it any time in runtime using `client.HttpClient` property
let client = PetStore.Client(httpClient)

task {
// Create new instance of provided type and add to store
let pet = PetStore.Pet(Id = Some(24L), Name = "Shani")
do! client.AddPet(pet)

// Request data back and deserialize to provided type
let! myPet = client.GetPetById(24L)
printfn "Waw, my name is %A" myPet.Name
}
|> Async.AwaitTask
|> Async.RunSynchronously
0
```
3 changes: 0 additions & 3 deletions docs/OpenApiClientProvider/README.md

This file was deleted.

26 changes: 15 additions & 11 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,19 @@
[![NuGet Badge](https://buildstats.info/nuget/SwaggerProvider?includePreReleases=true)](https://www.nuget.org/packages/SwaggerProvider)

`SwaggerProvider` is an umbrella project for two F# generative Type Providers that generate object model and HTTP clients for APIs described by [OpenApi 3.0](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md) and [Swagger 2.0](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md) schemas
- [OpenApiClientProvider](/OpenApiClientProvider/) <Badge type="success">New</Badge> - uses [Microsoft.OpenApi.Readers](https://www.nuget.org/packages/Microsoft.OpenApi.Readers/) to parse schema. Support both OpenApi ans Swagger schemas, but Swagger support is limited.
- [SwaggerClientProvider](/SwaggerClientProvider/) - uses custom old good Swagger 2.0 schema parser and tested on several hundreds schemas available in [APIs.guru](https://apis.guru/openapi-directory/) (Wikipedia for WEB APIs)
- [OpenApiClientProvider](/OpenApiClientProvider) <Badge type="success">New</Badge> - uses [Microsoft.OpenApi.Readers](https://www.nuget.org/packages/Microsoft.OpenApi.Readers/) to parse schema. Support both OpenApi ans Swagger schemas, but Swagger support is limited.
- [SwaggerClientProvider](/SwaggerClientProvider) - uses custom old good Swagger 2.0 schema parser and tested on several hundreds schemas available in [APIs.guru](https://apis.guru/openapi-directory/) (Wikipedia for WEB APIs)

Type Providers support schemas in `JSON` & `YAML` formats and runs on `netcoreapp3.0` and `net46`.

### Getting started

Create new F# `netcoreapp3.0` project

Create new F# `netcoreapp3.0` project and add reference to latest [SwaggerProvider](https://www.nuget.org/packages/SwaggerProvider) NuGet package
```bash
dotnet new console --language F# --name apiclient
dotnet new console --name apiclient --language F#
cd apiclient
dotnet add package SwaggerProvider --version 0.10.0-beta08
dotnet add package SwaggerProvider --version 0.10.0-beta09
```

replace content of `Program.fs` file by
Expand All @@ -35,18 +35,22 @@ let main argv =
0
```

build and run projects
build and run project

```bash
dotnet run
dotnet run
```

in the console you should see printed inventory from the server.

### Intellisense

Intellisense should work in you favorite IDE.
Intellisense works in your favorite IDE.

<ImageZoom src="files/img/OpenApiClientProvider.png" />
| [Visual Studio Code](https://code.visualstudio.com) + [Ionide](http://ionide.io) | [Rider](https://www.jetbrains.com/help/rider/F_Sharp.html) |
|-----------|-------------|
| <ImageZoom src="files/OpenApiClientProvider_Ionide.png" /> | <ImageZoom src="files/OpenApiClientProvider_Rider.png" /> |

On the screenshot you see [VS Code](https://code.visualstudio.com) with [Ionide](http://ionide.io).
| [Visual Studio for Mac](https://visualstudio.microsoft.com/vs/mac/) | [Visual Studio](https://visualstudio.microsoft.com/vs/) |
|-----------|-------------|
| <ImageZoom src="files/OpenApiClientProvider_VS4Mac.png" /> | <ImageZoom src="files/OpenApiClientProvider_VS.png" /> |
9 changes: 5 additions & 4 deletions docs/RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
#### 0.10.0-beta09 - November 3, 2019
- TP parameter names are unified [#129](https://github.com/fsprojects/SwaggerProvider/pull/129)

#### 0.10.0-beta08 - November 3, 2019
- Migration to new Type Provider SDK [#88](https://github.com/fsprojects/SwaggerProvider/pull/88)
- Migration to new Type Provider SDK [#88](https://github.com/fsprojects/SwaggerProvider/pull/88) [#125](https://github.com/fsprojects/SwaggerProvider/pull/125)
- SwaggerParser moved to runtime assembly (and exposed as API to library users) - [#90](https://github.com/fsprojects/SwaggerProvider/pull/90)
- Added runtime dependency on YamlDotNet
- Removed dependency on FSharp.Data/JsonValue (replaced by JSON.NET)
Expand All @@ -11,10 +14,8 @@
- Supported parsing of schemas with inheritance in types defined inside operation parameters
- Added Async & Task-based calls for the Operations [#21](https://github.com/fsprojects/SwaggerProvider/issues/21)
- Do not reference design-time assembly when install from nuget [#104](https://github.com/fsprojects/SwaggerProvider/pull/104)
- Migration to `HttpClient`-based communication [#105](https://github.com/fsprojects/SwaggerProvider/pull/105)
- TP SDK update - [#125](https://github.com/fsprojects/SwaggerProvider/pull/125)
- Migration to `HttpClient`-based communication [#105](https://github.com/fsprojects/SwaggerProvider/pull/105) [#100](https://github.com/fsprojects/SwaggerProvider/issues/100)
- Fixed null reference in query params [#126](https://github.com/fsprojects/SwaggerProvider/issues/126)
- HttpClient is used for all calls [#100](https://github.com/fsprojects/SwaggerProvider/issues/100)
- OpenApiClientProvider [#117](https://github.com/fsprojects/SwaggerProvider/pull/117)

#### 0.8.2 - January 19, 2017
Expand Down
2 changes: 1 addition & 1 deletion docs/References.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,4 @@
- [Application contracts with Swagger powered APIs for .NET or Why SwaggerProvider](https://sergeytihon.wordpress.com/2015/12/07/application-contracts-with-swagger-powered-apis-for-net-or-why-swaggerprovider/) - Sergey Tihon
- [Swagger for F# Web Apps](https://sergeytihon.wordpress.com/2015/09/06/swagger-for-f-web-apps/) - Sergey Tihon

Written something about SwaggerProvider? [Send a pull request](https://github.com/fsprojects/SwaggerProvider/tree/master/docs)!
Written something about OpenApi, Swagger or SwaggerProvider? [Send a pull request](https://github.com/fsprojects/SwaggerProvider/tree/master/docs)!
60 changes: 60 additions & 0 deletions docs/SwaggerClientProvider.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Swagger Client Provider

<Note type="warning">

The SwaggerClientProvider is outdated. There is no plans to improve custom Swagger 2.0 schema parser or bring new features to this type provider. We hope to remove it from source code when users migrate to [OpenApiClientProvider](/OpenApiClientProvider) and OpenApi 3.0 schemas.

</Note>

SwaggerClientProvider is generative F# Type Provider, build on top of custom Swagger schema parser that supports **only** 2.0 schema format.

```fsharp
open SwaggerProvider

let [<Literal>]schema = "http://petstore.swagger.io/v2/swagger.json"
type PetStore = SwaggerClientProvider<schema>
let petStore = PetStore.Client()
```

## Parameters

When you use TP you can specify following parameters

| Parameter | Description |
|-----------|-------------|
| `Schema` | Url or Path to Swagger schema file. |
| `Headers` | HTTP Headers requiried to access the schema. |
| `IgnoreOperationId` | Do not use `operationsId` and generate method names using `path` only. Default value `false`. |
| `IgnoreControllerPrefix` | Do not parse `operationsId` as `<controllerName>_<methodName>` and generate one client class for all operations. Default value `true`. |
| `PreferNullable` | Provide `Nullable<_>` for not required properties, instead of `Option<_>`. Defaults value `false`. |
| `PreferAsync` | Generate async actions of type `Async<'T>` instead of `Task<'T>`. Defaults value `false`. |

More configuration scenarios are described in [Customization section](/Customization)

## Sample

The usage is very similar to [OpenApiClientProvider](/OpenApiClientProvider#sample)

```fsharp
open SwaggerProvider

let [<Literal>] Schema = "https://petstore.swagger.io/v2/swagger.json"
// Explicitly request Async<'a> methods instead of Task<'a>
type PetStore = SwaggerClientProvider<Schema, PreferAsync=true>

[<EntryPoint>]
let main argv =
// Type Provider creates HttpClient for you under the hood
let client = PetStore.Client()
async {
// Create new instance of provided type and add to store
let pet = PetStore.Pet(Id = Some(24L), Name = "Shani")
do! client.AddPet(pet)

// Request data back and deserialize to provided type
let! myPet = client.GetPetById(24L)
printfn "Waw, my name is %A" myPet.Name
}
|> Async.RunSynchronously
0
```
Loading