mirego / absinthe_error_payload Goto Github PK
View Code? Open in Web Editor NEWBridges the gap between Ecto and Absinthe for mutation payload
Home Page: https://open.mirego.com
License: Other
Bridges the gap between Ecto and Absinthe for mutation payload
Home Page: https://open.mirego.com
License: Other
It there a way to translate error messages with Gettext? I guess relevant function is AbsintheErrorPayload.ChangesetParser.interpolate_message
Hi guys,
I ran into an issue with elixirs Range
not being handled properly.
This snippet from my changeset
|> validate_inclusion(:value, 6..20)
produces this error message when the condition is not met (shortened as it just goes on stating a ton of types)
** (Protocol.UndefinedError) protocol String.Chars not implemented for 6..20 of type Range (a struct). This protocol is implemented for the following type(s): Postgrex.Copy, [...], DateTime
I played around a bit and it seems like changing the interpolated_value_to_string/1
function to use inspect/1
rather than to_string
fixes the issue. I'm not sure what the exact difference in behaviour is and if this might break people's code, but if you guys are interested I'm happy to open a PR for this.
I'm on 1.1.2 with elixir 1.9.4 and OTP 22 if that makes any difference
Add t:AbsintheErrorPayload.ValidationMessage.t/0
for typespec referencing
Just a reminder that absinthe 1.5.0
will be released "soonish" and this fine package should be compatible with it. Thanks.
I have an AbsintheErrorPayload
struct which has been generated by the build_payload/2
middleware. Anyway, when having keyword list as the options
the resolution breaks with the following error:
Request: POST /api/graphiql
** (exit) an exception was raised:
** (BadMapError) expected a map, got: {:constraint, :unique}
(elixir) lib/map.ex:437: Map.get({:constraint, :unique}, :key, nil)
(absinthe) lib/absinthe/middleware/map_get.ex:9: Absinthe.Middleware.MapGet.call/2
(absinthe) lib/absinthe/phase/document/execution/resolution.ex:209: Absinthe.Phase.Document.Execution.Resolution.reduce_resolution/1
(absinthe) lib/absinthe/phase/document/execution/resolution.ex:168: Absinthe.Phase.Document.Execution.Resolution.do_resolve_field/4
(absinthe) lib/absinthe/phase/document/execution/resolution.ex:153: Absinthe.Phase.Document.Execution.Resolution.do_resolve_fields/6
(absinthe) lib/absinthe/phase/document/execution/resolution.ex:72: Absinthe.Phase.Document.Execution.Resolution.walk_result/5
(absinthe) lib/absinthe/phase/document/execution/resolution.ex:98: Absinthe.Phase.Document.Execution.Resolution.walk_results/6
(absinthe) lib/absinthe/phase/document/execution/resolution.ex:87: Absinthe.Phase.Document.Execution.Resolution.walk_result/5
(absinthe) lib/absinthe/phase/document/execution/resolution.ex:257: Absinthe.Phase.Document.Execution.Resolution.build_result/4
(absinthe) lib/absinthe/phase/document/execution/resolution.ex:153: Absinthe.Phase.Document.Execution.Resolution.do_resolve_fields/6
(absinthe) lib/absinthe/phase/document/execution/resolution.ex:72: Absinthe.Phase.Document.Execution.Resolution.walk_result/5
(absinthe) lib/absinthe/phase/document/execution/resolution.ex:98: Absinthe.Phase.Document.Execution.Resolution.walk_results/6
(absinthe) lib/absinthe/phase/document/execution/resolution.ex:87: Absinthe.Phase.Document.Execution.Resolution.walk_result/5
(absinthe) lib/absinthe/phase/document/execution/resolution.ex:257: Absinthe.Phase.Document.Execution.Resolution.build_result/4
(absinthe) lib/absinthe/phase/document/execution/resolution.ex:153: Absinthe.Phase.Document.Execution.Resolution.do_resolve_fields/6
(absinthe) lib/absinthe/phase/document/execution/resolution.ex:72: Absinthe.Phase.Document.Execution.Resolution.walk_result/5
(absinthe) lib/absinthe/phase/document/execution/resolution.ex:257: Absinthe.Phase.Document.Execution.Resolution.build_result/4
(absinthe) lib/absinthe/phase/document/execution/resolution.ex:153: Absinthe.Phase.Document.Execution.Resolution.do_resolve_fields/6
(absinthe) lib/absinthe/phase/document/execution/resolution.ex:72: Absinthe.Phase.Document.Execution.Resolution.walk_result/5
(absinthe) lib/absinthe/phase/document/execution/resolution.ex:53: Absinthe.Phase.Document.Execution.Resolution.perform_resolution/3
Here is the struct I used:
%AbsintheErrorPayload.Payload{
messages: [],
result: [
%AbsintheErrorPayload.ValidationMessage{
code: :unique,
field: "credential.email",
key: :email,
message: "has already been taken",
options: [constraint: :unique, constraint_name: "credentials_email_index"],
template: "has already been taken"
}
],
successful: true
}
Edit: It only breaks if we query for the options
in the GraphQL query.
I am getting the below error from AbsintheErrorPayload
Request: POST /v2/graphql
** (exit) an exception was raised:
** (Protocol.UndefinedError) protocol String.Chars not implemented for {:array, :map}. This protocol is implemented for: Postgrex.Copy, Postgrex.Query, Decimal, Float, DateTime, Time, List, Version.Requirement, Atom, Integer, Version, Date, BitString, NaiveDateTime, URI
(elixir) /home/build/elixir/lib/elixir/lib/string/chars.ex:3: String.Chars.impl_for!/1
(elixir) /home/build/elixir/lib/elixir/lib/string/chars.ex:22: String.Chars.to_string/1
(absinthe_error_payload) lib/absinthe_error_payload/changeset_parser.ex:114: anonymous fn/2 in AbsintheErrorPayload.ChangesetParser.interpolate_message/1
(elixir) lib/enum.ex:1940: Enum."-reduce/3-lists^foldl/2-0-"/3
(absinthe_error_payload) lib/absinthe_error_payload/changeset_parser.ex:93: AbsintheErrorPayload.ChangesetParser.construct_message/2
(ecto) lib/ecto/changeset.ex:2717: anonymous fn/4 in Ecto.Changeset.merge_error_keys/3
(elixir) lib/enum.ex:1940: Enum."-reduce/3-lists^foldl/2-0-"/3
(ecto) lib/ecto/changeset.ex:2704: Ecto.Changeset.traverse_errors/2
(ecto) lib/ecto/changeset.ex:2731: anonymous fn/3 in Ecto.Changeset.merge_related_keys/4
(elixir) lib/enum.ex:1431: Enum."-map_reduce/3-lists^mapfoldl/2-0-"/3
(ecto) lib/ecto/changeset.ex:2730: anonymous fn/4 in Ecto.Changeset.merge_related_keys/4
(stdlib) maps.erl:257: :maps.fold_1/3
(ecto) lib/ecto/changeset.ex:2731: anonymous fn/3 in Ecto.Changeset.merge_related_keys/4
(elixir) lib/enum.ex:1431: Enum."-map_reduce/3-lists^mapfoldl/2-0-"/3
(ecto) lib/ecto/changeset.ex:2730: anonymous fn/4 in Ecto.Changeset.merge_related_keys/4
(stdlib) maps.erl:257: :maps.fold_1/3
(absinthe_error_payload) lib/absinthe_error_payload/changeset_parser.ex:17: AbsintheErrorPayload.ChangesetParser.extract_messages/1
This looks like it is happening because I have a nested type in my model, like this:
schema "products" do
# ...
field(:tags, {:array, :string})
# ...
timestamps(type: :utc_datetime_usec)
end
The {:array, :string}
type is breaking the interpolate_messages
function, as you cannot automatically cast a tuple to a string
Is there any intent/interest in accepting atoms as error messages? For instance if the reason a mutation errors is its unauthenticated or the record it is attempting to change doesn't exist, :unauthorized
or :not_found
would be the expected error return, but if I want this library to parse that into the message I'd have to check to see if I'm returning an atom everywhere I might and stringify it.
Is adding a function head that converts atoms to binaries to convert_to_payload/1
a reasonable thing, or is the assumption that users of this library catch simple atom errors and format them into more verbose errors?
A better schema seems should be like this:
field(:messages, list_of(non_null:validation_message))
Any reason allowing nullable validation messages in the list?
Are there any projects using this library that I could check out?
When I follow the docs https://hexdocs.pm/absinthe_error_payload/AbsintheErrorPayload.Payload.html#module-usage, I keep getting errors and I can't figure out why. Thanks 😄
This is the error I get btw
Unexpected validation message: #Ecto.Changeset<action: :insert, changes: %{email: \"vxasaaa\", password_hash: \"P\", role: \"d\", username: \"hadfsaaaa\"}, errors: [username: {\"has already been taken\", [constraint: :unique, constraint_name: \"users_username_index\"]}]
Thanks for the great library, I've got a point of confusion I'd like help understanding.
The primary philosophy is that messages generated by invalid and/or unexpected user input are DATA, and should be returned as such. On the other hand, errors made in using an API - like querying a field that doesn’t exist -, are actually ERRORS and should be returned as errors.
are actually ERRORS and should be returned as errors.
This line confused me. Typically, my GraphQL responses have "data" and "errors" at the root. If, before passing into the build_payload function my resolution already has errors, I'm surprised they are being manipulated into the payload object and not being left as errors. That's what I though it meant by ERRORS should be returned as errors.
On the client side this gives me a bit of a problem. For example:
My ideal would be a change like this to the library, but please feel free to tell me why it'd a bad idea!
def build_payload(%{errors: _errors} = resolution, _config), do: resolution
Thanks for maintaining kronky/this! Is there a migration guide for migrating from Kronky? From the sounds of it not much is needed, just changing {:kronky, ">= 0.0.0"}
in your deps to :absinthe_error_payload
and Kronky
to AbsintheErrorPayload
but it would be nice to have that explicitly called out in the Readme or supplementary doc.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.