Packages
stripity_stripe
3.1.1
3.3.2
3.3.1
3.2.0
3.1.1
3.1.0
3.0.0
2.17.3
2.17.2
2.17.1
2.17.0
2.16.0
2.15.1
2.15.0
2.14.1
2.14.0
2.13.0
2.12.1
2.12.0
2.11.0
2.10.0
2.9.0
2.8.0
2.7.2
2.7.1
2.7.0
2.6.0
2.5.0
2.4.0
2.3.0
2.2.3
2.2.2
2.2.1
2.2.0
2.1.0
2.0.1
2.0.0
2.0.0-alpha.11
2.0.0-alpha.10
2.0.0-alpha.9
2.0.0-alpha.8
2.0.0-alpha.7
2.0.0-alpha.6
2.0.0-alpha.5
2.0.0-alpha.4
2.0.0-alpha.3
2.0.0-alpha.2
2.0.0-alpha.1
1.6.2
1.6.1
1.6.0
1.4.0
1.3.0
1.2.0
1.1.0
0.5.0
0.4.0
0.3.0
0.2.0
A Stripe client for Elixir.
Current section
Files
Jump to
Current section
Files
lib/openapi/phases/compile.ex
defmodule Stripe.OpenApi.Phases.Compile do
@moduledoc false
def run(blueprint, _options) do
modules = Enum.map(blueprint.components, fn {_k, component} -> component.module end)
for {_name, component} <- blueprint.components do
funcs_types =
for operation <- component.operations,
operation_definition =
lookup_operation(
{operation["path"], String.to_atom(operation["operation"])},
blueprint.operations
),
operation_definition != nil do
arguments =
operation_definition.path_parameters
|> Enum.map(&String.to_atom(&1.name))
params? =
match?({:object, _, [_ | _]}, operation_definition.query_parameters) ||
match?({:object, _, [_ | _]}, operation_definition.body_parameters)
argument_names =
arguments
|> Enum.map(fn
name ->
Macro.var(name, __MODULE__)
end)
argument_values =
arguments
|> Enum.reject(&(&1 == :params))
|> Enum.map(fn name ->
Macro.var(name, __MODULE__)
end)
argument_specs =
arguments
|> Enum.map(fn
:params ->
quote do
params :: map()
end
name ->
quote do
unquote(Macro.var(name, __MODULE__)) :: binary()
end
end)
function_name = String.to_atom(operation["method_name"])
success_response_spec = return_spec(operation_definition.success_response)
params =
cond do
operation_definition.query_parameters != {:object, [], []} ->
operation_definition.query_parameters
operation_definition.body_parameters != {:object, [], []} ->
operation_definition.body_parameters
true ->
[]
end
{param_specs, object_types} = unnest_object_types(params)
object_types = MapSet.to_list(object_types)
function_code =
if params? do
quote do
@spec unquote(function_name)(
unquote_splicing(argument_specs),
params :: unquote(to_inline_spec(param_specs)),
opts :: Keyword.t()
) ::
{:ok, unquote(success_response_spec)}
| {:error, Stripe.ApiErrors.t()}
| {:error, term()}
def unquote(function_name)(
unquote_splicing(argument_names),
params \\ %{},
opts \\ []
) do
path =
Stripe.OpenApi.Path.replace_path_params(
unquote(operation_definition.path),
unquote(operation_definition.path_parameters),
unquote(argument_names)
)
Stripe.Request.new_request(opts)
|> Stripe.Request.put_endpoint(path)
|> Stripe.Request.put_params(params)
|> Stripe.Request.put_method(unquote(operation_definition.method))
|> Stripe.Request.make_request()
end
end
else
if operation_definition.path == "/v1/files" and operation_definition.method == :post do
quote do
@spec unquote(function_name)(
unquote_splicing(argument_specs),
opts :: Keyword.t()
) ::
{:ok, unquote(success_response_spec)}
| {:error, Stripe.ApiErrors.t()}
| {:error, term()}
def unquote(function_name)(
params \\ %{},
opts \\ []
) do
Stripe.Request.new_request(opts)
|> Stripe.Request.put_endpoint(unquote(operation_definition.path))
|> Stripe.Request.put_params(params)
|> Stripe.Request.put_method(unquote(operation_definition.method))
|> Stripe.Request.make_file_upload_request()
end
end
else
quote do
@spec unquote(function_name)(
unquote_splicing(argument_specs),
opts :: Keyword.t()
) ::
{:ok, unquote(success_response_spec)}
| {:error, Stripe.ApiErrors.t()}
| {:error, term()}
def unquote(function_name)(
unquote_splicing(argument_names),
opts \\ []
) do
path =
Stripe.OpenApi.Path.replace_path_params(
unquote(operation_definition.path),
unquote(operation_definition.path_parameters),
unquote(argument_values)
)
Stripe.Request.new_request(opts)
|> Stripe.Request.put_endpoint(path)
|> Stripe.Request.put_method(unquote(operation_definition.method))
|> Stripe.Request.make_request()
end
end
end
end
deprecated =
if operation_definition.deprecated do
quote do
@deprecated "Stripe has deprecated this operation"
end
else
quote do
end
end
ast =
quote do
unquote(deprecated)
@doc unquote(operation_definition.description)
unquote(function_code)
end
{ast, object_types}
end
{funcs, types} = Enum.unzip(funcs_types)
fields = component.properties |> Map.keys() |> Enum.map(&String.to_atom/1)
# TODO fix uniq
types =
List.flatten(types)
|> Enum.uniq_by(fn {_, meta, _} -> meta[:name] end)
|> Enum.sort()
|> Enum.map(&to_type_spec/1)
specs =
Enum.map(component.properties, fn {key, value} ->
{String.to_atom(key), build_spec(value, modules)}
end)
|> Enum.sort()
typedoc_fields =
component.properties
|> Enum.sort()
|> Enum.map_join("\n", fn {key, value} -> typedoc(key, value) end)
typedoc = """
The `#{component.name}` type.
#{typedoc_fields}
"""
type_doc =
if fields != nil do
quote do
defstruct unquote(fields)
@typedoc unquote(typedoc)
@type t :: %__MODULE__{
unquote_splicing(specs)
}
end
else
quote do
end
end
body =
quote do
use Stripe.Entity
@moduledoc unquote(component.description)
unquote(type_doc)
unquote_splicing(types)
unquote_splicing(funcs)
end
# Module.create(component.module, body, Macro.Env.location(__ENV__))
code =
quote do
defmodule unquote(component.module) do
unquote(body)
end
end
bin =
code
|> Macro.to_string()
|> Code.format_string!()
[_ | names] = Module.split(component.module)
filename = names |> Enum.map_join("__", &Macro.underscore/1)
File.write!("lib/generated/#{filename}.ex", bin)
end
{:ok, blueprint}
end
defp unnest_object_types(params) do
Macro.postwalk(params, MapSet.new(), fn
{:object, meta, children}, acc ->
if meta[:name] == nil || children == [] do
{{:object, meta, children}, acc}
else
{{:ref, [name: meta[:name]], []}, MapSet.put(acc, {:object, meta, children})}
end
other, acc ->
{other, acc}
end)
end
defp to_type_spec({:object, meta, children}) do
specs = Enum.map(children, &to_spec_map/1)
name = type_spec_name(meta[:name])
quote do
@typedoc unquote(meta[:description])
@type unquote(Macro.var(name, __MODULE__)) :: %{
unquote_splicing(specs)
}
end
end
defp to_type_spec({:array, meta, [child]} = _ast) do
name = type_spec_name(meta[:name])
quote do
@typedoc unquote(meta[:description])
@type unquote(Macro.var(name, __MODULE__)) :: unquote(to_type(child))
end
end
defp to_type_spec({_, meta, children}) do
specs = Enum.map(children, &to_spec_map/1)
name = type_spec_name(meta[:name])
quote do
@typedoc unquote(meta[:description])
@type unquote(Macro.var(name, __MODULE__)) :: %{
unquote_splicing(specs)
}
end
end
defp type_spec_name(name) do
if name in [:reference] do
:reference_0
else
name
end
end
defp to_inline_spec({_, _meta, children}) do
specs = Enum.map(children, &to_spec_map/1)
quote do
%{
unquote_splicing(specs)
}
end
end
defp to_spec_map({:array, meta, [_type]} = ast) do
{to_name(meta), to_type(ast)}
end
defp to_spec_map({:any_of, meta, [type | tail]}) do
{to_name(meta),
quote do
unquote(to_type(type)) | unquote(to_type(tail))
end}
end
defp to_spec_map({:ref, meta, _} = ast) do
{to_name(meta), to_type(ast)}
end
defp to_spec_map({_type, meta, _children} = ast) do
{to_name(meta), to_type(ast)}
end
defp to_name(meta) do
if meta[:required] do
meta[:name]
else
quote do
optional(unquote(meta[:name]))
end
end
end
def to_type([type]) do
quote do
unquote(to_type(type))
end
end
def to_type([type | tail]) do
quote do
unquote(to_type(type)) | unquote(to_type(tail))
end
end
def to_type({:ref, meta, _}) do
Macro.var(meta[:name], __MODULE__)
end
def to_type({:array, _meta, [type]}) do
quote do
list(unquote(to_type(type)))
end
end
def to_type({:any_of, _, [type | tail]}) do
quote do
unquote(to_type(type)) | unquote(to_type(tail))
end
end
def to_type({:string, metadata, _}) do
if metadata[:enum] do
to_type(metadata[:enum])
else
to_type(:string)
end
end
def to_type({:object, metadata, _}) do
if metadata[:name] == :metadata do
quote do
%{optional(binary) => binary}
end
else
quote do
map()
end
end
end
def to_type({type, _, _}) do
to_type(type)
end
def to_type(type) when type in [:boolean, :number, :integer, :float] do
quote do
unquote(Macro.var(type, __MODULE__))
end
end
def to_type(:string) do
quote do
binary
end
end
def to_type(type) do
type
end
defp return_spec(%OpenApiGen.Blueprint.Reference{name: name}) do
module = module_from_ref(name)
quote do
unquote(module).t()
end
end
defp return_spec(%OpenApiGen.Blueprint.ListOf{type_of: type}) do
quote do
Stripe.List.t(unquote(return_spec(type)))
end
end
defp return_spec(%OpenApiGen.Blueprint.SearchResult{type_of: type}) do
quote do
Stripe.SearchResult.t(unquote(return_spec(type)))
end
end
defp return_spec(%{any_of: [type]} = _type) do
return_spec(type)
end
defp return_spec(%OpenApiGen.Blueprint.AnyOf{any_of: [any_of | tail]} = type) do
type = Map.put(type, :any_of, tail)
{:|, [], [return_spec(any_of), return_spec(type)]}
end
defp return_spec(_) do
[]
end
defp build_spec(%{"nullable" => true} = type, modules) do
type = Map.delete(type, "nullable")
{:|, [], [build_spec(type, modules), nil]}
end
defp build_spec(%{"anyOf" => [type]} = _type, modules) do
build_spec(type, modules)
end
defp build_spec(%{"anyOf" => [any_of | tail]} = type, modules) do
type = Map.put(type, "anyOf", tail)
{:|, [], [build_spec(any_of, modules), build_spec(type, modules)]}
end
defp build_spec(%{"type" => "string"}, _) do
quote do
binary
end
end
defp build_spec(%{"type" => "boolean"}, _) do
quote do
boolean
end
end
defp build_spec(%{"type" => "integer"}, _) do
quote do
integer
end
end
defp build_spec(%{"$ref" => ref}, modules) do
module = module_from_ref(ref)
if module in modules do
quote do
unquote(module).t()
end
else
quote do
term
end
end
end
defp build_spec(_, _) do
quote do
term
end
end
defp module_from_ref(ref) do
module =
ref |> String.split("/") |> List.last() |> String.split(".") |> Enum.map(&Macro.camelize/1)
Module.concat(["Stripe" | module])
end
defp typedoc(field, props) do
" * `#{field}` #{props["description"]}"
end
defp lookup_operation(path, operations) do
Map.get(operations, path)
end
end