0

I am on halfway of implementing JSON API structure (with underscore attributes).


Actual state for development environment is:

I use the Active Model Adapter structure for requesting to the backend for resources and backend response me with JSON API structure.

In Application Serializer I am using JSONAPISerializer. I override methods:

serializeBelongsTo
keyForRelationship
keyForAttribute
serialize
serializeAttribute
serializeHasMany

and for development, everything works for me (backend in Rails communicate with Ember very good).


The problem is with Ember CLI Mirage and conventions (not sure if there are simple solutions or I need to override again methods in this addon).

Actual state with Ember Cli Mirage and test environment:

I am using import { JSONAPISerializer } from 'ember-cli-mirage'; and then trying to manipulate proper request and then transform it for JSON API format.

It could work like this:

Ember Adapter (Active Model Adapter format - with underscore attributes) ---> Mirage Serializer should get request (find resources created before in tests with associations) and then response it with JSON API format ---> JSON API Serializer could catch it and fill Ember DS.

For now, I have a missing part to serialize it for all cases to JSON API standard (with underscored attributes)

Where should I do this transformation to minimize overriding JSONAPISerializer Mirage Serializer.

I noticed that there are some helpers, but I have a problem to wrap this knowledge together (http://www.ember-cli-mirage.com/docs/advanced/route-handlers#helpers)

UPDATE:

Example of structure from Backend:

{
  "data": {
    "id": "6",
    "type": "first_resource",
    "attributes": {
      "id": 6,
      "my_attribute": "my_attribute"
    },
    "relationships": {
      "second_resources": {
        "data": [
          {
            "id": "16",
            "type": "second_resource"
          }
        ]
      },
      "third_resource_other_type": {
        "data": {
          "id": "1",
          "type": "third_resource"
        }
      },
      "fourth_resource": {
        "data": {
          "id": "1",
          "type": "fourth_resource"
        }
      }
    },
    "links": {
      "fifth_resources": "/api/v1/first_resources/6/fifth_resources"
    }
  },
  "included": [
    {
      "id": "1",
      "type": "fourth_resource",
      "attributes": {
        "id": 1,
        "my_attribute": "my_attribute"
      },
      "links": {
        "sixth_resource": "/api/v1/fourth_resources/1/sixth_resource"
      }
    },
    {
      "id": "16",
      "type": "second_resource",
      "attributes": {
        "id": 16,
        "my_attribute": "my_attribute"
      },
      "relationships": {
        "eighth_resources": {
          "data": []
        }
      },
      "links": {
        "seventh_resources": "/api/v1/second_resources/16/seventh_resources"
      }
    },
    {
      "id": "17",
      "type": "second_resource",
      "attributes": {
        "id": 17,
        "my_attribute": "my_attribute"
      },
      "relationships": {
        "eighth_resources": {
          "data": []
        }
      },
      "links": {
        "seventh_resources": "/api/v1/second_resources/17/seventh_resources"
      }
    },
    {
      "id": "15",
      "type": "second_resource",
      "attributes": {
        "id": 15,
        "my_attribute": "my_attribute"
      },
      "relationships": {
        "eighth_resources": {
          "data": [
            {
              "id": "26",
              "type": "eighth_resource"
            },
            {
              "id": "24",
              "type": "eighth_resource"
            }
          ]
        }
      },
      "links": {
        "seventh_resources": "/api/v1/second_resources/15/seventh_resources"
      }
    },
    {
      "id": "26",
      "type": "eighth_resource",
      "attributes": {
        "id": 26,
        "my_attribute": "my_attribute"
      }
    }
  ]
}

UPDATE2

structure from mirage response:

data: {
  attributes: {
    my_attribute: 'my_attribute',
    second_resource_ids: [36, 37],
    fifth_resource_ids: []
  },
  id: 11,
  relationships: {
    third_resource_other_type: {data: null}
    fourth_resource: {data: null}
    second_resources: {data: []}
  },
  type: "first_resources"
}

resources in tests:

server.create('second-resource', {
  id: 36,
  first_resource_id: '11',
  my_attribute: "my_attribute"
});

server.create('eighth-resource', { 
  id: 140,
  second_resource_id: 37
});

server.create('eighth-resource', {
  id: 141,
  second_resource_id: 37
});

server.create('second-resource', {
  id: 37,
  first_resource_id: '11',
  eighth_resource_ids: [140, 141]
});

server.create('first-resource', {
  id: 11,
  second_resource_ids: [36, 37]
});

first_resource model in mirage:

export default Model.extend({
  third_resource_other_type: belongsTo(),
  fourth_resource: belongsTo(),
  fifth_resources: hasMany(),
  second_resources: hasMany()
});
Hubert Olender
  • 1,130
  • 9
  • 15
  • Could you post an update with a simple example of a JSON payload that's coming from your Rails server, as well as what the corresponding Mirage response looks like for the same endpoint? – Sam Selikoff Mar 10 '19 at 13:54
  • @SamSelikoff updated example of the response from my backend – Hubert Olender Mar 10 '19 at 14:43

1 Answers1

2

Let's try to focus a single relationship, since there's a lot going on in the question you've posted. We'll look at second-resource.

It looks like Mirage is sending back second_resource_ids under the attributes key of the first_resource primary data in the JSON:API payload. That tells me Mirage thinks second_resource_ids is an attribute of first_resource, when in fact it's a relationship.

Assuming your Models & Relationships are setup correctly, you need to tweak the way you're creating data in Mirage.

If you take a look at the Associations section of the Defining Routes guide, you'll see this message:

Mirage's database uses camelCase for all model attributes, including foreign keys (e.g. authorId in the example above)

Right now, you're doing this:

server.create('second-resource', {
  id: 36,
  first_resource_id: '11',
  my_attribute: "my_attribute"
});

server.create('first-resource', {
  id: 11,
  second_resource_ids: [36, 37]
});

But from Mirage's perspective, you need to use camelCase IDs, or just pass in the relationships, to set these up correctly. Something like this:

let firstResource = server.create('first-resource', {
  id: 11
});

server.create('second-resource', {
  id: 36,
  firstResource,
  myAttribute: "my_attribute"
});

You could also pass in the foreign key on creation if you wanted - just be sure to use camelCase:

server.create('second-resource', {
  id: 36,
  firstResourceId: '11',
  myAttribute: "my_attribute"
});

Just remember that the formatting decisions for things like attributes and foreign keys (things like some-attribute vs. some_attribute or relationship-id vs. relationship_id) are made at the serializer layer. When dealing with Mirage's ORM and database, you want to stick to camelCase, regardless of the format your Serializer emits.


For more information, have a look at these sections from the docs:

Sam Selikoff
  • 12,366
  • 13
  • 58
  • 104
  • Thank you for these advices, I was confued on some stuff, because there were dasherized: "server.create('second-resource', {" camelCased: "firstResourceId: '11'," underscored: "'second_resources': {" I also noticed that I had some bad configured things. After I improved these connections plus fixed associations on serializers I added too: alwaysIncludeLinkageData: true, keyForRelationship(key) { return underscore(key); }, keyForAttribute(attr) { return underscore(attr); }, normalizeIds: 'always', normalize(payload) { (...) } – Hubert Olender Mar 12 '19 at 13:23
  • Now the documentation is much better than I started with it. I appreciate it. After what I add there I don't see "links: {" on my response, not sure if it's correct bahavior on mirage or I need set something else too. – Hubert Olender Mar 12 '19 at 13:25
  • 1
    Check out the links section of the [Serializer](http://www.ember-cli-mirage.com/docs/api/modules/ember-cli-mirage/serializer~Serializer#links) documentation, or if you're not using JSONAPISerializer you can add them after calling "super" in the serialize method (check the `serialize` method docs). – Sam Selikoff Mar 15 '19 at 23:20
  • Thank you for the answers and to show me these links which stuck to the point. After these suggestions, I refactored around 100 files. One of the most confused things were when mirage factory has for example `my_type` (with underscore) - this attribute works with server.create('resource'). When I did server.create('resource', {my_type: 'something'}) it worked. When I did server.create('resource', {myType: 'something')) it didn't work. When I changed factory to `myType`, then `server.create('resource', {myType: 'something'))` worked. I had a mix of it and that caused a headache. Thanks. – Hubert Olender Mar 21 '19 at 11:21