Skip to content

Commit

Permalink
Sync with upstream (#2)
Browse files Browse the repository at this point in the history
* Exclude empty paths from spec (open-api-spex#583)

* Exclude empty paths from spec

* fix: assert_operation_response header lookup (open-api-spex#584)

* fix: assert_operation_response header lookup

* Release version 3.18.1

* Fix 'AllOf cast returns a map, but I expected a struct' (open-api-spex#592)

* Add failing test

* Cast result of AllOf cast into a struct

* Shorter module name

* Add missing NoneCache test

* Release version 3.18.2

* Relax dependency constraint on ymlr to allow version ~> 5.0 (open-api-spex#586)

* relax dependency on ymlr, and fix some tests

* test with more elixir versions

* Update Elixir version test matrix (open-api-spex#602)

* Update Elixir version test matrix

* Fix map key order dependent test

* Release version 3.18.3

* Support response code ranges

See: https://swagger.io/docs/specification/describing-responses/

* Release version 3.19.0

* Add notice that body params are not merged into Conn.params whne using cast and validate plug (open-api-spex#589)

* Set nonces on <script> and <style> elements if configured (open-api-spex#593)

* Allow script and style nonces

* Allow nonces on the SwaggerUIOAuth2Redirect plug as well

---------

Co-authored-by: Alisina Bahadori <alisina.bm@gmail.com>
Co-authored-by: Matt Sutkowski <msutkowski@gmail.com>
Co-authored-by: Dimitris Zorbas <dimitrisplusplus@gmail.com>
Co-authored-by: Angelika Tyborska <angelikatyborska@fastmail.com>
Co-authored-by: Aleksandr Lossenko <aleksandr.lossenko@memsource.com>
Co-authored-by: Nathan Alderson <me@nathanalderson.com>
  • Loading branch information
7 people committed May 14, 2024
1 parent c133e7f commit a1de716
Show file tree
Hide file tree
Showing 21 changed files with 400 additions and 132 deletions.
21 changes: 18 additions & 3 deletions .github/workflows/elixir.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,27 @@ jobs:
name: Test (OTP ${{matrix.otp}} / Elixir ${{matrix.elixir}})
strategy:
matrix:
otp: ['22', '23', '24', '25']
elixir: ['1.11', '1.12', '1.13']
otp: ['22', '23', '24', '25', '26']
elixir: ['1.11', '1.12', '1.13', '1.14', '1.15', '1.16']
exclude:
- {otp: '25', elixir: '1.10'}
- {otp: '22', elixir: '1.14'}
- {otp: '22', elixir: '1.15'}
- {otp: '22', elixir: '1.16'}
- {otp: '23', elixir: '1.14'}
- {otp: '23', elixir: '1.15'}
- {otp: '23', elixir: '1.16'}
- {otp: '24', elixir: '1.11'}
- {otp: '24', elixir: '1.12'}
- {otp: '24', elixir: '1.13'}
- {otp: '24', elixir: '1.14'}
- {otp: '24', elixir: '1.15'}
- {otp: '25', elixir: '1.11'}
- {otp: '25', elixir: '1.12'}
- {otp: '25', elixir: '1.14'}
- {otp: '26', elixir: '1.11'}
- {otp: '26', elixir: '1.12'}
- {otp: '26', elixir: '1.13'}
- {otp: '26', elixir: '1.14'}
steps:
- uses: actions/checkout@v2
- uses: erlef/setup-beam@v1
Expand Down
20 changes: 20 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,26 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## v3.19.0 - 2024-04-30

* Support response code ranges by @zorbash in 8898859da1

## v3.18.3 - 2024-03-15

* Relax dependency constraint on ymlr to allow version ~> 5.0 by @egze in https://github.com/open-api-spex/open_api_spex/pull/586

## v3.18.2 - 2024-01-26

* Fix 'AllOf cast returns a map, but I expected a struct' by @angelikatyborska in https://github.com/open-api-spex/open_api_spex/pull/592

## v3.18.1 - 2023-12-19

* Fix `assert_operation_response/2` header lookup by @msutkowski in https://github.com/open-api-spex/open_api_spex/pull/584
* Exclude empty paths (`operation false`) from generated spec by @alisinabh in https://github.com/open-api-spex/open_api_spex/pull/583
* Cast discriminator when no title present (#574) by @albertored in https://github.com/open-api-spex/open_api_spex/pull/574
* Docstest Operation.parameter/5 by @zorbash
* Document the spec export task `--filename` option by @zorbash

## v3.18.0 - 2023-08-23

* Relax dependency constraint on ymlr to allow version ~> 4.0 by @arcanemachine in https://github.com/open-api-spex/open_api_spex/pull/544
Expand Down
9 changes: 7 additions & 2 deletions examples/phoenix_app/lib/phoenix_app_web/router.ex
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,12 @@ defmodule PhoenixAppWeb.Router do
oauth: [
# client_id: "e2195a7487322a0f19bf"
client_id: "Iv1.d7c611e5607d77b0"
]
],
csp_nonce_assign_key: %{script: :script_src_nonce, style: :style_src_nonce}
]

@oauth_redirect_config [
csp_nonce_assign_key: %{script: :script_src_nonce}
]

def swagger_ui_config, do: @swagger_ui_config
Expand All @@ -28,7 +33,7 @@ defmodule PhoenixAppWeb.Router do

get "/swaggerui", OpenApiSpex.Plug.SwaggerUI, @swagger_ui_config

get "/swaggerui/oauth2-redirect.html", OpenApiSpex.Plug.SwaggerUIOAuth2Redirect, :show
get "/swaggerui/oauth2-redirect.html", OpenApiSpex.Plug.SwaggerUIOAuth2Redirect, @oauth_redirect_config
end

scope "/api" do
Expand Down
8 changes: 4 additions & 4 deletions lib/open_api_spex/cast/all_of.ex
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,14 @@ defmodule OpenApiSpex.Cast.AllOf do
cast_all_of(%{ctx | schema: %{schema | allOf: [nested_schema | remaining]}}, result)
end

defp cast_all_of(%{schema: %{allOf: []}, errors: []} = ctx, acc) do
defp cast_all_of(%{schema: %{allOf: [], "x-struct": module}, errors: []} = ctx, acc)
when not is_nil(module) do
with :ok <- Utils.check_required_fields(ctx, acc) do
{:ok, acc}
{:ok, struct(module, acc)}
end
end

defp cast_all_of(%{schema: %{allOf: [], errors: [], "x-struct": module}} = ctx, acc)
when not is_nil(module) do
defp cast_all_of(%{schema: %{allOf: []}, errors: []} = ctx, acc) do
with :ok <- Utils.check_required_fields(ctx, acc) do
{:ok, acc}
end
Expand Down
16 changes: 13 additions & 3 deletions lib/open_api_spex/cast/utils.ex
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,19 @@ defmodule OpenApiSpex.Cast.Utils do
- If multiple `content-type` headers are found, the function will only return the value of the first one.
"""
@spec content_type_from_header(Plug.Conn.t()) :: String.t() | nil
def content_type_from_header(conn = %Plug.Conn{}) do
case Plug.Conn.get_req_header(conn, "content-type") do
@spec content_type_from_header(Plug.Conn.t(), :request | :response) ::
String.t() | nil
def content_type_from_header(conn = %Plug.Conn{}, header_location \\ :request) do
content_type =
case header_location do
:request ->
Plug.Conn.get_req_header(conn, "content-type")

:response ->
Plug.Conn.get_resp_header(conn, "content-type")
end

case content_type do
[header_value | _] ->
header_value
|> String.split(";")
Expand Down
5 changes: 5 additions & 0 deletions lib/open_api_spex/operation_builder.ex
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,11 @@ defmodule OpenApiSpex.OperationBuilder do
def build_responses(_), do: []

defp status_to_code(:default), do: :default

defp status_to_code(status)
when status in [:"1XX", "1XX", :"2XX", "2XX", :"3XX", "3XX", :"4XX", "4XX", :"5XX", "5XX"],
do: status

defp status_to_code(status), do: Status.code(status)

def build_request_body(%{body: {description, media_type, schema}}) do
Expand Down
23 changes: 12 additions & 11 deletions lib/open_api_spex/path_item.ex
Original file line number Diff line number Diff line change
Expand Up @@ -73,16 +73,17 @@ defmodule OpenApiSpex.PathItem do
defp from_valid_routes([]), do: nil

defp from_valid_routes(routes) do
attrs =
routes
|> Enum.map(fn route ->
case Operation.from_route(route) do
nil -> nil
op -> {route.verb, op}
end
end)
|> Enum.filter(& &1)

struct(PathItem, attrs)
routes
|> Enum.map(fn route ->
case Operation.from_route(route) do
nil -> nil
op -> {route.verb, op}
end
end)
|> Enum.filter(& &1)
|> case do
[] -> nil
attrs -> struct(PathItem, attrs)
end
end
end
2 changes: 2 additions & 0 deletions lib/open_api_spex/plug/cast_and_validate.ex
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
defmodule OpenApiSpex.Plug.CastAndValidate do
@moduledoc """
Module plug that will cast and validate the `Conn.params` and `Conn.body_params` according to the schemas defined for the operation.
Note that when using this plug, the body params are no longer merged into `Conn.params` and must be read from `Conn.body_params`
separately.
The operation_id can be given at compile time as an argument to `init`:
Expand Down
42 changes: 36 additions & 6 deletions lib/open_api_spex/plug/swagger_ui.ex
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,11 @@ defmodule OpenApiSpex.Plug.SwaggerUI do
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/4.14.0/swagger-ui.css" >
<link rel="icon" type="image/png" href="./favicon-32x32.png" sizes="32x32" />
<link rel="icon" type="image/png" href="./favicon-16x16.png" sizes="16x16" />
<style>
<%= if style_src_nonce do %>
<style nonce="<%= style_src_nonce %>">
<% else %>
<style>
<% end %>
html
{
box-sizing: border-box;
Expand All @@ -68,7 +72,11 @@ defmodule OpenApiSpex.Plug.SwaggerUI do
<script src="https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/4.14.0/swagger-ui-bundle.js" charset="UTF-8"> </script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/4.14.0/swagger-ui-standalone-preset.js" charset="UTF-8"> </script>
<script>
<%= if script_src_nonce do %>
<script nonce="<%= script_src_nonce %>">
<% else %>
<script>
<% end %>
window.onload = function() {
// Begin Swagger UI call region
const api_spec_url = new URL(window.location);
Expand All @@ -95,7 +103,7 @@ defmodule OpenApiSpex.Plug.SwaggerUI do
}
return request;
}
<%= for {k, v} <- Map.drop(config, [:path, :oauth]) do %>
<%= for {k, v} <- Map.drop(config, [:path, :oauth, :csp_nonce_assign_key]) do %>
, <%= camelize(k) %>: <%= encode_config(camelize(k), v) %>
<% end %>
})
Expand Down Expand Up @@ -135,14 +143,19 @@ defmodule OpenApiSpex.Plug.SwaggerUI do
* `:path` - Required. The URL path to the API definition.
* `:oauth` - Optional. Config to pass to the `SwaggerUIBundle.initOAuth()` function.
* `:csp_nonce_assign_key` - Optional. An assign key to find the CSP nonce value used
for assets. Supports either `atom()` or a map of type
`%{optional(:script) => atom(), optional(:style) => atom()}`. You will probably
want to set this on the `SwaggerUIOAuth2Redirect` plug as well.
* all other opts - forwarded to the `SwaggerUIBundle` constructor
## Example
get "/swaggerui", OpenApiSpex.Plug.SwaggerUI,
path: "/api/openapi",
default_model_expand_depth: 3,
display_operation_id: true
display_operation_id: true,
csp_nonce_assign_key: %{script: :script_src_nonce, style: :style_src_nonce}
"""
@impl Plug
def init(opts) when is_list(opts) do
Expand All @@ -153,7 +166,14 @@ defmodule OpenApiSpex.Plug.SwaggerUI do
def call(conn, config) do
csrf_token = Plug.CSRFProtection.get_csrf_token()
config = supplement_config(config, conn)
html = render(config, csrf_token)

html =
render(
config,
csrf_token,
get_nonce(conn, config, :style),
get_nonce(conn, config, :script)
)

conn
|> Plug.Conn.put_resp_content_type("text/html")
Expand All @@ -164,7 +184,9 @@ defmodule OpenApiSpex.Plug.SwaggerUI do

EEx.function_from_string(:defp, :render, @html, [
:config,
:csrf_token
:csrf_token,
:style_src_nonce,
:script_src_nonce
])

defp camelize(identifier) do
Expand Down Expand Up @@ -203,4 +225,12 @@ defmodule OpenApiSpex.Plug.SwaggerUI do
defp supplement_config(config, _conn) do
config
end

def get_nonce(conn, config, type) do
case config[:csp_nonce_assign_key] do
key when is_atom(key) -> conn.assigns[key]
%{^type => key} when is_atom(key) -> conn.assigns[key]
_ -> nil
end
end
end

0 comments on commit a1de716

Please sign in to comment.