Packages
dagger
0.9.1
0.21.7
0.21.6
0.21.5
0.21.4
0.21.3
0.21.2
0.21.1
0.21.0
0.20.8
0.20.7
0.20.6
0.20.5
0.20.4
0.20.3
0.20.2
0.20.1
0.20.0
0.19.11
0.19.10
0.19.9
0.19.8
0.19.7
0.19.6
0.19.5
0.19.4
0.19.3
0.19.2
0.19.1
0.19.0
0.18.19
0.18.18
0.18.17
0.18.16
0.18.15
0.18.14
0.18.13
0.18.12
0.18.11
0.18.10
0.18.9
0.18.8
0.18.7
0.18.5
0.18.4
0.18.3
0.18.2
0.18.1
0.18.0
0.17.2
0.17.1
0.17.0
0.16.3
0.16.2
0.16.1
0.16.0
0.15.4
0.15.3
0.15.2
0.15.1
0.15.0
0.14.0
0.13.7
0.13.6
0.13.5
0.13.4
0.13.3
0.13.2
0.13.1
0.13.0
0.12.7
0.12.6
0.12.5
0.12.4
0.12.3
0.12.2
0.12.1
0.12.0
0.11.9
0.11.8
0.11.7
0.11.6
0.11.5
0.11.4
0.11.3
0.11.2
0.11.1
0.11.0
0.10.3
0.10.2
0.10.1
0.10.0
0.9.11
0.9.10
0.9.9
0.9.8
0.9.7
0.9.6
0.9.5
0.9.4
0.9.3
0.9.2
0.9.1
0.9.0
0.8.8
0.8.7
0.8.6
0.8.5
0.8.4
0.8.2
0.0.0
retired
Dagger SDK for Elixir
Current section
Files
Jump to
Current section
Files
lib/dagger/codegen/elixir/templates/object_tmpl.ex
defmodule Dagger.Codegen.Elixir.Templates.Object do
@moduledoc false
alias Dagger.Codegen.Elixir.Function
alias Dagger.Codegen.Elixir.Module, as: Mod
alias Dagger.Codegen.Elixir.Type
@id_modules [
"CacheVolumeID",
"ContainerID",
"DirectoryID",
"FileID",
"ProjectCommandID",
"ProjectID",
"SecretID",
"SocketID"
]
def render(full_type, types) do
if is_simple_object?(full_type) do
render_data_module(full_type, types)
else
render_api_module(full_type, types)
end
end
defp is_simple_object?(%{"fields" => fields}) do
not has_id_fields?(fields) and empty_args?(fields)
end
defp has_id_fields?(fields) do
fields
|> Enum.any?(fn %{"name" => name} -> name == "id" end)
end
defp empty_args?(fields) do
fields
|> Enum.all?(fn %{"args" => args} -> args == [] end)
end
defp render_data_module(
%{
"name" => name,
"fields" => fields,
"description" => desc
},
_
)
when name not in @id_modules do
mod_name = Mod.from_name(name)
desc =
if desc == "" do
name
else
desc
end
struct_fields = Enum.map(fields, &to_struct_field/1)
data_type_t =
{:%, [],
[
{:__MODULE__, [], Elixir},
{:%{}, [],
fields
|> Enum.map(fn %{"type" => type} = field ->
type =
case type do
%{"kind" => "NON_NULL", "ofType" => type} -> Type.render_type(type)
type -> Type.render_type(type) |> Type.render_nullable_type()
end
{to_struct_field(field), type}
end)}
]}
quote do
defmodule unquote(mod_name) do
@moduledoc unquote(desc)
@type t() :: unquote(data_type_t)
@derive Nestru.Decoder
defstruct unquote(struct_fields)
end
end
end
defp to_struct_field(%{"name" => name}) do
name
|> Macro.underscore()
|> String.to_atom()
end
defp render_api_module(
%{
"name" => name,
"fields" => fields,
"description" => desc
},
types
) do
mod_name = Mod.from_name(name)
funs = Enum.map(fields, &render_function(&1, Function.format_var_name(name), types))
derive_sync =
if Enum.any?(fields, fn %{"name" => name} -> name == "sync" end) do
[
quote do
@derive Dagger.Sync
end
]
else
[]
end
desc =
if desc == "" do
name
else
desc
end
quote do
defmodule unquote(mod_name) do
@moduledoc unquote(desc)
use Dagger.QueryBuilder
@type t() :: %__MODULE__{}
unquote_splicing(derive_sync)
defstruct [:selection, :client]
unquote_splicing(funs)
end
end
end
defp render_function(
%{
"name" => name,
"args" => args,
"type" => type
} = field,
mod_var_name,
types
) do
mod_var_name = to_macro_var(mod_var_name)
fun_args = [module_fun_arg(mod_var_name) | fun_args(args)]
fun_body = format_function_body(name, {mod_var_name, args}, type, types)
Function.define(name, fun_args, nil, fun_body,
doc: format_doc(field),
deprecated: deprecated_reason(field),
spec: format_spec(field)
)
end
defp format_function_body(
field_name,
{mod_var_name, [%{"name" => "id"} = arg]},
type_ref,
_types
)
when field_name in ["file", "secret"] do
name =
case type_ref do
%{"kind" => "OBJECT", "name" => name} ->
name
%{"kind" => "NON_NULL", "ofType" => %{"kind" => "OBJECT", "name" => name}} ->
name
end
mod_name = Mod.from_name(name)
arg_name = arg |> fun_arg_name() |> Function.format_var_name()
quote do
selection = select(unquote(mod_var_name).selection, unquote(field_name))
selection = arg(selection, "id", unquote(to_macro_var(arg_name)))
%unquote(mod_name){
selection: selection,
client: unquote(mod_var_name).client
}
end
end
defp format_function_body(
field_name,
{mod_var_name, args},
%{"kind" => "NON_NULL", "ofType" => %{"kind" => "OBJECT", "name" => name}},
_types
)
when field_name != "moduleConfig" do
name = if(name == "Query", do: "Client", else: name)
mod_name = Mod.from_name(name)
args = render_args(args)
quote do
selection = select(unquote(mod_var_name).selection, unquote(field_name))
unquote_splicing(args)
%unquote(mod_name){
selection: selection,
client: unquote(mod_var_name).client
}
end
end
defp format_function_body(
field_name,
{mod_var_name, args},
%{
"kind" => "NON_NULL",
"ofType" => %{
"kind" => "LIST",
"ofType" => %{"ofType" => %{"kind" => "OBJECT", "name" => name}}
}
},
types
) do
args = render_args(args)
selection_fields =
types
|> Enum.find(fn %{"name" => typename} -> typename == name end)
|> then(fn %{"fields" => fields} -> get_in(fields, [Access.all(), "name"]) end)
|> Enum.join(" ")
return_module = Mod.from_name(name)
quote do
selection = select(unquote(mod_var_name).selection, unquote(field_name))
selection = select(selection, unquote(selection_fields))
unquote_splicing(args)
with {:ok, data} <- execute(selection, unquote(mod_var_name).client) do
Nestru.decode_from_list_of_maps(data, unquote(return_module))
end
end
end
defp format_function_body(field_name, {mod_var_name, args}, type_ref, _types) do
args = render_args(args)
execute_block =
case type_ref do
%{"kind" => "OBJECT", "name" => name} ->
return_module = Mod.from_name(name)
quote do
case execute(selection, unquote(mod_var_name).client) do
{:ok, nil} ->
{:ok, nil}
{:ok, data} ->
Nestru.decode_from_map(data, unquote(return_module))
error ->
error
end
end
_ ->
quote do
execute(selection, unquote(mod_var_name).client)
end
end
quote do
selection = select(unquote(mod_var_name).selection, unquote(field_name))
unquote_splicing(args)
unquote(execute_block)
end
end
defp deprecated_reason(%{"isDeprecated" => true, "deprecationReason" => reason}) do
reason = String.trim_trailing(reason, ".")
for [text, api] <- Regex.scan(~r/`(?<name>[a-zA-Z0-9]+)`/, reason),
reduce: reason do
reason -> String.replace(reason, text, "`#{Macro.underscore(api)}`")
end
end
defp deprecated_reason(_), do: nil
defp format_doc(%{"description" => desc, "args" => args}) do
sep = [~c"\n", ~c"\n"]
doc = [desc]
required_args_doc =
case format_required_args_doc(args) do
[] -> []
args_doc -> ["## Required Arguments", ~c"\n", ~c"\n", args_doc]
end
optional_args_doc =
case format_optional_args_doc(args) do
[] -> []
args_doc -> ["## Optional Arguments", ~c"\n", ~c"\n", args_doc]
end
(doc ++ sep ++ required_args_doc ++ sep ++ optional_args_doc)
|> IO.iodata_to_binary()
|> String.trim()
end
defp format_required_args_doc(args) do
args
|> Enum.filter(&required_arg?/1)
|> Enum.map(&format_arg_doc/1)
|> Enum.intersperse(~c"\n")
end
defp format_optional_args_doc(args) do
args
|> Enum.filter(&(not required_arg?(&1)))
|> Enum.map(&format_arg_doc/1)
|> Enum.intersperse(~c"\n")
end
defp required_arg?(arg) do
arg["type"]["kind"] == "NON_NULL"
end
defp format_arg_doc(%{"name" => name, "description" => description}) do
name = Function.format_var_name(name)
"* `#{name}` - #{description}"
end
defp render_args(args) do
required_args = render_required_args(args)
optional_args = render_optional_args(args)
required_args ++ optional_args
end
defp render_required_args(args) do
for arg <- args,
arg["type"]["kind"] == "NON_NULL" do
name = arg |> fun_arg_name() |> Function.format_var_name()
case arg do
%{"type" => %{"ofType" => %{"name" => type_name}}}
when type_name in @id_modules ->
mod = Mod.from_name(Mod.id_module_to_module(type_name))
quote do
{:ok, id} = unquote(mod).id(unquote(to_macro_var(name)))
selection = arg(selection, unquote(arg["name"]), id)
end
_ ->
quote do
selection = arg(selection, unquote(arg["name"]), unquote(to_macro_var(name)))
end
end
end
end
defp render_optional_args(args) do
for arg <- args, not required_arg?(arg) do
name = arg["name"]
arg_name = Function.format_var_name(name)
render =
case arg do
%{
"type" => %{
"kind" => "LIST",
"name" => nil,
"ofType" => %{
"kind" => "NON_NULL",
"name" => nil,
"ofType" => %{"kind" => "SCALAR", "name" => type_name, "ofType" => nil}
}
}
}
when type_name in @id_modules ->
mod = Mod.from_name(Mod.id_module_to_module(type_name))
quote do
ids =
optional_args[unquote(arg_name)]
|> Enum.map(fn value ->
{:ok, id} = unquote(mod).id(value)
id
end)
arg(selection, unquote(name), ids)
end
%{
"type" => %{"kind" => "SCALAR", "name" => type_name, "ofType" => nil}
}
when type_name in @id_modules ->
mod = Mod.from_name(Mod.id_module_to_module(type_name))
quote do
{:ok, id} = unquote(mod).id(optional_args[unquote(arg_name)])
arg(selection, unquote(name), id)
end
_ ->
quote do
arg(selection, unquote(name), optional_args[unquote(arg_name)])
end
end
quote do
selection =
if is_nil(optional_args[unquote(arg_name)]) do
selection
else
unquote(render)
end
end
end
end
defp format_spec(%{"name" => name, "type" => type} = field) do
return_type =
case type do
%{"kind" => "NON_NULL", "ofType" => %{"kind" => "OBJECT"} = type} ->
Type.render_type(type)
%{"kind" => "NON_NULL", "ofType" => type} ->
Type.render_type(type)
|> Type.render_result_type()
type ->
Type.render_type(type)
|> Type.render_nullable_type()
|> Type.render_result_type()
end
{[quote(do: t()) | render_arg_types(field["args"], name not in ["file", "secret"])],
return_type}
end
defp render_arg_types(args, strip_id?) do
{required_args, optional_args} =
args
|> Enum.split_with(&required_arg?/1)
required_arg_types =
for %{"type" => type} <- required_args do
case type do
%{"kind" => "NON_NULL", "ofType" => %{"kind" => "SCALAR"}} = type ->
if strip_id? do
update_in(type, ["ofType", "name"], fn name ->
String.trim_trailing(name, "ID")
end)
else
type
end
type ->
type
end
|> Type.render_type()
end
if optional_args != [] do
required_arg_types ++ [quote(do: keyword())]
else
required_arg_types
end
end
defp module_fun_arg(mod_var_name) do
quote do
%__MODULE__{} = unquote(mod_var_name)
end
end
defp fun_args([]), do: []
defp fun_args(args) do
{required_args, optional_args} =
args
|> Enum.split_with(&required_arg?/1)
required_fun_args(required_args) ++ optional_fun_args(optional_args)
end
defp required_fun_args(args) do
args
|> Enum.map(&Function.format_var_name(fun_arg_name(&1)))
|> Enum.map(&to_macro_var/1)
end
defp optional_fun_args([]), do: []
defp optional_fun_args(_args) do
[
quote do
unquote(to_macro_var(:optional_args)) \\ []
end
]
end
defp fun_arg_name(%{"name" => "id", "type" => %{"ofType" => %{"name" => id_mod}}})
when id_mod in @id_modules do
Function.id_module_to_var_name(id_mod)
end
defp fun_arg_name(%{"name" => name}) do
name
end
defp to_macro_var(var), do: Macro.var(var, __MODULE__)
end