Skip to content

Commit

Permalink
Add support for array-typed path parameters
Browse files Browse the repository at this point in the history
  • Loading branch information
aj-foster committed Jul 13, 2024
1 parent 2a0e9bd commit bbcaea8
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 9 deletions.
50 changes: 41 additions & 9 deletions lib/open_api/renderer/operation.ex
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ defmodule OpenAPI.Renderer.Operation do
]
"""
require Logger
alias OpenAPI.Processor.Operation
alias OpenAPI.Processor.Operation.Param
alias OpenAPI.Processor.Schema
Expand Down Expand Up @@ -249,7 +250,6 @@ defmodule OpenAPI.Renderer.Operation do
module_name: module_name,
request_body: request_body,
request_method: request_method,
request_path: request_path,
request_path_parameters: path_params,
request_query_parameters: query_params,
responses: responses
Expand Down Expand Up @@ -281,14 +281,9 @@ defmodule OpenAPI.Renderer.Operation do
end

url =
String.replace(request_path, ~r/\{([[:word:]]+)\}/, "#\{\\1\}")
|> then(&"\"#{&1}\"")
|> Code.string_to_quoted!()
|> then(fn url ->
quote do
{:url, unquote(url)}
end
end)
quote do
{:url, unquote(render_url(state, operation))}
end

method =
quote do
Expand Down Expand Up @@ -346,6 +341,43 @@ defmodule OpenAPI.Renderer.Operation do
end
end

defp render_url(_state, operation) do
%Operation{request_path: path, request_path_parameters: path_params} = operation
param_map = Map.new(path_params, fn %Param{name: name} = param -> {"{#{name}}", param} end)

String.split(path, "/")
|> Enum.map(fn
path_segment ->
if param = Map.get(param_map, path_segment) do
%Param{name: name} = param

case param do
%Param{style: :simple, value_type: primitive}
when primitive in [:boolean, :integer, :number] ->
quote do: "#{unquote(Util.identifier(name))}"

%Param{style: :simple, value_type: {:string, _}} ->
quote do: "#{unquote(Util.identifier(name))}"

%Param{style: :simple, value_type: {:array, primitive}}
when primitive in [:boolean, :integer, :number] ->
quote do: "#{Enum.join(unquote(Util.identifier(name)), ",")}"

%Param{style: :simple, value_type: {:array, {:string, _}}} ->
quote do: "#{Enum.join(unquote(Util.identifier(name)), ",")}"

_else ->
Logger.warning("Path param style not implemented for type: #{inspect(param)}")
quote do: "#{unquote(Util.identifier(name))}"
end
else
path_segment
end
end)
|> Enum.intersperse("/")
|> Util.collapse_binary()
end

@doc """
Render the spec of an operation function
Expand Down
32 changes: 32 additions & 0 deletions lib/open_api/renderer/util.ex
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,30 @@ defmodule OpenAPI.Renderer.Util do
|> Enum.reject(&is_nil/1)
end

@doc """
Collapse a list of binary expressions into a single binary
This utility is useful when several individual double-quoted strings are concatenated. Instead
of creating a large `<<...>>` expression, this function will produce an AST that is formatted
as a single double-quoted string (when possible).
"""
@spec collapse_binary(Macro.t()) :: Macro.t()
def collapse_binary(nodes) do
nodes =
nodes
|> Enum.map(fn
{:<<>>, _meta, args} ->
{:<<>>, _meta, args} = collapse_binary(args)
args

other_node ->
other_node
end)
|> List.flatten()

{:<<>>, [], nodes}
end

@doc """
Convert an AST into formatted code as a string
Expand Down Expand Up @@ -92,6 +116,14 @@ defmodule OpenAPI.Renderer.Util do
ast_node
end

@doc """
Unquote the given string as an identifier (rather than a string literal or atom)
"""
@spec identifier(String.t()) :: Macro.t()
def identifier(value) do
{String.to_atom(value), [], nil}
end

@doc """
Enforce the existence of whitespace after an expression
Expand Down

0 comments on commit bbcaea8

Please sign in to comment.