Current section

Files

Jump to
ex_shopify_schema lib mix ex_shopify_schema.ex
Raw

lib/mix/ex_shopify_schema.ex

defmodule Mix.ExShopifySchema do
@moduledoc """
Conveniences for writing ExShopifySchema related Mix tasks.
"""
import Mix.Generator
alias ExShopifySchema.Graphql.Introspection.Definition
@typep opts() :: [
dir: Path.t(),
namespace: module(),
ecto_types: %{(type_name :: String.t()) => {:field | :embeds, module(), Keyword.t()}}
]
@spec generate_scalar(Definition.ScalarType.t(), opts()) :: module()
def generate_scalar(%Definition.ScalarType{} = type, opts) do
namespace = Keyword.fetch!(opts, :namespace)
dir = Keyword.fetch!(opts, :dir)
mod = Module.concat(namespace, type.name)
assigns = [
mod: mod,
description: type.description
]
content = eval_template(type, assigns)
true = create_file(file_path(type, dir), content, force: true)
mod
end
@spec generate_object_like(
Definition.ObjectType.t()
| Definition.InterfaceType.t()
| Definition.InputObjectType.t(),
opts()
) :: module()
def generate_object_like(type, opts)
when is_struct(type, Definition.ObjectType)
when is_struct(type, Definition.InterfaceType)
when is_struct(type, Definition.InputObjectType) do
namespace = Keyword.fetch!(opts, :namespace)
dir = Keyword.fetch!(opts, :dir)
mod = Module.concat(namespace, type.name)
fields =
Enum.flat_map(type.fields, fn field ->
List.wrap(build_object_field(field, opts))
end)
assigns = [
mod: mod,
description: type.description,
fields: fields
]
content = eval_template(type, assigns)
true = create_file(file_path(type, dir), content, force: true)
mod
end
defp build_object_field(field, opts)
when is_struct(field, Definition.Field)
when is_struct(field, Definition.InputValue) do
{cardinality, type} = unwrap_object_field_type(field.type)
ecto_types = Keyword.fetch!(opts, :ecto_types)
case {cardinality, Map.fetch(ecto_types, type.name)} do
{:one, {:ok, {:field, type, opts}}} ->
{:loadable_field, field_name(field.name), type, opts}
{:many, {:ok, {:field, type, opts}}} ->
{:loadable_field, field_name(field.name), {:array, type}, opts}
{:one, {:ok, {:embeds, type, opts}}} ->
{:embeds_one, field_name(field.name), type, opts}
{:many, {:ok, {:embeds, type, opts}}} ->
{:embeds_many, field_name(field.name), type, opts}
{_cardinality, :error} ->
nil
end
end
defp unwrap_object_field_type(type, is_list? \\ false)
defp unwrap_object_field_type(%Definition.NonNullType{} = type, is_list?),
do: unwrap_object_field_type(type.type, is_list?)
defp unwrap_object_field_type(%Definition.ListType{} = type, _is_list?),
do: unwrap_object_field_type(type.type, true)
defp unwrap_object_field_type(%Definition.NamedType{} = type, is_list?),
do: if(is_list?, do: {:many, type}, else: {:one, type})
defp field_name(name) when is_binary(name) do
name |> Macro.underscore() |> String.to_atom()
end
defp file_path(%{name: name} = type, dir) when is_struct(type) do
type_name = Macro.underscore(name)
Path.join(dir, "#{type_name}.ex")
end
defp eval_template(type, assigns) do
type |> template_file_path() |> EEx.eval_file(assigns: assigns)
end
defp template_file_path(%Definition.ScalarType{} = type) do
type_name = Macro.underscore(type.name)
Path.join([schema_template_dir(), "scalars", "#{type_name}.eex"])
end
defp template_file_path(type)
when is_struct(type, Definition.ObjectType)
when is_struct(type, Definition.InterfaceType)
when is_struct(type, Definition.InputObjectType) do
Path.join(schema_template_dir(), "object_like.eex")
end
defp schema_template_dir do
Path.join(
:code.priv_dir(:ex_shopify_schema),
"graphql/templates/schemas/"
)
end
end