diff --git a/lib/wabanex_web/schema/types/custom/date_range.ex b/lib/wabanex_web/schema/types/custom/date_range.ex new file mode 100644 index 0000000..d57e1ab --- /dev/null +++ b/lib/wabanex_web/schema/types/custom/date_range.ex @@ -0,0 +1,46 @@ +defmodule WabanexWeb.Schema.Types.Custom.DateRange do + use Absinthe.Schema.Notation + + alias Absinthe.Blueprint.Input + + scalar :date_range, name: "DateRange" do + description(""" + The `DateRange` scalar represents a range/period of date. The `DateRange` + appears as a string of two ISO8601 formatted dates separated by comma. + """) + + serialize &serialize_range/1 + parse &parse_range/1 + end + + @spec serialize_range(PgRanges.DateRange.t()) :: String.t() + @spec serialize_range(list(String.t())) :: String.t() + defp serialize_range(%PgRanges.DateRange{lower: start_range, upper: end_range}), + do: serialize_range([start_range, end_range]) + + defp serialize_range([start_range, nil]), do: "#{Date.to_iso8601(start_range)}," + + defp serialize_range([start_range, end_range]), + do: "#{Date.to_iso8601(start_range)},#{Date.to_iso8601(end_range)}" + + @spec parse_range(Input.String.t()) :: {:ok, PgRanges.DateRange.t()} | :error + @spec parse_range(Input.Null.t()) :: {:ok, nil} + defp parse_range(%Input.String{value: value}) do + value + |> String.split(",") + |> Enum.map(&Date.from_iso8601/1) + |> case do + [{:error, _}] -> :error + [{:error, _}, _] -> :error + [_, {:error, _}] -> :error + result -> {:ok, result |> Enum.map(fn {:ok, value} -> value end) |> new()} + end + end + + defp parse_range(%Input.Null{}), do: {:ok, nil} + defp parse_range(_), do: :error + + defp new([start_date]), do: new([start_date, nil]) + defp new([start_date, end_date]), do: PgRanges.DateRange.new(start_date, end_date) + defp new([]), do: new([nil, nil]) +end diff --git a/test/wabanex_web/schema/types/custom/date_range_test.exs b/test/wabanex_web/schema/types/custom/date_range_test.exs new file mode 100644 index 0000000..2a1cb99 --- /dev/null +++ b/test/wabanex_web/schema/types/custom/date_range_test.exs @@ -0,0 +1,48 @@ +defmodule WabanexWeb.Schema.Types.Custom.DateRangeTest do + use ExUnit.Case, async: true + + alias Absinthe.{Blueprint.Input, Type} + alias PgRanges.DateRange + alias WabanexWeb.Schema.Types + + defmodule TestSchema do + use Absinthe.Schema + + import_types Types.Custom.DateRange + + query do + end + end + + defp serialize(type, value) do + TestSchema.__absinthe_type__(type) + |> Type.Scalar.serialize(value) + end + + defp parse(type, value) do + TestSchema.__absinthe_type__(type) + |> Type.Scalar.parse(value) + end + + describe ":date_range" do + test "serialize a list of dates as a list of ISO8601 date strings" do + assert "1978-12-15,1980-02-13" == serialize(:date_range, [~D[1978-12-15], ~D[1980-02-13]]) + assert "1978-12-15," == serialize(:date_range, [~D[1978-12-15], nil]) + end + + test "serialize a postgres range as a list of ISO8601 date strings" do + assert "1978-12-15,1980-02-13" == + serialize(:date_range, DateRange.new(~D[1978-12-15], ~D[1980-02-13])) + + assert "1978-12-15," == serialize(:date_range, DateRange.new(~D[1978-12-15], nil)) + end + + test "can be parsed from a string of ISO8601" do + assert {:ok, DateRange.new(~D[1978-12-15], ~D[1980-02-13])} == + parse(:date_range, %Input.String{value: "1978-12-15,1980-02-13"}) + + assert {:ok, DateRange.new(~D[1978-12-15], nil)} == + parse(:date_range, %Input.String{value: "1978-12-15"}) + end + end +end