beam-community / jsonapi

JSON:API Serializer and Query Handler for Elixir
https://hex.pm/packages/jsonapi
MIT License
490 stars 79 forks source link

(FunctionClauseError) no function clause matching in JSONAPI.Utils.DataToParams.transform_relationship/2 #294

Closed matt-glover closed 1 year ago

matt-glover commented 1 year ago

The error in the title occurs if certain keys are present and others are missing from the JSON body when attempting to POST to an endpoint.

In my local example I setup a plug pipeline with the following JSON API plugs:

    plug JSONAPI.EnsureSpec
    plug JSONAPI.Deserializer

I largely copied over the examples from the README to setup some views and wire those into a controller.

Once I called the route via curl I correctly got warnings from EnsureSpec for many different missing JSON keys. Example:

{"errors":[{"detail":"Check out http://jsonapi.org/format/#crud for more info.","source":{"pointer":"/data/type"},"status":"400","title":"Missing type in data parameter"}]}

Once I partially/incorrectly defined a relationship the deserializer started to crash leading to a 500 error:

[info] POST /posts
[debug] Processing with TryJsonWeb.PageController.create/2
  Parameters: %{"data" => %{"attributes" => %{}, "type" => "posts"}, "relationships" => %{"comment" => %{"data" => %{}}}}
  Pipelines: [:jsonapi]
[info] Sent 500 in 6ms
[error] #PID<0.846.0> running TryJsonWeb.Endpoint (connection #PID<0.845.0>, stream id 1) terminated
Server: localhost:4000 (http)
Request: POST /posts
** (exit) an exception was raised:
    ** (FunctionClauseError) no function clause matching in JSONAPI.Utils.DataToParams.transform_relationship/2
        (jsonapi 1.5.1) lib/jsonapi/utils/data_to_params.ex:60: JSONAPI.Utils.DataToParams.transform_relationship({"comment", %{"data" => %{}}}, %{})
        (stdlib 3.17) maps.erl:410: :maps.fold_1/3
        (jsonapi 1.5.1) lib/jsonapi/utils/data_to_params.ex:53: JSONAPI.Utils.DataToParams.process_relationships/1
        (jsonapi 1.5.1) lib/jsonapi/utils/data_to_params.ex:15: JSONAPI.Utils.DataToParams.process/1
        (jsonapi 1.5.1) lib/jsonapi/plugs/deserializer.ex:61: JSONAPI.Deserializer.call/2
        (try_json 0.1.0) TryJsonWeb.Router.jsonapi/2
        (try_json 0.1.0) lib/try_json_web/router.ex:1: TryJsonWeb.Router.__pipe_through1__/1
        (phoenix 1.6.16) lib/phoenix/router.ex:346: Phoenix.Router.__call__/2
        (try_json 0.1.0) lib/try_json_web/endpoint.ex:1: TryJsonWeb.Endpoint.plug_builder_call/2
        (try_json 0.1.0) lib/plug/debugger.ex:136: TryJsonWeb.Endpoint."call (overridable 3)"/2
        (try_json 0.1.0) lib/try_json_web/endpoint.ex:1: TryJsonWeb.Endpoint.call/2
        (phoenix 1.6.16) lib/phoenix/endpoint/cowboy2_handler.ex:54: Phoenix.Endpoint.Cowboy2Handler.init/4
        (cowboy 2.10.0) /home/matt/code/try_json/deps/cowboy/src/cowboy_handler.erl:37: :cowboy_handler.execute/2
        (cowboy 2.10.0) /home/matt/code/try_json/deps/cowboy/src/cowboy_stream_h.erl:306: :cowboy_stream_h.execute/3
        (cowboy 2.10.0) /home/matt/code/try_json/deps/cowboy/src/cowboy_stream_h.erl:295: :cowboy_stream_h.request_process/3
        (stdlib 3.17) proc_lib.erl:226: :proc_lib.init_p_do_apply/3

I'm happy to offer a PR to the deserializer flow to reduce the cases that crash in the face of malformed params like these. But I wanted to log an issue in case there's a preference for an alternate approach with this project like adding more EnsureSpec checks or something else.

mattpolzin commented 1 year ago

I think I like your idea of handling this with EnsureSpec as opposed to making other code handle more cases not representable by JSON:API. A tough call, but that's the way I think I lean.

matt-glover commented 1 year ago

Cool. I'll take a pass at enhancing the spec flow early next week and share what I come up with.

mattpolzin commented 1 year ago

Closed by https://github.com/beam-community/jsonapi/pull/299