Fix references in V3 payloads

V3 API doesn't return any of the records more than 2 times. If a record
is already included in the response any other occurences will be
represented as a reference, ie. a hash with just an @href. Ember Data
doesn't play nice with such references as it needs an id to identify a
record.

The code in this commit traverses payloads from V3 API and adds an id to
each of the references that are present.

For example a following payload:

    {
      "@href": "/build/1",
      "@type": "build"
      "id": 1,
      "state": "passed",
      "branch": {
        "@href": "/repo/1/branch/master",
        "name": "master",
        "lastBuild": {
          "@href": "/build/1"
        }
      }
    }

Will be changed to:

    {
      "@href": "/build/1",
      "@type": "build"
      "id": 1,
      "state": "passed",
      "branch": {
        "@href": "/repo/1/branch/master",
        "name": "master",
        "lastBuild": {
          "@href": "/build/1",
          "id": 1
        }
      }
    }

In this case an "id" field was added to "branch.lastBuild" field.
This commit is contained in:
Piotr Sarnacki 2015-11-18 16:48:21 +01:00
parent e2a602a8af
commit 147ab06fcf

View File

@ -1,6 +1,29 @@
import Ember from 'ember';
import DS from 'ember-data';
var traverse = function(object, callback) {
if(!object) {
return;
}
if(typeof(object) === 'object' && !Ember.isArray(object)) {
callback(object);
}
if(Ember.isArray(object)) {
for(let item of object) {
traverse(item, callback);
}
} else if(typeof object === 'object') {
for(let key in object) {
if(object.hasOwnProperty(key)) {
let item = object[key];
traverse(item, callback);
}
}
}
};
export default DS.JSONSerializer.extend({
isNewSerializerAPI: true,
@ -38,6 +61,11 @@ export default DS.JSONSerializer.extend({
return attributes;
},
normalizeResponse(store, primaryModelClass, payload, id, requestType) {
this._fixReferences(payload);
return this._super(...arguments);
},
normalizeArrayResponse(store, primaryModelClass, payload, id, requestType) {
let documentHash = {
data: null,
@ -111,5 +139,44 @@ export default DS.JSONSerializer.extend({
} else {
return Ember.String.camelize(key);
}
},
_fixReferences(payload) {
let byHref = {}, href, records;
if(payload['@type']) {
// API V3 doesn't return all of the objects in a full representation
// If an object is present in one place in the response, all of the
// other occurences will be just references of a kind - they will just
// include @href property.
//
// I don't want to identify records by href in ember-data, so here I'll
// set an id and a @type field on all of the references.
//
// First we need to group all of the items in the response by href:
traverse(payload, (item) => {
if(href = item['@href']) {
if(records = byHref[href]) {
records.push(item);
} else {
byHref[href] = [item];
}
}
});
// Then we can choose a record with an id for each href and put the id
// in all of the other occurences.
for(let href in byHref) {
records = byHref[href];
let recordWithAnId = records.find( (record) => record.id );
if(recordWithAnId) {
for(let record of records) {
record.id = recordWithAnId.id;
//record['@type'] = recordWithAnId['@type'];
}
}
}
}
return payload;
}
});