-
Notifications
You must be signed in to change notification settings - Fork 530
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Date and Time Scalars #256
Date and Time Scalars #256
Conversation
@@ -59,6 +59,48 @@ defmodule Absinthe.Type.BuiltIns.Scalars do | |||
parse parse_with([Absinthe.Blueprint.Input.Boolean], &parse_boolean/1) | |||
end | |||
|
|||
scalar :utc_datetime, name: "DateTime" do |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Was there a specific reason to need to opt-in to timezone? I know for me, I prefer to default to being overly specific which would be using a timezone by default. This also goes against the assumptions of the Elixir conventions of DateTime is specific while NaiveDateTime is not specific. Is there any reason not to use :naive_datetime
as the identifier to keep that consistency?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was following the same conventions than Ecto (which I concede might be driven by legacy). I was also considering what an API consumer would see in introspection and I thought this choice would make the most sense. That said being more specific doesn't hurt :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah given that we're using the native Elixir date time handling I think we should go with :datetime
as always UTC.
I'm open to the idea of packaging additional, useful types (provided they only use the standard library), but I'm not keen on the idea of including types that go beyond the GraphQL Specification as built-ins -- both because of the possibility of future collision with "official" types and because we already have users with types that match these names but may have different accepted date formats, etc. I think the reasonable choices here come down to one of:
In either case, use of the types by schema designers should probably be opt-in via |
@bruce that make sense, I didn't consider the standard scalars from the official specs. They even mention ISO-8601 time string as an example of custom scalar. I like both ideas. They could be packaged in About the build failing: it looks like the container installs an older version of Elixir lacking |
I think we make an |
Update up for review. The build is still failing due to Elixir 1.3.0. |
lib/absinthe/type/extensions.ex
Outdated
@spec parse_datetime(any) :: {:ok, DateTime.t} | :error | ||
defp parse_datetime(value) when is_binary(value) do | ||
case DateTime.from_iso8601(value) do | ||
{:ok, datetime, _offset} -> {:ok, datetime} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have something similar in my code, but I also consider a non-zero offset to be an input error:
case DateTime.from_iso8601(string) do
{:ok, dt, 0} -> {:ok, dt}
{:ok, _, _} -> {:error, :unsupported_offset}
{:error, reason} -> {:error, reason}
end
That policy might be too strict for a general implementation, but I think it's slightly more honest about how offset information is treated by the server.
Can you update the .travis.yml to Elixir 1.4? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This PR could use some integration level tests. Build a small schema try to do some queries with each of these types as both inputs and return values.
lib/absinthe/type/extensions.ex
Outdated
""" | ||
|
||
serialize &DateTime.to_iso8601/1 | ||
parse parse_with([Absinthe.Blueprint.Input.DateTime], &parse_datetime/1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you're misunderstanding what parse_with
does. These are all gonna be Input.String
types because that's the Blueprint type that these datetime strings will initially come in as. They get parsed into datetimes sure, but prior to that they're just strings. The list you pass to parse_with
is a list of the basic input types that are acceptable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got it! I should have better reviewed when I moved the code from builtins to extensions :)
I'll update soon.
This is a very minor comment, but I have a small preference for |
@hellomika any update on this PR ? Thanks in advance. |
We are about to release a 1.3 beta, and it would be nice if we could include these in here. I may just merge them into the 1.3 branch and then make some edits tomorrow, we'll see. |
Hey all, apologies for the delay, time goes by fast :) I'm working on it right now. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added a few notes about documenting and testing the non-zero-offset behavior. Other than that, this looks great, and I'll be happy to remove my local implementations in favor of these. Thanks!
.travis.yml
Outdated
@@ -1,5 +1,5 @@ | |||
language: elixir | |||
elixir: 1.3.0 | |||
elixir: 1.4.1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
May as well go to Elixir 1.4.2 now.
lib/absinthe/type/custom.ex
Outdated
description """ | ||
The `DateTime` scalar type represents a date and time in the UTC | ||
timezone. The DateTime appears in a JSON response as an ISO8601 formatted | ||
string, including UTC timezone ("Z"). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might be helpful to include a brief sentence about the input format's expectations, too. Parsing an input string with a timezone offset other than 0 is considered an error.
assert {:ok, @datetime} == parse(:datetime, "2017-01-27 20:31:55Z") | ||
end | ||
|
||
it "cannot be parsed without UTC timezone marker" do |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please also include a test for parsing a non-0 offset (which should also be an error).
lib/absinthe/type/custom.ex
Outdated
end | ||
|
||
@spec parse_datetime(any) :: {:ok, DateTime.t} | :error | ||
defp parse_datetime(value) when is_binary(value) do |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These functions are not passed simply a string IE "2016-01-01T12:12:12". Rather they are passed an input blueprint struct. You'll want to pattern match that a string input has been given by doing:
defp parse_datetime(%Absinthe.Blueprint.Input.String{value: value}) do
When you do so, there isn't really any need to do the is_binary
check.
@@ -37,6 +38,11 @@ defmodule Absinthe.Type.CustomTest do | |||
assert {:ok, @datetime} == parse(:datetime, "2017-01-27 20:31:55Z") | |||
end | |||
|
|||
it "cannot be parsed when a UTC offset is included" do |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry to be pedantic, but I think should should be "non-zero UTC offset". I don't believe this string will not error: "2017-01-27T20:31:55+00:00"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, great feedback :)
Changes up for review. Let me know if the high level tests are adequate! |
Merged into the 1.3 branch. |
hey folks,
I will use the 1.3 branch as a mix dependency worst case... Thanks! |
@harmon25 you should try the beta.2, I believe it's part of that release. |
* Add logging, with support for filtering (#280) * Support log: false configuration * Address some feedback, clean up defaults (#282) * Middleware (#254) * minor optimization around arguments * basic functionality * almost green tests * green tests * handle middleware callbacks * move plugins over to the middleware namespace * forgot files as usual * significant progress * remove async and batch indirection * rename Absinthe.Resolution.Middleware => Absinthe.Middleware * handle before and after resolution * some middleware testing * improved resolution state names * plug is now middleware * finalize name for ensuring middleware * doc updates * it helps if you commit all the files * finalizing names * handle commentary * better doc header * Schema ensure query type (#226) * Add query type rule * Start to fix tests with querytype * Add test * fix more tests with query type * Remove double imports * Remove unused Type * Add Explanation to FieldImportExist * Fix the last two tests Rewinding my own mistakes * Fixed valid_schema * ensure anon function form works * context modification test * more docs * updated travis * last doc tweak * changelog update * version bump * correct version * typo fix * Date and Time Scalars (#256) * added utc_datetime as a built-in scalar * added naive datetime as a built-in scalar * added date as a built-in scalar * added time as a built-in scalar * removed datetime types from built_ins * moved datetime types to new extensions module * added stricter pattern matching to datetime parser * elixir 1.4 for travis * fixed parsing and renamed module * bumped elixir version * bumped elixir version for travis * testing against UTC offsets * testing parsing 0-utc offset * parsers are passed a blueprint input struct instead of a string * added high-level tests for custom types * build fix * Use Markdown list syntax for nicer rendering (#285) * changelog entry * doc fix * Support for `to_string/1` compatible error tuples in resolution (#286) * Use to_string for error messages Sometimes you get other types in {:error, value}, like atom and integers, and instead of people handling the converation themselves it's better just returning the string equivalent of the atom inline with what json libraries like Poison do or any other type that can be a string. An example would be if you are doing `with` in you body you get the error message from the library you are using. In guardian case you could get: `{:error, :token_expired}` * Changes for bruce * Add changelog entry * Moving TODOs to PR * better changelog for beta * fix bug where root query and mutation types had nil identifiers * version bump * provide test helping primer function * docs * Tuning (#292) * do expansion prior so that we don't have to do it in resolution * faster field applies check * get rid of infinite loops and improved type condition handling * add renamed file, and code reorganization * so close to green * tests are green at last * remove bug line * Fix #244 * Interface subtype (#293) * Add failing test for interface subtype (#288) This shows that an implementor of an interface with a more specific field than expected will not compile. * test fixes * fixes to enable absinthe relay * test prime bug fix * more assertive * resurrect plugin module as the before/after callbacks from middleware * middleware.t => middleware.spec * move scalar and enum serialization to the result phase (#296) * remove commented out debug statements * Add Absinthe.Pipeline.replace/3 (#300) * Add Pipeline.replace/3 * Better type spec for Type.Directive * Make Pipeline.replace/3 more flexible * Clean up some warnings * Prepare 1.3.0-beta.2 * Fix doc typo * Another doc fix * laxer test time for travis * Batch resolution phase (#302) * basic work on batching resolution phase * bug fix * remove warnings * Changed HandleAuth to HandleError (#297) * minor tweaks to support absinthe plug batching * Extensions (#306) * make the whole pipeline work with blueprints * extensions test * bug fixes * version bump * if a validation phase cannot jump, it returns an error instead of an ok tuple from the phase * remove strange indentation * fix performance issue, need to fix some tests though (#307) * fix performance issue, need to fix some tests though * green tests
I added 4 built-in scalars:
utc_datetime
parses to aDateTime
struct and serializes to an ISO8601 date and time + UTC timezone marker stringdatetime
parses to aNativeDateTime
struct and serializes to an ISO8601 date and time stringdate
parses to aDate
struct and serializes to an ISO8601 date stringtime
parses to aTime
struct and serializes to an ISO8601 time string