Current section
Files
Jump to
Current section
Files
lib/absinthe/custom_types.ex
defmodule FastWeb.Schema.CustomTypes do
use Absinthe.Schema.Notation
@moduledoc """
This module contains the following data types:
- input_error
- datetime
- date
- time
- uuid4
Further description of these types can be found in the source code.
To use: `import_types FastWeb.Schema.CustomTypes`
This is a modified version of: https://github.com/absinthe-graphql/absinthe/blob/be199215ad52050ed5a12b7c5af6cfaae329252c/lib/absinthe/type/custom.ex
"""
@desc "An error encountered trying to persist input"
object :input_error do
field :key, non_null(:string)
field :message, non_null(:string)
end
scalar :datetime, name: "DateTime" do
description """
The `DateTime` scalar type represents a date and time.
The DateTime appears in a JSON response as an ISO8601 formatted
string, including UTC timezone("Z").
"""
serialize &serialize_datetime/1
parse &parse_datetime/1
end
scalar :date do
description """
The `Date` scalar type represents a date. The Date appears in a JSON
response as an ISO8601 formatted string.
"""
serialize &Date.to_iso8601/1
parse &parse_date/1
end
scalar :time do
description """
The `Time` scalar type represents a time. The Time appears in a JSON
response as an ISO8601 formatted string.
"""
serialize &Time.to_iso8601/1
parse &parse_time/1
end
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_uuid/1
parse &decode_uuid/1
end
object :point do
description """
This turns a Geo.PostGIS.Geometry (Geo.Point) into a format we'll expect in JS land.
Geo.Point looks like this:
%Geo.Point{
coordinates: {-66.749961, 18.180555},
properties: %{},
srid: 4326
}
and we want it to look like this for JS:
{lat: float, lng: float}
"""
field :lat, non_null(:float) do
resolve fn geom, _, _ ->
%{coordinates: {_lng, lat}} = geom
{:ok, lat}
end
end
field :lng, non_null(:float) do
resolve fn geom, _, _ ->
%{coordinates: {lng, _lat}} = geom
{:ok, lng}
end
end
end
input_object :point_input do
field :lat, non_null(:float)
field :lng, non_null(:float)
end
object :phone_number do
description """
The `PhoneNumber` object type represents a phone number. The PhoneNumber
appears in a JSON response with fields `formatted` and `raw`.
"""
field :formatted, :string do
resolve fn phone_number, _, _ ->
{:ok, Fast.PhoneNumbers.format(phone_number)}
end
end
field :raw, :string do
resolve fn phone_number, _, _ ->
case phone_number do
nil ->
{:ok, nil}
"" ->
{:ok, nil}
phone_number when is_binary(phone_number) ->
{:ok, phone_number}
end
end
end
end
scalar :json, name: "Json" do
description """
The `Json` scalar type represents arbitrary json string data, represented as UTF-
character sequences. The Json type is most often used to represent a free-form
human-readable json string.
"""
serialize &encode_json/1
parse &decode_json/1
end
@spec parse_datetime(Absinthe.Blueprint.Input.String.t()) :: {:ok, DateTime.t()} | :error
@spec parse_datetime(Absinthe.Blueprint.Input.Null.t()) :: {:ok, nil}
defp parse_datetime(%Absinthe.Blueprint.Input.String{value: value}) do
case Timex.Parse.DateTime.Parser.parse(
value,
"{ISO:Extended}",
Timex.Parse.DateTime.Tokenizers.Default
) do
{:ok, datetime} -> {:ok, datetime}
_error -> :error
end
end
defp parse_datetime(%Absinthe.Blueprint.Input.Null{}) do
{:ok, nil}
end
defp parse_datetime(_) do
:error
end
defp serialize_datetime(%DateTime{} = datetime), do: DateTime.to_iso8601(datetime)
defp serialize_datetime(%NaiveDateTime{} = dt) do
dt
|> DateTime.from_naive!("Etc/UTC")
|> serialize_datetime
end
@spec parse_date(Absinthe.Blueprint.Input.String.t()) :: {:ok, Date.t()} | :error
@spec parse_date(Absinthe.Blueprint.Input.Null.t()) :: {:ok, nil}
defp parse_date(%Absinthe.Blueprint.Input.String{value: value}) do
case Date.from_iso8601(value) do
{:ok, date} -> {:ok, date}
_error -> :error
end
end
defp parse_date(%Absinthe.Blueprint.Input.Null{}) do
{:ok, nil}
end
defp parse_date(_) do
:error
end
@spec parse_time(Absinthe.Blueprint.Input.String.t()) :: {:ok, Time.t()} | :error
@spec parse_time(Absinthe.Blueprint.Input.Null.t()) :: {:ok, nil}
defp parse_time(%Absinthe.Blueprint.Input.String{value: value}) do
case Time.from_iso8601(value) do
{:ok, time} -> {:ok, time}
_error -> :error
end
end
defp parse_time(%Absinthe.Blueprint.Input.Null{}) do
{:ok, nil}
end
defp parse_time(_) do
:error
end
defp encode_uuid(value), do: value
@spec decode_uuid(Absinthe.Blueprint.Input.String.t()) :: {:ok, term()} | :error
@spec decode_uuid(Absinthe.Blueprint.Input.Null.t()) :: {:ok, nil}
defp decode_uuid(%Absinthe.Blueprint.Input.String{value: value}) do
Ecto.UUID.cast(value)
end
defp decode_uuid(%Absinthe.Blueprint.Input.Null{}) do
{:ok, nil}
end
defp decode_uuid(_) do
:error
end
@spec decode_json(Absinthe.Blueprint.Input.String.t()) :: {:ok, term()} | :error
@spec decode_json(Absinthe.Blueprint.Input.Null.t()) :: {:ok, nil}
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
defp encode_json(value) when is_map(value), do: Jason.encode!(value)
defp encode_json(value), do: value
end