From 9679b9b458596e1bc799d767fa8f60d626c372a0 Mon Sep 17 00:00:00 2001 From: Sean Abrahams Date: Sat, 14 Jan 2017 17:29:02 -0800 Subject: [PATCH 1/2] Support Elixir 1.4 date structs for Ecto adapter. --- lib/graphql/relay/connection/ecto.ex | 22 ++++++++-- mix.lock | 4 +- test/graphql/relay/connection/ecto_test.exs | 46 +++++++++++++++++++-- 3 files changed, 64 insertions(+), 8 deletions(-) diff --git a/lib/graphql/relay/connection/ecto.ex b/lib/graphql/relay/connection/ecto.ex index 384971e..a050549 100644 --- a/lib/graphql/relay/connection/ecto.ex +++ b/lib/graphql/relay/connection/ecto.ex @@ -181,9 +181,21 @@ if Code.ensure_loaded?(Ecto) do case Base.decode64(cursor) do {:ok, decoded_cursor} -> string = String.slice(decoded_cursor, String.length(@prefix)..String.length(decoded_cursor)) - case Ecto.DateTime.cast(string) do - {:ok, date} -> date - :error -> string + case DateTime.from_iso8601(string) do + {:ok, date, _utc_offset} -> DateTime.to_iso8601(date) + {:error, _} -> + case NaiveDateTime.from_iso8601(string) do + {:ok, date_time} -> NaiveDateTime.to_iso8601(date_time) + {:error, _} -> + case Date.from_iso8601(string) do + {:ok, date} -> Date.to_iso8601(date) + {:error, _} -> + case Time.from_iso8601(string) do + {:ok, time} -> Time.to_iso8601(time) + {:error, _} -> string + end + end + end end :error -> nil @@ -193,6 +205,10 @@ if Code.ensure_loaded?(Ecto) do def cursor_for_object_in_connection(object, property \\ :id) do prop = case Map.get(object, property) do %Ecto.DateTime{} = date_time -> Ecto.DateTime.to_iso8601(date_time) + %Date{} = date -> Date.to_iso8601(date) + %Time{} = time -> Time.to_iso8601(time) + %DateTime{} = date_time -> DateTime.to_iso8601(date_time) + %NaiveDateTime{} = date_time -> NaiveDateTime.to_iso8601(date_time) prop -> to_string(prop) end diff --git a/mix.lock b/mix.lock index 8b9191b..c3f46e1 100644 --- a/mix.lock +++ b/mix.lock @@ -1,7 +1,7 @@ %{"connection": {:hex, :connection, "1.0.3", "3145f7416be3df248a4935f24e3221dc467c1e3a158d62015b35bd54da365786", [:mix], []}, - "db_connection": {:hex, :db_connection, "1.0.0-rc.2", "c5b2329651ef046d85e47ec2c947cb0e3d10a69eb49fdb71e365e72d6758e4c5", [:mix], [{:sbroker, "~> 1.0.0-beta.2", [hex: :sbroker, optional: true]}, {:poolboy, "~> 1.5", [hex: :poolboy, optional: true]}, {:connection, "~> 1.0.2", [hex: :connection, optional: false]}]}, + "db_connection": {:hex, :db_connection, "1.0.0-rc.2", "c5b2329651ef046d85e47ec2c947cb0e3d10a69eb49fdb71e365e72d6758e4c5", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, optional: false]}, {:poolboy, "~> 1.5", [hex: :poolboy, optional: true]}, {:sbroker, "~> 1.0.0-beta.2", [hex: :sbroker, optional: true]}]}, "decimal": {:hex, :decimal, "1.1.2", "79a769d4657b2d537b51ef3c02d29ab7141d2b486b516c109642d453ee08e00c", [:mix], []}, - "ecto": {:hex, :ecto, "2.0.1", "cf97a4d353e14af3d3cc3b4452cfbd18b3aeee1fb4075475efeccec3853444a9", [:mix], [{:poison, "~> 1.5 or ~> 2.0", [hex: :poison, optional: true]}, {:sbroker, "~> 1.0-beta", [hex: :sbroker, optional: true]}, {:mariaex, "~> 0.7.7", [hex: :mariaex, optional: true]}, {:postgrex, "~> 0.11.2", [hex: :postgrex, optional: true]}, {:db_connection, "~> 1.0-rc.2", [hex: :db_connection, optional: true]}, {:decimal, "~> 1.0", [hex: :decimal, optional: false]}, {:poolboy, "~> 1.5", [hex: :poolboy, optional: false]}]}, + "ecto": {:hex, :ecto, "2.0.1", "cf97a4d353e14af3d3cc3b4452cfbd18b3aeee1fb4075475efeccec3853444a9", [:mix], [{:db_connection, "~> 1.0-rc.2", [hex: :db_connection, optional: true]}, {:decimal, "~> 1.0", [hex: :decimal, optional: false]}, {:mariaex, "~> 0.7.7", [hex: :mariaex, optional: true]}, {:poison, "~> 1.5 or ~> 2.0", [hex: :poison, optional: true]}, {:poolboy, "~> 1.5", [hex: :poolboy, optional: false]}, {:postgrex, "~> 0.11.2", [hex: :postgrex, optional: true]}, {:sbroker, "~> 1.0-beta", [hex: :sbroker, optional: true]}]}, "esqlite": {:hex, :esqlite, "0.2.2", "e4e8b39062a075e0bd4756b894dd2aa8edbf94c9081c1fb5c72dc705bf2f2d88", [:rebar, :make], []}, "graphql": {:hex, :graphql, "0.3.1", "d3bb5467877456cc2b33debc75407e9216567b10e35e83d5195e2d51e835e8c7", [:mix], []}, "pipe": {:hex, :pipe, "0.0.2", "eff98a868b426745acef103081581093ff5c1b88100f8ff5949b4a30e81d0d9f", [:mix], []}, diff --git a/test/graphql/relay/connection/ecto_test.exs b/test/graphql/relay/connection/ecto_test.exs index e27ed1c..2eca7da 100644 --- a/test/graphql/relay/connection/ecto_test.exs +++ b/test/graphql/relay/connection/ecto_test.exs @@ -14,7 +14,7 @@ defmodule GraphQL.Relay.Connection.EctoTest do field :number, :integer field :letter_id, :integer # belongs_to :letter, Letter - timestamps + timestamps(usec: true) end end @@ -26,7 +26,7 @@ defmodule GraphQL.Relay.Connection.EctoTest do field :second_column, :string field :order, :integer has_one :number, Number - timestamps + timestamps(usec: true) end end @@ -92,9 +92,17 @@ defmodule GraphQL.Relay.Connection.EctoTest do defp edge_for_object(obj), do: edge_for_object(obj, :id) defp edge_for_object(obj, ordered_by) do + cursor_suffix = case Map.get(obj, ordered_by) do + %Ecto.DateTime{} = date_time -> Ecto.DateTime.to_iso8601(date_time) + %Date{} = date -> Date.to_iso8601(date) + %Time{} = time -> Time.to_iso8601(time) + %DateTime{} = date_time -> DateTime.to_iso8601(date_time) + %NaiveDateTime{} = date_time -> NaiveDateTime.to_iso8601(date_time) + prop -> to_string(prop) + end %{ node: obj, - cursor: Base.encode64("ectoconnection:#{Map.get(obj, ordered_by)}") + cursor: Base.encode64("ectoconnection:#{cursor_suffix}") } end @@ -251,6 +259,38 @@ defmodule GraphQL.Relay.Connection.EctoTest do assert(Connection.Ecto.resolve(Repo, letters_query, %{first: 2, after: edge_for_object(e, :order).cursor, ordered_by: :order, ordered_by_direction: :desc}) == expected) end + test "pagination: respects first and after with datetime ordered_by property" do + expected = %{ + edges: [ + edge_for_object(c, :inserted_at), + edge_for_object(d, :inserted_at), + ], + pageInfo: %{ + startCursor: edge_for_object(c, :inserted_at).cursor, + endCursor: edge_for_object(d, :inserted_at).cursor, + hasPreviousPage: false, + hasNextPage: true, + } + } + assert(Connection.Ecto.resolve(Repo, letters_query, %{first: 2, after: edge_for_object(b, :inserted_at).cursor, ordered_by: :inserted_at}) == expected) + end + + test "pagination: respects first and after with datetime ordered_by and ordered_by_direction" do + expected = %{ + edges: [ + edge_for_object(d, :inserted_at), + edge_for_object(c, :inserted_at), + ], + pageInfo: %{ + startCursor: edge_for_object(d, :inserted_at).cursor, + endCursor: edge_for_object(c, :inserted_at).cursor, + hasPreviousPage: false, + hasNextPage: true, + } + } + assert(Connection.Ecto.resolve(Repo, letters_query, %{first: 2, after: edge_for_object(e, :inserted_at).cursor, ordered_by: :inserted_at, ordered_by_direction: :desc}) == expected) + end + test "respects first and after with long first" do expected = %{ edges: [ From 42b7cb1518a0491c38f549fd9d9851202bccca53 Mon Sep 17 00:00:00 2001 From: Sean Abrahams Date: Wed, 11 Jul 2018 08:54:59 -0700 Subject: [PATCH 2/2] Allow poison 3.0 --- mix.exs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mix.exs b/mix.exs index 9cb0396..85b9139 100644 --- a/mix.exs +++ b/mix.exs @@ -1,7 +1,7 @@ defmodule GraphQL.Relay.Mixfile do use Mix.Project - @version "0.5.0" + @version "0.5.1" @description "Relay helpers for GraphQL Elixir" @repo_url "https://github.com/graphql-elixir/graphql_relay" @@ -29,7 +29,7 @@ defmodule GraphQL.Relay.Mixfile do ] ] end - + defp applications(:test), do: applications(:prod) ++ [:ecto] defp applications(:dev), do: applications(:prod) ++ [:ecto] defp applications(_), do: [:logger] @@ -37,7 +37,7 @@ defmodule GraphQL.Relay.Mixfile do defp deps do [ {:graphql, "~> 0.3"}, - {:poison, "~> 1.5 or ~> 2.0"}, # For .generate_schema_json! + {:poison, "~> 1.5 or ~> 2.0 or ~> 3.0"}, # For .generate_schema_json! {:ecto, "~> 1.0 or ~> 2.0", optional: true, only: [:dev, :test]}, {:postgrex, ">= 0.0.0", only: [:dev, :test]}, ]