ash_json_api_wrapper/lib/open_api/resource_generator.ex

206 lines
4.8 KiB
Elixir

defmodule AshJsonApiWrapper.OpenApi.ResourceGenerator do
def generate(json, main_config) do
main_config[:resources]
|> Enum.map(fn {resource, config} ->
endpoints =
json
|> operations(config)
|> Enum.map_join("\n\n", fn {path, _method, operation} ->
entity_path =
if config[:entity_path] do
"entity_path \"#{config[:entity_path]}\""
end
"""
endpoint :#{operation_id(operation)} do
path "#{path}"
#{entity_path}
end
"""
end)
actions =
json
|> operations(config)
|> Enum.map_join("\n\n", fn
{_path, "get", config} ->
"""
read :#{operation_id(config)}
"""
{_path, "post", config} ->
"""
create :#{operation_id(config)}
"""
end)
fields =
config[:fields]
|> Enum.map_join("\n\n", fn {name, field_config} ->
filter_handler =
if field_config[:filter_handler] do
"filter_handler #{inspect(field_config[:filter_handler])}"
end
"""
field #{inspect(name)} do
#{filter_handler}
end
"""
end)
|> case do
"" ->
""
other ->
"""
fields do
#{other}
end
"""
end
{:ok, [object]} =
json
|> ExJSONPath.eval(config[:object_type])
attributes =
object
|> Map.get("properties")
|> Enum.map(fn {name, config} ->
{Macro.underscore(name), config}
end)
|> Enum.sort_by(fn {name, _} ->
name not in List.wrap(config[:primary_key])
end)
|> Enum.map_join("\n\n", fn {name, property} ->
type =
case property do
%{"enum" => _values} ->
":atom"
%{"format" => "date-time"} ->
":utc_datetime"
%{"type" => "string"} ->
":string"
%{"type" => "integer"} ->
":integer"
%{"type" => "boolean"} ->
":boolean"
other ->
raise "Unsupported property: #{inspect(other)}"
end
constraints =
case property do
%{"enum" => values} ->
"one_of: #{inspect(Enum.map(values, &String.to_atom/1))}"
%{"maxLength" => max, "minLength" => min, "type" => "string"} ->
"min_length: #{min}, max_length: #{max}"
%{"maxLength" => max, "type" => "string"} ->
"max_length: #{max}"
%{"minLength" => min, "type" => "string"} ->
"min_length: #{min}"
_ ->
nil
end
primary_key? = name in List.wrap(config[:primary_key])
if constraints || primary_key? do
constraints =
if constraints do
"constraints #{constraints}"
end
primary_key =
if primary_key? do
"""
primary_key? true
allow_nil? false
"""
end
"""
attribute :#{name}, #{type} do
#{primary_key}
#{constraints}
end
"""
else
"""
attribute :#{name}, #{type}
"""
end
end)
tesla =
if main_config[:tesla] do
"tesla #{main_config[:tesla]}"
end
endpoint =
if main_config[:endpoint] do
"base \"#{main_config[:endpoint]}\""
end
code =
"""
defmodule #{resource} do
use Ash.Resource, data_layer: AshJsonApiWrapper.DataLayer
json_api_wrapper do
#{tesla}
endpoints do
#{endpoint}
#{endpoints}
end
#{fields}
end
actions do
#{actions}
end
attributes do
#{attributes}
end
end
"""
|> Code.format_string!()
|> IO.iodata_to_binary()
{resource, code}
end)
end
defp operation_id(%{"operationId" => operationId}) do
operationId
|> Macro.underscore()
end
defp operations(json, config) do
json["paths"]
|> Enum.filter(fn {path, _value} ->
String.starts_with?(path, config[:path])
end)
|> Enum.flat_map(fn {path, methods} ->
Enum.map(methods, fn {method, config} ->
{path, method, config}
end)
end)
|> Enum.filter(fn {_path, method, _config} ->
method in ["get", "post"]
end)
end
end