vt-elixir / ja_serializer

JSONAPI.org Serialization in Elixir.
Other
640 stars 148 forks source link

data.type attribute gets overwritten by data.attributes.type value #236

Closed greyhwndz closed 5 years ago

greyhwndz commented 7 years ago

I had a prob with ja_serializer where the value of an attribute called type (data.attributes.type) is being copied to the value of the type field at the level of id & attribute. The type of the following data should be rental and not Estate, Condo or Apartment:

{
  "jsonapi":{
    "version":"1.0"
  },
  "data":[
    {
      "type":"Estate",
      "id":"1",
      "attributes":{
        "updated-at":"2017-03-24T07:13:29.867832",
        "type":"Estate",
        "title":"Grand Old Mansion",
        "owner":"Veruca Salt",
        "inserted-at":"2017-03-24T07:13:29.819691",
        "image":"https://upload.wikimedia.org/wikipedia/commons/c/cb/Crane_estate_(5).jpg",
        "city":"San Francisco",
        "bedrooms":15
      }
    },
    {
      "type":"Condo",
      "id":"2",
      "attributes":{
        "updated-at":"2017-03-24T07:13:30.049467",
        "type":"Condo",
        "title":"Urban Living",
        "owner":"Mike Teavee",
        "inserted-at":"2017-03-24T07:13:30.049458",
        "image":"https://upload.wikimedia.org/wikipedia/commons/0/0e/Alfonso_13_Highrise_Tegucigalpa.jpg",
        "city":"Seattle",
        "bedrooms":1
      }
    },
    {
      "type":"Apartment",
      "id":"3",
      "attributes":{
        "updated-at":"2017-03-24T07:13:30.051900",
        "type":"Apartment",
        "title":"Downtown Charm",
        "owner":"Violet Beauregarde",
        "inserted-at":"2017-03-24T07:13:30.051891",
        "image":"https://upload.wikimedia.org/wikipedia/commons/f/f7/Wheeldon_Apartment_Building_-_Portland_Oregon.jpg",
        "city":"Portland",
        "bedrooms":3
      }
    }
  ]
}

Here is a link to the problematic repo having this prob: https://github.com/greyhwndz/phoenix_api#when-i-curl-to-the-api

@alanpeabody ^^^ :)

IceDragon200 commented 7 years ago

@greyhwndz

Having the same problem here, I've been looking through the code for the cause, but haven't gotten the exact location.

Something overwrote type/2 and added a type/1 function to the view, this type/2 calls the type/1 and retrieves the type from the data provided. i.e:

MySerializer.type(%{type: "other_type"}, conn) #=> "other_type"

Now, if I could only find who or what is doing it.

As a fix however, you can overwrite type/2 in the serializer and have it return the correct type.

defmodule App.RecordView do
  use App.Web, :view

  def type(_, _), do: "correct_type"
  # ...
end

That's if you just want to correct a single view.

You can always just force it back to it's original behaviour by defining type/2 in the view quote and having it return type/0

defmodule App.Web do
  # ...
  def view do
    quote do
      use Phoenix.View, root: "web/templates"

      # Import convenience functions from controllers
      import Phoenix.Controller, only: [get_csrf_token: 0, get_flash: 2, view_module: 1]

      # Use all HTML functionality (forms, tags, etc)
      use Phoenix.HTML

      import App.Web.Router.Helpers
      import App.Web.ErrorHelpers
      import App.Web.Gettext

      # Serialize as JSONAPI
      use JaSerializer.PhoenixView
      def type(_, _), do: type()
    end
  end
  # ...
end
greyhwndz commented 7 years ago

@IceDragon200,

Seems like the jsonapi specs doesn't recommend using the word type to be used in the attributes part of the response. It is a reserved word together with id.

http://jsonapi.org/format/#document-resource-object-fields

alanpeabody commented 7 years ago

Yeah, this is a conflict with the callback functions and using then as attributes. If you do continue to use type or any of the other callback functions I suggest using the attributes callback instead of the attributes macro.

It would be great to add documentation to the attributes/1 macro to the effect if someone it interested.

atomkirk commented 7 years ago

same thing happens with a meta attribute

I don't have the option of renaming fields, so for those searching, I worked around it like this:

  def attributes(cat, _conn), do: %{
    type: cat.type,
    meta: cat.meta,
    error_message: error_message(cat, _conn),
  }

  def type(_, _), do: type()
joshsmith commented 6 years ago

It's a bit unfortunate this a reserved keyword, because it means in cases where we don't have control over the attributes (Stripe Connect accounts for example have a type), then we have to use the callback instead of the macro. This is less your problem than it is just an unfortunate thing about JSON API.

greyhwndz commented 6 years ago

@joshsmith perhaps airing this concern as an issue to the JSON API specs repo might be welcome for the proponents :)

CMCDragonkai commented 6 years ago

This should result in some sort of warning. I was using type field in my DB, and now I have rename everything.

paveltyk commented 5 years ago

Is there any way to force type in the serializer for data.type only?

alanpeabody commented 5 years ago

FIX for this issue: Don't use attributes macro, use the attributes callback instead. This is only an issue when using the macro.

If someone want to add docs or a warning to the macro that would be great.

Otherwise the solution is use the behaviour callbacks instead of the macros.