From d1b9fa185a620e86365dfa760d06609baaeddcd4 Mon Sep 17 00:00:00 2001 From: Alberto Sartori Date: Mon, 24 Jan 2022 06:36:04 +0100 Subject: [PATCH] decode/1 function correctly populates the :extensions field of structs (#416) --- lib/open_api_spex/open_api/decode.ex | 17 +++++++++++++++++ test/open_api/decode_test.exs | 23 ++++++++++++++++++----- test/support/encoded_schema.json | 14 +++++++++++--- 3 files changed, 46 insertions(+), 8 deletions(-) diff --git a/lib/open_api_spex/open_api/decode.ex b/lib/open_api_spex/open_api/decode.ex index e3976650..343b665c 100644 --- a/lib/open_api_spex/open_api/decode.ex +++ b/lib/open_api_spex/open_api/decode.ex @@ -39,6 +39,7 @@ defmodule OpenApiSpex.OpenApi.Decode do |> prop_to_struct(:components, Components) |> prop_to_struct(:tags, Tag) |> prop_to_struct(:externalDocs, ExternalDocumentation) + |> add_extensions(map) end defp struct_from_map(struct, map) when is_atom(struct) do @@ -142,6 +143,7 @@ defmodule OpenApiSpex.OpenApi.Decode do Tag |> struct_from_map(map) |> prop_to_struct(:externalDocs, ExternalDocumentation) + |> add_extensions(map) end defp to_struct(list, Tag) when is_list(list) do @@ -177,6 +179,7 @@ defmodule OpenApiSpex.OpenApi.Decode do SecurityScheme |> struct_from_map(map) |> prop_to_struct(:flows, OAuthFlows) + |> add_extensions(map) end defp to_struct(map, SecuritySchemes), do: embedded_ref_or_struct(map, SecurityScheme) @@ -280,6 +283,7 @@ defmodule OpenApiSpex.OpenApi.Decode do |> prop_to_struct(:requestBody, RequestBody) |> prop_to_struct(:callbacks, Callbacks) |> prop_to_struct(:servers, Server) + |> add_extensions(map) end defp to_struct(%{"$ref" => _} = map, RequestBody), do: struct_from_map(Reference, map) @@ -301,6 +305,7 @@ defmodule OpenApiSpex.OpenApi.Decode do |> prop_to_struct(:examples, Examples) |> prop_to_struct(:content, Content) |> prop_to_struct(:schema, Schema) + |> add_extensions(map) end defp to_struct(map_or_list, Parameters), do: embedded_ref_or_struct(map_or_list, Parameter) @@ -332,6 +337,7 @@ defmodule OpenApiSpex.OpenApi.Decode do |> prop_to_struct(:headers, Headers) |> prop_to_struct(:content, Content) |> prop_to_struct(:links, Links) + |> add_extensions(map) end defp to_struct(map, Responses), do: embedded_ref_or_struct(map, Response) @@ -386,6 +392,7 @@ defmodule OpenApiSpex.OpenApi.Decode do |> prop_to_struct(:trace, Operation) |> prop_to_struct(:parameters, Parameters) |> prop_to_struct(:servers, Servers) + |> add_extensions(map) end defp to_struct(map, PathItems) do @@ -400,6 +407,7 @@ defmodule OpenApiSpex.OpenApi.Decode do |> struct_from_map(map) |> prop_to_struct(:contact, Contact) |> prop_to_struct(:license, License) + |> add_extensions(map) end defp to_struct(list, mod) when is_list(list) and is_atom(mod), @@ -429,4 +437,13 @@ defmodule OpenApiSpex.OpenApi.Decode do end defp manage_additional_properties(map), do: map + + defp add_extensions(struct, map) do + extensions = + map + |> Enum.filter(fn {key, _val} -> String.starts_with?(key, "x-") end) + |> Map.new() + + Map.put(struct, :extensions, if(map_size(extensions) == 0, do: nil, else: extensions)) + end end diff --git a/test/open_api/decode_test.exs b/test/open_api/decode_test.exs index e2917cab..fffdec50 100644 --- a/test/open_api/decode_test.exs +++ b/test/open_api/decode_test.exs @@ -33,14 +33,17 @@ defmodule OpenApiSpex.OpenApi.DecodeTest do version: _version, description: _description, contact: contact, - license: license + license: license, + extensions: info_extensions } = info assert %OpenApiSpex.Contact{} = contact assert %OpenApiSpex.License{} = license - assert nil == extensions + assert info_extensions == %{"x-extension" => "foo"} + + assert %{"x-extension" => %{"value" => "haha"}} == extensions assert %OpenApiSpex.ExternalDocumentation{ description: _, @@ -154,7 +157,8 @@ defmodule OpenApiSpex.OpenApi.DecodeTest do schema: %OpenApiSpex.Schema{ example: "gzip", type: :string - } + }, + extensions: %{"x-extension" => "foo"} } == components_parameters_parameter assert %{ @@ -228,7 +232,7 @@ defmodule OpenApiSpex.OpenApi.DecodeTest do } == link assert %{ - "api_key" => _api_key_security_scheme, + "api_key" => api_key_security_scheme, "petstore_auth" => petstore_auth_security_scheme } = securitySchemes @@ -236,6 +240,10 @@ defmodule OpenApiSpex.OpenApi.DecodeTest do flows: oauth_flows } = petstore_auth_security_scheme + assert %OpenApiSpex.SecurityScheme{ + extensions: %{"x-extension" => "foo"} + } = api_key_security_scheme + assert %OpenApiSpex.OAuthFlows{ implicit: oauth_flow } = oauth_flows @@ -278,6 +286,7 @@ defmodule OpenApiSpex.OpenApi.DecodeTest do assert [tag] = tags assert %OpenApiSpex.Tag{ + extensions: %{"x-extension" => "foo"}, description: "Pets operations", externalDocs: %OpenApiSpex.ExternalDocumentation{ description: "Find more info here", @@ -296,6 +305,7 @@ defmodule OpenApiSpex.OpenApi.DecodeTest do "/example" => %OpenApiSpex.PathItem{ summary: "/example summary", description: "/example description", + extensions: %{"x-extension" => "foo"}, servers: [%OpenApiSpex.Server{}], parameters: [ %OpenApiSpex.Reference{ @@ -303,6 +313,7 @@ defmodule OpenApiSpex.OpenApi.DecodeTest do } ], post: %OpenApiSpex.Operation{ + extensions: %{"x-extension" => "foo"}, parameters: [ %OpenApiSpex.Reference{}, %OpenApiSpex.Reference{}, @@ -346,7 +357,9 @@ defmodule OpenApiSpex.OpenApi.DecodeTest do ] == operationSecurity assert %{ - "200" => %OpenApiSpex.Response{} + "200" => %OpenApiSpex.Response{ + extensions: %{"x-extension" => "foo"} + } } = operationResponses assert %{ diff --git a/test/support/encoded_schema.json b/test/support/encoded_schema.json index beb30503..dab242e5 100644 --- a/test/support/encoded_schema.json +++ b/test/support/encoded_schema.json @@ -7,7 +7,8 @@ "name": "test" }, "title": "Duffel Technology Ltd.", - "version": "1.0.0" + "version": "1.0.0", + "x-extension": "foo" }, "x-extension": { "value": "haha" @@ -43,7 +44,8 @@ "schema": { "example": "gzip", "type": "string" - } + }, + "x-extension": "foo" } }, "headers": { @@ -83,6 +85,7 @@ }, "securitySchemes": { "api_key": { + "x-extension": "foo", "type": "apiKey", "name": "api_key", "in": "header" @@ -319,9 +322,11 @@ }, "paths": { "/example": { + "x-extension": "foo", "summary": "/example summary", "description": "/example description", "post": { + "x-extension": "foo", "operationId": "example-post-test", "callbacks": { "operationCallback": { @@ -445,6 +450,7 @@ }, "responses": { "200": { + "x-extension": "foo", "content": { "application/json": { "example": { @@ -483,6 +489,7 @@ }, "responses": { "200": { + "x-extension": "foo", "content": { "application/json": { "example": { @@ -543,7 +550,8 @@ "externalDocs": { "description": "Find more info here", "url": "https://example.com" - } + }, + "x-extension": "foo" } ], "externalDocs": {