Current section
Files
Jump to
Current section
Files
lib/open_api/generator.ex
defmodule OpenAPI.Generator do
@moduledoc false
alias OpenAPI.Config
alias OpenAPI.Generator.Operation
alias OpenAPI.Generator.Render
alias OpenAPI.Generator.Schema
alias OpenAPI.State
@spec run(State.t()) :: :ok
def run(state) do
state
|> collect_schema_files()
|> process_operations()
|> collect_operation_files()
|> reconcile_files()
|> write()
end
#
# Schemas
#
@spec collect_schema_files(State.t()) :: State.t()
defp collect_schema_files(%State{config: config} = state) do
%Config{base_location: base_location, schema_location: schema_location} = config
state = Schema.discover(state, state.spec.paths)
files =
state.schemas
|> Map.values()
|> Enum.reduce(%{}, fn schema, files ->
%Schema{final_name: final_name} = schema = Schema.process(state, schema)
filename =
Path.join([
base_location,
schema_location,
Macro.underscore(final_name) <> ".ex"
])
file = %{name: filename, operations: [], schemas: [schema]}
Map.update(files, final_name, file, fn existing_file ->
%{existing_file | schemas: [schema | existing_file.schemas]}
end)
end)
%State{state | schema_files: files}
end
#
# Operations
#
@methods [:get, :put, :post, :delete, :options, :head, :patch, :trace]
@spec process_operations(State.t()) :: State.t()
defp process_operations(state) do
for {path, item} <- state.spec.paths,
method <- @methods,
not is_nil(Map.get(item, method)),
reduce: state do
state ->
operation = Map.get(item, method)
Operation.process(state, path, method, operation)
end
end
@spec collect_operation_files(State.t()) :: State.t()
defp collect_operation_files(%State{operations: operations, config: config} = state) do
%Config{base_location: base_location, operation_location: operation_location} = config
operations =
operations
|> List.flatten()
|> Enum.reduce(%{}, fn operation, acc ->
filename =
Path.join([
base_location,
operation_location,
Macro.underscore(operation.module) <> ".ex"
])
file = %{name: filename, operations: [operation], schemas: []}
Map.update(acc, operation.module, file, fn existing_file ->
%{existing_file | operations: [operation | existing_file.operations]}
end)
end)
%State{state | operation_files: operations}
end
#
# Write
#
@spec reconcile_files(State.t()) :: State.t()
defp reconcile_files(state) do
files =
Map.merge(state.schema_files, state.operation_files, fn _module,
schema_file,
operation_file ->
%{
name: operation_file.name,
operations: operation_file.operations,
schemas: schema_file.schemas
}
end)
%{state | files: files}
end
@spec write(State.t()) :: :ok
defp write(%State{files: files, config: config}) do
%Config{
base_module: base_module,
base_location: base_location,
default_client: default_client,
types: types
} = config
File.mkdir_p!(base_location)
for {original_module, file} <- files do
%{name: filename} = file
module = Module.concat(base_module, original_module)
contents =
file
|> Map.merge(%{
default_client: default_client,
original_module: original_module,
module: module,
types: types
})
|> Render.render()
File.mkdir_p!(Path.dirname(filename))
File.write!(filename, [contents, "\n"])
end
:ok
end
end