Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Add normalizePayload(modelName, payload) to DS.Store #3838

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/ember-data/lib/serializers/json-api-serializer.js
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,10 @@ const JSONAPISerializer = JSONSerializer.extend({
store.push(normalizedPayload);
},

normalizePayload: function(primaryModelName, payload) {
return this._normalizeDocumentHelper(payload);
},

/**
@method _normalizeResponse
@param {DS.Store} store
Expand Down
35 changes: 30 additions & 5 deletions packages/ember-data/lib/serializers/rest-serializer.js
Original file line number Diff line number Diff line change
Expand Up @@ -344,33 +344,58 @@ var RESTSerializer = JSONSerializer.extend({
@param {Object} payload
*/
pushPayload: function(store, payload) {
let documentHash = this.normalizePayload(payload);
store.push(documentHash);
},

normalizePayload: function(primaryModelName, payload) {
if (!payload) {
payload = primaryModelName;
primaryModelName = null;
}

let documentHash = {
data: [],
included: []
};

for (var prop in payload) {
var modelName = this.modelNameFromPayloadKey(prop);
if (!store.modelFactoryFor(modelName)) {
if (!this.store.modelFactoryFor(modelName)) {
Ember.warn(this.warnMessageNoModelForKey(prop, modelName), false, {
id: 'ds.serializer.model-for-key-missing'
});
continue;
}
var type = store.modelFor(modelName);
var typeSerializer = store.serializerFor(type.modelName);
var type = this.store.modelFor(modelName);
var typeSerializer = this.store.serializerFor(type.modelName);

/*jshint loopfunc:true*/
Ember.makeArray(payload[prop]).forEach((hash) => {
let { data, included } = typeSerializer.normalize(type, hash, prop);
documentHash.data.push(data);

if (primaryModelName) {
// if a primary model is specified, set the data as primary data of
// the normalized payload, otherwise the data is included
if (primaryModelName === modelName) {
documentHash.data = data;
} else {
documentHash.included.push(data);
}
} else {
// no primary model specified means, that the primary data of each
// included resource is added to the primary data of the normalized
// payload
documentHash.data.push(data);
}

if (included) {
documentHash.included.push(...included);
}
});
}

store.push(documentHash);
return documentHash;
},

/**
Expand Down
70 changes: 70 additions & 0 deletions packages/ember-data/lib/system/store.js
Original file line number Diff line number Diff line change
Expand Up @@ -1810,6 +1810,76 @@ Store = Service.extend({
this._adapterRun(() => serializer.pushPayload(this, payload));
},

/**
Normalize raw data.

This method can be used to normalize a payload into JSON API format Ember
Data expects for `push`. If the primary model of the payload is specified
as first parameter, the normalized payload will contain the data of this
model as primary data of the normalized hash:

```js
var payload = {
posts: [
{ id: 1, post_title: "Great post", comment_ids: [2] }
],
comments: [
{ id: 2, comment_body: "Insightful comment" }
]
}

var normalizedPayload = store.normalizePayload('post', pushData);

normalizedPayload == {
data: [
{ id: 1, type: 'post', ... }
],
included: [
{ id: 2, type: 'comment', ... }
]
};
```

If the primary model is omitted, then all data is added to the primary data
of the resulting hash:

```js
var payload = {
posts: [
{ id: 1, post_title: "Great post", comment_ids: [2] }
],
comments: [
{ id: 2, comment_body: "Insightful comment" }
]
}

var normalizedPayload = store.normalizePayload(pushData);

normalizedPayload == {
data: [
{ id: 1, type: 'post', ... },
{ id: 2, type: 'comment', ... }
],
included: []
};
```
*/
normalizePayload: function(modelName, inputPayload) {
var serializer;
var payload;
if (!inputPayload) {
payload = modelName;
serializer = defaultSerializer(this);
} else {
payload = inputPayload;
Ember.assert(`Passing classes to store methods has been removed. Please pass a dasherized string instead of ${Ember.inspect(modelName)}`, typeof modelName === 'string');
serializer = this.serializerFor(modelName);
}

Ember.assert("You cannot use `store#normalizePayload` without your serializer defining `normalizePayload`", typeof serializer.normalizePayload === 'function');
return serializer.normalizePayload(modelName, inputPayload);
},

/**
`normalize` converts a json payload into the normalized form that
[push](#method_push) expects.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,3 +120,51 @@ test('Warns when normalizing an unknown type', function() {
});
}, /Encountered a resource object with type "UnknownType", but no model was found for model name "unknown-type"/);
});

test('normalizePayload(modelName, payload)', function() {
var payload = {
data: {
type: 'user',
id: '1',
relationships: {
company: {
data: { type: 'company', id: '2' }
}
}
},
included: [{
type: 'company',
id: '2',
attributes: {
name: 'Tilde Inc.'
}
}]
};

var normalizedPayload = env.store.normalizePayload('user', payload);
deepEqual(normalizedPayload, payload);
});

test('normalizePayload(payload)', function() {
var payload = {
data: {
type: 'user',
id: '1',
relationships: {
company: {
data: { type: 'company', id: '2' }
}
}
},
included: [{
type: 'company',
id: '2',
attributes: {
name: 'Tilde Inc.'
}
}]
};

var normalizedPayload = env.store.normalizePayload('user', payload);
deepEqual(normalizedPayload, payload);
});
136 changes: 136 additions & 0 deletions packages/ember-data/tests/unit/store/normalize-payload-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
var env, store, Person, PhoneNumber, Post;
var attr = DS.attr;
var hasMany = DS.hasMany;
var belongsTo = DS.belongsTo;
var run = Ember.run;

module("unit/store/normalize-payload - DS.Store#normalizePayload", {
setup: function() {
Person = DS.Model.extend({
firstName: attr('string'),
lastName: attr('string'),
phoneNumbers: hasMany('phone-number', { async: false })
});
Person.toString = function() {
return 'Person';
};

PhoneNumber = DS.Model.extend({
number: attr('string'),
person: belongsTo('person', { async: false })
});
PhoneNumber.toString = function() {
return 'PhoneNumber';
};

Post = DS.Model.extend({
postTitle: attr('string')
});
Post.toString = function() {
return 'Post';
};

env = setupStore({
post: Post,
person: Person,
"phone-number": PhoneNumber
});

store = env.store;

env.registry.register('serializer:post', DS.RESTSerializer);
},

teardown: function() {
run(function() {
store.destroy();
});
}
});

test("normalizePayload(modelName, payload)", function() {
env.registry.register('serializer:application', DS.RESTSerializer.extend());

var payload = {
person: {
id: 1,
firstName: "first name",
phoneNumbers: [1]
},
phoneNumbers: [
{ id: 1, number: "1234" }
]
};
var normalized = store.normalizePayload('person', payload);

deepEqual(normalized, {
data: {
id: '1',
type: 'person',
attributes: {
firstName: 'first name'
},
relationships: {
phoneNumbers: {
data: [
{ id: '1', type: 'phone-number' }
]
}
}
},
included: [
{
id: '1',
type: 'phone-number',
attributes: {
number: '1234'
},
relationships: {}
}
]
});
});

test("normalizePayload(payload)", function() {
env.registry.register('serializer:application', DS.RESTSerializer.extend());

var payload = {
person: {
id: 1,
firstName: "first name",
phoneNumbers: [1]
},
phoneNumbers: [
{ id: 1, number: "1234" }
]
};
var normalized = store.normalizePayload(payload);

deepEqual(normalized, {
data: [
{
id: '1',
type: 'person',
attributes: {
firstName: 'first name'
},
relationships: {
phoneNumbers: {
data: [
{ id: '1', type: 'phone-number' }
]
}
}
},
{
id: '1',
type: 'phone-number',
attributes: {
number: '1234'
},
relationships: {}
}
],
included: []
});
});