inaka / Dayron

A repository `similar` to Ecto.Repo that maps to an underlying http client, sending requests to an external rest api instead of a database
http://inaka.net/blog/2016/05/24/introducing-dayron/
Apache License 2.0
159 stars 20 forks source link

Allow specifying top-level attribute, possibly overriding `__from_json_list__: 2` #55

Closed jcypret closed 7 years ago

jcypret commented 7 years ago

I'm currently using Dayron to interact with a jsonapi.org spec API (and it's working great! 😄), but ran into an issue deserializing the data. The JSON-API spec returns either a top level data key or a top-level errors keys. This runs into an issue with Dayron because in Dayron.Model the first method that gets called is __from_json_list__/2 (https://github.com/inaka/Dayron/blob/7be2be5be319fbd502a2d97a19cc86118d75191f/lib/dayron/model.ex#L90) which assumes the array of results is at the root level.

So, when JSON-API returns an array of records nested inside of the data attribute, Dayron sees there is no list at the root level, then it tries to apply the attributes to a struct, which results in an empty struct being returned.

I was able to correct his behavior by mapping the data key instead:

def __from_json_list__(%{data: data}, opts) do
  Enum.map(data, &__from_json__(&1, opts))
end

This also had the benefit of allowing me to match on the error key as well:

def __from_json_list__(%{errors: errors}, opts) do
  # ...
end

But this is not currently allowed by the Dayron library. I had to adjust the Dayron.Model to allow the __from_json_list/2` to be overridable, otherwise the library models took precedence:

# lib/dayron/model.ex#96
defoverridable [__url_for__: 1, __from_json__: 2, __from_json_list__: 2]

With the overridable change to the library and the custom __from_json_list__/2 method on the model, I was able to achieve the desired result.

flaviogranero commented 7 years ago

@jcypret thank you for pointing this out!

jcypret commented 7 years ago

I've opened a pull request: https://github.com/inaka/Dayron/pull/60