diff --git a/src/SwaggerProvider.DesignTime/Provider.OpenApiClient.fs b/src/SwaggerProvider.DesignTime/Provider.OpenApiClient.fs index 69b4d162..7c20e20f 100644 --- a/src/SwaggerProvider.DesignTime/Provider.OpenApiClient.fs +++ b/src/SwaggerProvider.DesignTime/Provider.OpenApiClient.fs @@ -55,18 +55,8 @@ type public OpenApiClientTypeProvider(cfg : TypeProviderConfig) as this = let addCache() = lazy let schemaData = - match schemaPathRaw.StartsWith("http", true, null) with - | true -> - let request = new HttpRequestMessage(HttpMethod.Get, schemaPathRaw) - // using a custom handler means that we can set the default credentials. - use handler = new HttpClientHandler(UseDefaultCredentials = true) - use client = new HttpClient(handler) - async { - let! response = client.SendAsync(request) |> Async.AwaitTask - return! response.Content.ReadAsStringAsync() |> Async.AwaitTask - } |> Async.RunSynchronously - | false -> - schemaPathRaw |> IO.File.ReadAllText + SwaggerProvider.Internal.SchemaReader.readSchemaPath "" schemaPathRaw + |> Async.RunSynchronously let openApiReader = Microsoft.OpenApi.Readers.OpenApiStringReader() let (schema, diagnostic) = openApiReader.Read(schemaData) diff --git a/src/SwaggerProvider.DesignTime/Provider.SwaggerClient.fs b/src/SwaggerProvider.DesignTime/Provider.SwaggerClient.fs index 6ec671b6..4bd662a2 100644 --- a/src/SwaggerProvider.DesignTime/Provider.SwaggerClient.fs +++ b/src/SwaggerProvider.DesignTime/Provider.SwaggerClient.fs @@ -2,7 +2,6 @@ namespace SwaggerProvider open System open System.Reflection -open System.Net.Http open ProviderImplementation.ProvidedTypes open Microsoft.FSharp.Core.CompilerServices open Swagger @@ -70,28 +69,8 @@ type public SwaggerTypeProvider(cfg : TypeProviderConfig) as this = let addCache() = lazy let schemaData = - match schemaPathRaw.StartsWith("http", true, null) with - | true -> - let headers = - headersStr.Split('|') - |> Seq.choose (fun x -> - let pair = x.Split('=') - 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 - request.Headers.TryAddWithoutValidation(name, value) |> ignore - // using a custom handler means that we can set the default credentials. - use handler = new HttpClientHandler(UseDefaultCredentials = true) - use client = new HttpClient(handler) - async { - let! response = client.SendAsync(request) |> Async.AwaitTask - return! response.Content.ReadAsStringAsync() |> Async.AwaitTask - } |> Async.RunSynchronously - | false -> - schemaPathRaw |> IO.File.ReadAllText + SwaggerProvider.Internal.SchemaReader.readSchemaPath headersStr schemaPathRaw + |> Async.RunSynchronously let schema = SwaggerParser.parseSchema schemaData let defCompiler = DefinitionCompiler(schema, preferNullable) diff --git a/src/SwaggerProvider.DesignTime/Utils.fs b/src/SwaggerProvider.DesignTime/Utils.fs index 0a4b79b3..696a4482 100644 --- a/src/SwaggerProvider.DesignTime/Utils.fs +++ b/src/SwaggerProvider.DesignTime/Utils.fs @@ -1,4 +1,45 @@ -namespace SwaggerProvider.Internal +namespace SwaggerProvider.Internal + +module SchemaReader = + open System + open System.Net.Http + + let readSchemaPath (headersStr:string) (schemaPathRaw:string) = + async { + match schemaPathRaw.StartsWith("http", true, null) with + | true -> + let headers = + headersStr.Split('|') + |> Seq.choose (fun x -> + let pair = x.Split('=') + 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 + request.Headers.TryAddWithoutValidation(name, value) |> ignore + // using a custom handler means that we can set the default credentials. + use handler = new HttpClientHandler(UseDefaultCredentials = true) + use client = new HttpClient(handler) + let! res = + async { + let! response = client.SendAsync(request) |> Async.AwaitTask + return! response.Content.ReadAsStringAsync() |> Async.AwaitTask + } |> Async.Catch + match res with + | Choice1Of2 x -> return x + | Choice2Of2 (:? System.Net.WebException as wex) -> + use stream = wex.Response.GetResponseStream() + use reader = new System.IO.StreamReader(stream) + let err = reader.ReadToEnd() + return + if String.IsNullOrEmpty err then raise wex + else err.ToString() + | Choice2Of2 e -> return failwith(e.ToString()) + | false -> + return schemaPathRaw |> IO.File.ReadAllText + } type UniqueNameGenerator() = let hash = System.Collections.Generic.HashSet<_>() diff --git a/src/SwaggerProvider.DesignTime/v2/Parser/Parsers.fs b/src/SwaggerProvider.DesignTime/v2/Parser/Parsers.fs index cd4db071..a2a947a8 100644 --- a/src/SwaggerProvider.DesignTime/v2/Parser/Parsers.fs +++ b/src/SwaggerProvider.DesignTime/v2/Parser/Parsers.fs @@ -412,7 +412,20 @@ module Parsers = | "options" -> Some <| parseOperationObject context path Options obj | "head" -> Some <| parseOperationObject context path Head obj | "patch" -> Some <| parseOperationObject context path Patch obj - | "$ref" -> failwith "External definition of this path item is not supported yet" + | "$ref" -> + let fileName = obj.AsString() + let path = + // If path is empty: + // We could match something like interactive __SOURCE_DIRECTORY__ + // or else (System.Reflection.Assembly.GetExecutingAssembly().Location |> System.IO.Path.GetDirectoryName) + obj.GetStringSafe("basePath") + let filePath = System.IO.Path.Combine [| path; (if fileName.Contains("#") then fileName.Split('#').[0] else fileName) |] + + let schemaData = + SwaggerProvider.Internal.SchemaReader.readSchemaPath "" filePath + |> Async.RunSynchronously + + failwith "External definition of this path item is not supported yet" | _ -> None let updateContext (pathItemObj:SchemaNode) = match pathItemObj.TryGetProperty("parameters") with diff --git a/tests/SwaggerProvider.Tests/v2/Schema.Spec.Yaml.Tests.fs b/tests/SwaggerProvider.Tests/v2/Schema.Spec.Yaml.Tests.fs index d4dac7be..d029afb9 100644 --- a/tests/SwaggerProvider.Tests/v2/Schema.Spec.Yaml.Tests.fs +++ b/tests/SwaggerProvider.Tests/v2/Schema.Spec.Yaml.Tests.fs @@ -821,4 +821,16 @@ GeneralError: } |] |> Map.ofArray) Expect.equal actual expected "Responses Definitions Object" + + + ptestCase "External reference test" <| fun _ -> // Ignore("Not supported") + """ +# What should be the relative path? e.g. $ref: ../v2/token.yaml#/token +/tokens: + $ref: ./tests/SwaggerProvider.Tests/v2/token.yaml#/token +""" + |> SwaggerParser.parseYaml + |> Parsers.parsePathsObject Parsers.ParserContext.Empty + |> fun actual -> + Expect.equal (actual.ToString()) "token" "External reference test" ] diff --git a/tests/SwaggerProvider.Tests/v2/token.yaml b/tests/SwaggerProvider.Tests/v2/token.yaml new file mode 100644 index 00000000..41e6147e --- /dev/null +++ b/tests/SwaggerProvider.Tests/v2/token.yaml @@ -0,0 +1,19 @@ + token: + post: + summary: Just a test + operationId: test_token + requestBody: + required: true + content: + application/json: + schema: + $ref: ../v2/token.yaml + responses: + "200": + description: Generated + content: + application/json: + schema: + $ref: ../v2/token.yaml + default: + $ref: ../error.yaml