Page MenuHomePhorge

No OneTemporary

Size
5 KB
Referenced Files
None
Subscribers
None
diff --git a/lib/open_api_spex/extendable.ex b/lib/open_api_spex/extendable.ex
new file mode 100644
index 0000000..663155e
--- /dev/null
+++ b/lib/open_api_spex/extendable.ex
@@ -0,0 +1,17 @@
+defprotocol OpenApiSpex.Extendable do
+ @fallback_to_any true
+ def to_map(struct)
+end
+
+defimpl OpenApiSpex.Extendable, for: Any do
+ def to_map(struct), do: Map.from_struct(struct)
+end
+
+defimpl OpenApiSpex.Extendable, for: [OpenApiSpex.Info] do
+ def to_map(struct = %{extensions: e}) do
+ struct
+ |> Map.from_struct()
+ |> Map.delete(:extensions)
+ |> Map.merge(e || %{})
+ end
+end
diff --git a/lib/open_api_spex/info.ex b/lib/open_api_spex/info.ex
index 47b46ce..0ee0c1b 100644
--- a/lib/open_api_spex/info.ex
+++ b/lib/open_api_spex/info.ex
@@ -1,30 +1,32 @@
defmodule OpenApiSpex.Info do
@moduledoc """
Defines the `OpenApiSpex.Info.t` type.
"""
alias OpenApiSpex.{Contact, License}
@enforce_keys [:title, :version]
defstruct [
:title,
:description,
:termsOfService,
:contact,
:license,
- :version
+ :version,
+ :extensions
]
@typedoc """
[Info Object](https://swagger.io/specification/#infoObject)
The object provides metadata about the API. The metadata MAY be used by the clients if needed,
and MAY be presented in editing or documentation generation tools for convenience.
"""
@type t :: %__MODULE__{
title: String.t,
description: String.t | nil,
termsOfService: String.t | nil,
contact: Contact.t | nil,
license: License.t | nil,
- version: String.t
+ version: String.t,
+ extensions: %{String.t() => String.t()} | nil,
}
end
diff --git a/lib/open_api_spex/open_api.ex b/lib/open_api_spex/open_api.ex
index 0e18636..114f139 100644
--- a/lib/open_api_spex/open_api.ex
+++ b/lib/open_api_spex/open_api.ex
@@ -1,99 +1,99 @@
defmodule OpenApiSpex.OpenApi do
@moduledoc """
Defines the `OpenApiSpex.OpenApi.t` type and the behaviour for application modules that
construct an `OpenApiSpex.OpenApi.t` at runtime.
"""
alias OpenApiSpex.{
- Info, Server, Paths, Components,
+ Extendable, Info, Server, Paths, Components,
SecurityRequirement, Tag, ExternalDocumentation,
OpenApi
}
@enforce_keys [:info, :paths]
defstruct [
openapi: "3.0.0",
info: nil,
servers: [],
paths: nil,
components: nil,
security: [],
tags: [],
externalDocs: nil
]
@typedoc """
[OpenAPI Object](https://swagger.io/specification/#oasObject)
This is the root document object of the OpenAPI document.
"""
@type t :: %OpenApi{
openapi: String.t,
info: Info.t,
servers: [Server.t] | nil,
paths: Paths.t,
components: Components.t | nil,
security: [SecurityRequirement.t] | nil,
tags: [Tag.t] | nil,
externalDocs: ExternalDocumentation.t | nil
}
@doc """
A spec/0 callback function is required for use with the `OpenApiSpex.Plug.PutApiSpec` plug.
## Example
@impl OpenApiSpex.OpenApi
def spec do
%OpenApi{
servers: [
# Populate the Server info from a phoenix endpoint
Server.from_endpoint(MyAppWeb.Endpoint, otp_app: :my_app)
],
info: %Info{
title: "My App",
version: "1.0"
},
# populate the paths from a phoenix router
paths: Paths.from_router(MyAppWeb.Router)
}
|> OpenApiSpex.resolve_schema_modules() # discover request/response schemas from path specs
end
"""
@callback spec() :: t
@json_encoder Enum.find([Jason, Poison], &Code.ensure_loaded?/1)
def json_encoder, do: @json_encoder
for encoder <- [Poison.Encoder, Jason.Encoder] do
if Code.ensure_loaded?(encoder) do
defimpl encoder do
def encode(api_spec = %OpenApi{}, options) do
api_spec
|> to_json()
|> unquote(encoder).encode(options)
end
defp to_json(%Regex{source: source}), do: source
defp to_json(value = %{__struct__: _}) do
value
- |> Map.from_struct()
+ |> Extendable.to_map()
|> to_json()
end
defp to_json(value) when is_map(value) do
value
|> Stream.map(fn {k,v} -> {to_string(k), to_json(v)} end)
|> Stream.filter(fn {_, nil} -> false; _ -> true end)
|> Enum.into(%{})
end
defp to_json(value) when is_list(value) do
Enum.map(value, &to_json/1)
end
defp to_json(nil), do: nil
defp to_json(true), do: true
defp to_json(false), do: false
defp to_json(value) when is_atom(value), do: to_string(value)
defp to_json(value), do: value
end
end
end
end
diff --git a/test/encode_test.exs b/test/encode_test.exs
new file mode 100644
index 0000000..5fcb252
--- /dev/null
+++ b/test/encode_test.exs
@@ -0,0 +1,36 @@
+defmodule OpenApiSpex.EncodeTest do
+ use ExUnit.Case
+
+ alias OpenApiSpex.{
+ Info,
+ OpenApi
+ }
+
+ test "Vendor extensions x-logo properly encoded" do
+ spec = %OpenApi{
+ info: %Info{
+ title: "Test",
+ version: "1.0.0",
+ extensions: %{
+ "x-logo" => %{
+ "url" => "https://example.com/logo.png",
+ "backgroundColor" => "#FFFFFF",
+ "altText" => "Example logo"
+ }
+ }
+ },
+ paths: %{}
+ }
+
+ decoded =
+ OpenApiSpex.resolve_schema_modules(spec)
+ |> Jason.encode!()
+ |> Jason.decode!()
+
+ assert decoded["info"]["x-logo"]["url"] == "https://example.com/logo.png"
+ assert decoded["info"]["x-logo"]["backgroundColor"] == "#FFFFFF"
+ assert decoded["info"]["x-logo"]["altText"] == "Example logo"
+
+ assert is_nil(decoded["info"]["extensions"])
+ end
+end

File Metadata

Mime Type
text/x-diff
Expires
Fri, Nov 29, 4:21 AM (1 d, 19 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
41141
Default Alt Text
(5 KB)

Event Timeline