Coder Social home page Coder Social logo

mirego / absinthe_error_payload Goto Github PK

View Code? Open in Web Editor NEW
115.0 32.0 24.0 93 KB

Bridges the gap between Ecto and Absinthe for mutation payload

Home Page: https://open.mirego.com

License: Other

Elixir 98.56% Makefile 1.44%
ecto absinthe graphql elixir

absinthe_error_payload's People

Contributors

acbullen avatar davorbadrov avatar dolfinus avatar kianmeng avatar lauraannwilliams avatar martinmaillard avatar remi avatar scottming avatar simonprev avatar thermech avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

absinthe_error_payload's Issues

Integration with Gettext

It there a way to translate error messages with Gettext? I guess relevant function is AbsintheErrorPayload.ChangesetParser.interpolate_message

Ranges are not handled

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

Absinthe 1.5.0

Just a reminder that absinthe 1.5.0 will be released "soonish" and this fine package should be compatible with it. Thanks.

ValidationMessage options not working with a keyword list

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.

ChangesetParser.extract_messages breaks with tuple types

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

Request: Handle atom error messages

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?

Middleware style usage example/tutorial

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\"]}]

Clarification Question. Errors

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:

  • I check auth before resolving, it fails, I add an {:error, :unauthorised}
  • I look for this error on the client to refresh tokens etc.
  • On my mutations which use build_payload, these errors will get moved to messages.
  • This leaves me with 2 different checks.

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

Migration guide

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.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.