defmodule GlificWeb.Schema.GenericTypes do
@moduledoc """
GraphQL Representation of common data representations used across different
Glific's DataType
"""
use Absinthe.Schema.Notation
@desc "An error encountered trying to persist input"
object :input_error do
field :key, non_null(:string)
field :message, non_null(:string)
end
@desc "Lets collapse sort order, limit and offset into its own little groups"
input_object :opts do
field :order, type: :sort_order, default_value: :asc
field :order_with, :string
field :limit, :integer
field :offset, :integer, default_value: 0
end
@desc """
A generic status results for calls that don't return a value.
Typically this is for delete operations
"""
object :generic_result do
field :status, non_null(:api_status_enum)
field :errors, list_of(:input_error)
end
@desc """
A generic date range filter where from and to both are inclusive
"""
input_object :date_range_input do
@desc "Start date for the filter"
field :from, :datetime
@desc "End date for the filter"
field :to, :datetime
@desc """
column name where we apply the date range. Default is inserted_at.
"""
field :column, :string
end
scalar :gid do
description("""
The `gid` scalar appears in JSON as a String. The string appears to
the glific backend as an integer
""")
parse(&parse_maybe_integer/1)
serialize(&Integer.to_string/1)
end
@doc """
A forgivable parser which allows integers or strings to represent integers
"""
@spec parse_maybe_integer(Absinthe.Blueprint.Input.String.t()) :: {:ok, integer} | :error
def parse_maybe_integer(%Absinthe.Blueprint.Input.String{value: value}) when is_binary(value) do
Glific.parse_maybe_integer(value)
end
def parse_maybe_integer(_), do: :error
scalar :json, name: "Json" do
description("""
A generic json type so return the results as json object
""")
serialize(&Poison.encode!/1)
parse(&decode_json/1)
end
@spec decode_json(Absinthe.Blueprint.Input.String.t()) :: {:ok, term()} | :error
defp decode_json(%Absinthe.Blueprint.Input.String{value: value}) do
case Jason.decode(value) do
{:ok, result} -> {:ok, result}
_ -> :error
end
end
defp decode_json(%Absinthe.Blueprint.Input.Null{}) do
{:ok, nil}
end
defp decode_json(_) do
:error
end
# Enable Ecto UUID scalar for graphql
scalar :uuid4, name: "UUID4" do
description("""
The `UUID4` scalar type represents UUID4 compliant string data, represented as UTF-8
character sequences. The UUID4 type is most often used to represent unique
human-readable ID strings.
""")
serialize(&encode_uuid4/1)
parse(&decode_uuid4/1)
end
@spec decode_uuid4(Absinthe.Blueprint.Input.String.t()) :: {:ok, term()} | :error
@spec decode_uuid4(Absinthe.Blueprint.Input.Null.t()) :: {:ok, nil}
defp decode_uuid4(%Absinthe.Blueprint.Input.String{value: value}) do
Ecto.UUID.cast(value)
end
defp decode_uuid4(%Absinthe.Blueprint.Input.Null{}) do
{:ok, nil}
end
defp decode_uuid4(_) do
:error
end
defp encode_uuid4(value), do: value
# We will move this logic somewhere else in the future because it's not generic
scalar :role_label, name: "RoleLabel" do
description("""
Convert a string/atom to label (camel case)
""")
serialize(&encode_label/1)
parse(&parse_label/1)
end
@spec parse_label(Absinthe.Blueprint.Input.String.t()) :: {:ok, String.t()} | :error
@spec parse_label(Absinthe.Blueprint.Input.Null.t()) :: {:ok, nil}
defp parse_label(%Absinthe.Blueprint.Input.String{value: "No access"}) do
{:ok, :none}
end
defp parse_label(%Absinthe.Blueprint.Input.String{value: label}) do
if is_binary(label) do
label =
String.downcase(label)
|> Glific.safe_string_to_atom()
{:ok, label}
else
{:ok, label}
end
end
defp parse_label(%Absinthe.Blueprint.Input.Null{}) do
{:ok, nil}
end
defp parse_label(_) do
:error
end
defp encode_label(:none) do
"No access"
end
defp encode_label(label) when is_atom(label) do
label
|> Atom.to_string()
|> String.capitalize()
end
defp encode_label(label) when is_binary(label) do
label
|> String.capitalize()
end
defp encode_label(label), do: label
end