Skip to content
1 change: 1 addition & 0 deletions docs/RELEASE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
- BREAKING: `task` CE wrap all exceptions in `AggregateException` (with `OpenApiException` inside)
- Model enums as `string`, `int32` or `boolean` (Fixed [#186](https://github.com/fsprojects/SwaggerProvider/issues/186) )
- Add `Accept` header to all request (Fixed [#196](https://github.com/fsprojects/SwaggerProvider/issues/196))
- Supported requests with `octet-stream` body content [#203](https://github.com/fsprojects/SwaggerProvider/pull/203)
- Microsoft.OpenApi v1.4.1

#### 1.0.2 - Jul 10, 2022
Expand Down
13 changes: 6 additions & 7 deletions src/SwaggerProvider.DesignTime/Caching.fs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ module SwaggerProvider.Caching

open System
open System.Collections.Concurrent
open System.IO

// https://github.com/fsharp/FSharp.Data/blob/master/src/CommonRuntime/IO.fs

Expand Down Expand Up @@ -74,7 +73,7 @@ let internal logTime category (instance: string) =

let internal dummyDisposable =
{ new IDisposable with
member __.Dispose() = ()
member _.Dispose() = ()
}

let inline internal log(_: string) = ()
Expand Down Expand Up @@ -112,25 +111,25 @@ let createInMemoryCache(expiration: TimeSpan) =
}

{ new ICache<_, _> with
member __.Set(key, value) =
dict.[key] <- (value, DateTime.UtcNow)
member _.Set(key, value) =
dict[key] <- (value, DateTime.UtcNow)
invalidationFunction key |> Async.Start

member x.TryRetrieve(key, ?extendCacheExpiration) =
match dict.TryGetValue(key) with
| true, (value, timestamp) when DateTime.UtcNow - timestamp < expiration ->
if extendCacheExpiration = Some true then
dict.[key] <- (value, DateTime.UtcNow)
dict[key] <- (value, DateTime.UtcNow)

Some value
| _ -> None

member __.Remove(key) =
member _.Remove(key) =
match dict.TryRemove(key) with
| true, _ -> log $"Explicitly removed from cache: {key}"
| _ -> ()

member __.GetOrAdd(key, valueFactory) =
member _.GetOrAdd(key, valueFactory) =
let res, _ = dict.GetOrAdd(key, (fun k -> valueFactory(), DateTime.UtcNow))
invalidationFunction key |> Async.Start
res
Expand Down
17 changes: 1 addition & 16 deletions src/SwaggerProvider.DesignTime/Provider.SwaggerClient.fs
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,8 @@ open SwaggerProvider.Internal
open SwaggerProvider.Internal.v2.Parser
open SwaggerProvider.Internal.v2.Compilers

//module Handlers =

// let logError event (ex: exn) =
// let ex =
// match ex with
// | :? TypeInitializationException as typInit -> typInit.InnerException
// | _ -> ex
// Logging.logf "[%s]\t%s: %s\n%s" event (ex.GetType().Name) ex.Message ex.StackTrace
// let logResolve kind (args: ResolveEventArgs) = Logging.logf "[%s]\t%s on behalf of %s" kind args.Name args.RequestingAssembly.FullName


/// The Swagger Type Provider.
[<TypeProvider>]
[<TypeProvider; Obsolete("Use OpenApiClientTypeProvider when possible, it supports v2 & v3 schema formats.")>]
type public SwaggerTypeProvider(cfg: TypeProviderConfig) as this =
inherit TypeProviderForNamespaces
(
Expand All @@ -30,10 +19,6 @@ type public SwaggerTypeProvider(cfg: TypeProviderConfig) as this =
addDefaultProbingLocation = true
)

// static do
// AppDomain.CurrentDomain.FirstChanceException.Add(fun args -> Handlers.logError "FirstChanceException" args.Exception)
// AppDomain.CurrentDomain.UnhandledException.Add(fun args -> Handlers.logError "UnhandledException" (args.ExceptionObject :?> exn))

let ns = "SwaggerProvider"
let asm = Assembly.GetExecutingAssembly()

Expand Down
8 changes: 4 additions & 4 deletions src/SwaggerProvider.DesignTime/Utils.fs
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,11 @@ module SchemaReader =
|> Seq.choose(fun x ->
let pair = x.Split('=')

if (pair.Length = 2) then Some(pair.[0], pair.[1]) else None)
if (pair.Length = 2) then Some(pair[0], pair[1]) else None)

let request = new HttpRequestMessage(HttpMethod.Get, schemaPathRaw)

for (name, value) in headers do
for name, value in headers do
request.Headers.TryAddWithoutValidation(name, value) |> ignore
// using a custom handler means that we can set the default credentials.
use handler = new HttpClientHandler(UseDefaultCredentials = true)
Expand All @@ -44,7 +44,7 @@ module SchemaReader =

match res with
| Choice1Of2 x -> return x
| Choice2Of2(:? System.Net.WebException as wex) when not <| isNull wex.Response ->
| Choice2Of2(:? WebException as wex) when not <| isNull wex.Response ->
use stream = wex.Response.GetResponseStream()
use reader = new StreamReader(stream)
let err = reader.ReadToEnd()
Expand Down Expand Up @@ -75,5 +75,5 @@ type UniqueNameGenerator() =
newName
| true -> findUniq prefix (i + 1)

member __.MakeUnique methodName =
member _.MakeUnique methodName =
findUniq methodName 0
54 changes: 27 additions & 27 deletions src/SwaggerProvider.DesignTime/v2/DefinitionCompiler.fs
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ type DefinitionPath =
if ind = definitionPath.Length then
ind - 1
elif
Char.IsLetterOrDigit definitionPath.[ind]
|| definitionPath.[ind] = nsSeparator
Char.IsLetterOrDigit definitionPath[ind]
|| definitionPath[ind] = nsSeparator
then
getCharInTypeName(ind + 1)
else
Expand Down Expand Up @@ -76,10 +76,10 @@ and NamespaceAbstraction(name: string) =
| _, value -> failwithf $"Cannot %s{opName} '%s{tyName}' because the slot is used by %A{value}"

/// Namespace name
member __.Name = name
member _.Name = name

/// Generate unique name and reserve it for the type
member __.ReserveUniqueName namePref nameSuffix = // TODO: Strange signature - think more
member _.ReserveUniqueName namePref nameSuffix = // TODO: Strange signature - think more
let rec findUniq prefix i =
let newName = sprintf "%s%s" prefix (if i = 0 then "" else i.ToString())

Expand All @@ -93,34 +93,34 @@ and NamespaceAbstraction(name: string) =
newName

/// Release previously reserved name
member __.ReleaseNameReservation tyName =
member _.ReleaseNameReservation tyName =
updateReservation "release the name" tyName (fun () -> providedTys.Remove(tyName) |> ignore)

/// Mark type name as named alias for basic type
member __.MarkTypeAsNameAlias tyName =
updateReservation "mark as Alias type" tyName (fun () -> providedTys.[tyName] <- NameAlias)
member _.MarkTypeAsNameAlias tyName =
updateReservation "mark as Alias type" tyName (fun () -> providedTys[tyName] <- NameAlias)

/// Associate ProvidedType with reserved type name
member __.RegisterType(tyName, ty) =
member _.RegisterType(tyName, ty) =
match providedTys.TryGetValue tyName with
| true, Reservation -> providedTys.[tyName] <- ProvidedType ty
| true, Namespace ns -> providedTys.[tyName] <- NestedType(ty, ns)
| true, Reservation -> providedTys[tyName] <- ProvidedType ty
| true, Namespace ns -> providedTys[tyName] <- NestedType(ty, ns)
| false, _ -> failwithf $"Cannot register the type '%s{tyName}' because name was not reserved"
| _, value -> failwithf $"Cannot register the type '%s{tyName}' because the slot is used by %A{value}"

/// Get or create sub-namespace
member __.GetOrCreateNamespace name =
member _.GetOrCreateNamespace name =
match providedTys.TryGetValue name with
| true, Namespace ns -> ns
| true, NestedType(_, ns) -> ns
| true, ProvidedType ty ->
let ns = NamespaceAbstraction(name)
providedTys.[name] <- NestedType(ty, ns)
providedTys[name] <- NestedType(ty, ns)
ns
| false, _
| true, Reservation ->
let ns = NamespaceAbstraction(name)
providedTys.[name] <- Namespace ns
providedTys[name] <- Namespace ns
ns
| true, value -> failwithf $"Name collision, cannot create namespace '%s{name}' because it used by '%A{value}'"

Expand All @@ -133,7 +133,7 @@ and NamespaceAbstraction(name: string) =
ns.Resolve { dPath with Namespace = tail }

/// Create Provided representation of Namespace
member __.GetProvidedTypes() =
member _.GetProvidedTypes() =
List.ofSeq providedTys
|> List.choose(fun kv ->
match kv.Value with
Expand Down Expand Up @@ -168,7 +168,7 @@ type DefinitionCompiler(schema: SwaggerObject, provideNullable) as this =
let propertyName = scope.MakeUnique <| nicePascalName propName

let providedField =
let fieldName = $"_%c{Char.ToLower propertyName.[0]}%s{propertyName.Substring(1)}"
let fieldName = $"_%c{Char.ToLower propertyName[0]}%s{propertyName.Substring(1)}"

ProvidedField(fieldName, ty)

Expand Down Expand Up @@ -239,7 +239,7 @@ type DefinitionCompiler(schema: SwaggerObject, provideNullable) as this =
let pTy =
compileSchemaObject ns (ns.ReserveUniqueName tyName (nicePascalName p.Name)) p.Type p.IsRequired ns.RegisterType

let (pField, pProp) = generateProperty p.Name pTy
let pField, pProp = generateProperty p.Name pTy

if not <| String.IsNullOrWhiteSpace p.Description then
pProp.AddXmlDoc p.Description
Expand Down Expand Up @@ -279,15 +279,15 @@ type DefinitionCompiler(schema: SwaggerObject, provideNullable) as this =
ctorParams,
invokeCode =
fun args ->
let (this, args) =
let this, args =
match args with
| x :: xs -> (x, xs)
| _ -> failwith "Wrong constructor arguments"

List.zip args fields
|> List.map(fun (arg, f) -> Expr.FieldSetUnchecked(this, f, arg))
|> List.rev
|> List.fold (fun a b -> Expr.Sequential(a, b)) (<@@ () @@>)
|> List.fold (fun a b -> Expr.Sequential(a, b)) <@@ () @@>
)

// Override `.ToString()`
Expand All @@ -299,9 +299,9 @@ type DefinitionCompiler(schema: SwaggerObject, provideNullable) as this =
isStatic = false,
invokeCode =
fun args ->
let this = args.[0]
let this = args[0]

let (pNames, pValues) =
let pNames, pValues =
Array.ofList members
|> Array.map(fun (pField, pProp) ->
let pValObj = Expr.FieldGet(this, pField)
Expand Down Expand Up @@ -329,15 +329,15 @@ type DefinitionCompiler(schema: SwaggerObject, provideNullable) as this =

let strs =
values
|> Array.mapi(fun i v -> String.Format("{0}={1}", pNames.[i], formatValue v))
|> Array.mapi(fun i v -> String.Format("{0}={1}", pNames[i], formatValue v))

String.Format("{{{0}}}", String.Join("; ", strs))
@@>
)

toStr.SetMethodAttrs(MethodAttributes.Public ||| MethodAttributes.Virtual)

let objToStr = (typeof<obj>).GetMethod("ToString", [||])
let objToStr = typeof<obj>.GetMethod ("ToString", [||])
ty.DefineMethodOverride(toStr, objToStr)
ty.AddMember <| toStr

Expand All @@ -362,10 +362,10 @@ type DefinitionCompiler(schema: SwaggerObject, provideNullable) as this =
| String -> typeof<string>
| Date
| DateTime -> typeof<DateTime>
| File -> typeof<byte>.MakeArrayType (1)
| File -> typeof<byte>.MakeArrayType 1
| Enum(_, "string") -> typeof<string>
| Enum(_, "boolean") -> typeof<bool>
| Enum(_, _) -> typeof<int32>
| Enum _ -> typeof<int32>
| Array eTy ->
(compileSchemaObject ns (ns.ReserveUniqueName tyName "Item") eTy true ns.RegisterType)
.MakeArrayType(1)
Expand Down Expand Up @@ -399,14 +399,14 @@ type DefinitionCompiler(schema: SwaggerObject, provideNullable) as this =
|> Seq.iter(fun (name, _) -> compileDefinition name |> ignore)

/// Namespace that represent provided type space
member __.Namespace = nsRoot
member _.Namespace = nsRoot

/// Method that allow OperationCompiler to resolve object reference, compile basic and anonymous types.
member __.CompileTy opName tyUseSuffix ty required =
member _.CompileTy opName tyUseSuffix ty required =
compileSchemaObject nsOps (nsOps.ReserveUniqueName opName tyUseSuffix) ty required nsOps.RegisterType

/// Default value for optional parameters
member __.GetDefaultValue _ =
member _.GetDefaultValue _ =
// This method is only used for not required types
// Reference types, Option<T> and Nullable<T>
null
22 changes: 10 additions & 12 deletions src/SwaggerProvider.DesignTime/v2/OperationCompiler.fs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ open SwaggerProvider.Internal.v2.Parser.Schema
open Swagger.Internal

open System
open System.Collections.Generic
open System.Net.Http
open System.Text.Json
open System.Text.RegularExpressions
Expand All @@ -15,7 +14,6 @@ open Microsoft.FSharp.Quotations
open Microsoft.FSharp.Quotations.ExprShape
open SwaggerProvider.Internal
open Swagger
open Swagger.Internal

/// Object for compiling operations.
type OperationCompiler(schema: SwaggerObject, defCompiler: DefinitionCompiler, ignoreControllerPrefix, ignoreOperationId, asAsync: bool) =
Expand All @@ -39,7 +37,7 @@ type OperationCompiler(schema: SwaggerObject, defCompiler: DefinitionCompiler, i
Array.append required optional
|> Array.fold
(fun (names, parameters) current ->
let (names, paramName) = uniqueParamName names current
let names, paramName = uniqueParamName names current

let paramType =
defCompiler.CompileTy methodName paramName current.Type current.Required
Expand Down Expand Up @@ -77,7 +75,7 @@ type OperationCompiler(schema: SwaggerObject, defCompiler: DefinitionCompiler, i
typedefof<Async<unit>>
else
typedefof<System.Threading.Tasks.Task<unit>>),
[ defaultArg retTy (typeof<unit>) ]
[ defaultArg retTy typeof<unit> ]
)

let m =
Expand All @@ -88,7 +86,7 @@ type OperationCompiler(schema: SwaggerObject, defCompiler: DefinitionCompiler, i
invokeCode =
fun args ->
let this =
Expr.Coerce(args.[0], typeof<ProvidedApiClientBase>)
Expr.Coerce(args[0], typeof<ProvidedApiClientBase>)
|> Expr.Cast<ProvidedApiClientBase>

let httpMethod = op.Type.ToString()
Expand Down Expand Up @@ -158,7 +156,7 @@ type OperationCompiler(schema: SwaggerObject, defCompiler: DefinitionCompiler, i
<@ Array.append %heads [| name, %value |] @>

// Partitions arguments based on their locations
let (path, payload, queries, heads) =
let path, payload, queries, heads =
let mPath = op.Path

parameters
Expand Down Expand Up @@ -260,25 +258,25 @@ type OperationCompiler(schema: SwaggerObject, defCompiler: DefinitionCompiler, i

static member GetMethodNameCandidate (op: OperationObject) skipLength ignoreOperationId =
if ignoreOperationId || String.IsNullOrWhiteSpace(op.OperationId) then
let (_, pathParts) =
let _, pathParts =
(op.Path.Split([| '/' |], StringSplitOptions.RemoveEmptyEntries), (false, []))
||> Array.foldBack(fun x (nextIsArg, pathParts) ->
if x.StartsWith("{") then
(true, pathParts)
else
(false, (if nextIsArg then singularize x else x) :: pathParts))

String.Join("_", (op.Type.ToString()) :: pathParts)
String.Join("_", op.Type.ToString() :: pathParts)
else
op.OperationId.Substring(skipLength)
|> nicePascalName

member __.CompileProvidedClients(ns: NamespaceAbstraction) =
member _.CompileProvidedClients(ns: NamespaceAbstraction) =
let defaultHost =
let protocol =
match schema.Schemes with
| [||] -> "http" // Should use the scheme used to access the Swagger definition itself.
| array -> array.[0]
| array -> array[0]

$"%s{protocol}://%s{schema.Host}"

Expand Down Expand Up @@ -334,7 +332,7 @@ type OperationCompiler(schema: SwaggerObject, defCompiler: DefinitionCompiler, i
)
ProvidedConstructor(
[ ProvidedParameter("options", typeof<JsonSerializerOptions>) ],
invokeCode = (fun args -> <@@ () @@>),
invokeCode = (fun _ -> <@@ () @@>),
BaseConstructorCall =
fun args ->
let httpClient = <@ RuntimeHelpers.getDefaultHttpClient defaultHost @> :> Expr
Expand All @@ -348,7 +346,7 @@ type OperationCompiler(schema: SwaggerObject, defCompiler: DefinitionCompiler, i
)
ProvidedConstructor(
[],
invokeCode = (fun args -> <@@ () @@>),
invokeCode = (fun _ -> <@@ () @@>),
BaseConstructorCall =
fun args ->
let httpClient = <@ RuntimeHelpers.getDefaultHttpClient defaultHost @> :> Expr
Expand Down
Loading