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

Defer serializer loading #4884

Merged
merged 1 commit into from
Mar 31, 2017
Merged
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
18 changes: 9 additions & 9 deletions addon/-private/system/store/finders.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,14 @@ export function _find(adapter, store, modelClass, id, internalModel, options) {
let snapshot = internalModel.createSnapshot(options);
let { modelName } = internalModel;
let promise = adapter.findRecord(store, modelClass, id, snapshot);
let serializer = serializerForAdapter(store, adapter, modelName);
let label = `DS: Handle Adapter#findRecord of '${modelName}' with id: '${id}'`;

promise = Promise.resolve(promise, label);
promise = _guard(promise, _bind(_objectIsAlive, store));

return promise.then(adapterPayload => {
assert(`You made a 'findRecord' request for a '${modelName}' with id '${id}', but the adapter's response did not have any data`, payloadIsNotBlank(adapterPayload));
let serializer = serializerForAdapter(store, adapter, modelName);
let payload = normalizeResponseHelper(serializer, store, modelClass, adapterPayload, id, 'findRecord');
assert(`Ember Data expected the primary data returned from a 'findRecord' response to be an object but instead it found an array.`, !Array.isArray(payload.data));

Expand All @@ -58,7 +58,6 @@ export function _findMany(adapter, store, modelName, ids, internalModels) {
let snapshots = Ember.A(internalModels).invoke('createSnapshot');
let modelClass = store.modelFor(modelName); // `adapter.findMany` gets the modelClass still
let promise = adapter.findMany(store, modelClass, ids, snapshots);
let serializer = serializerForAdapter(store, adapter, modelName);
let label = `DS: Handle Adapter#findMany of '${modelName}'`;

if (promise === undefined) {
Expand All @@ -70,6 +69,7 @@ export function _findMany(adapter, store, modelName, ids, internalModels) {

return promise.then(adapterPayload => {
assert(`You made a 'findMany' request for '${modelName}' records with ids '[${ids}]', but the adapter's response did not have any data`, payloadIsNotBlank(adapterPayload));
let serializer = serializerForAdapter(store, adapter, modelName);
let payload = normalizeResponseHelper(serializer, store, modelClass, adapterPayload, null, 'findMany');
return store._push(payload);
}, null, `DS: Extract payload of ${modelName}`);
Expand All @@ -79,7 +79,6 @@ export function _findHasMany(adapter, store, internalModel, link, relationship)
let snapshot = internalModel.createSnapshot();
let modelClass = store.modelFor(relationship.type);
let promise = adapter.findHasMany(store, snapshot, link, relationship);
let serializer = serializerForAdapter(store, adapter, relationship.type);
let label = `DS: Handle Adapter#findHasMany of '${internalModel.modelName}' : '${relationship.type}'`;

promise = Promise.resolve(promise, label);
Expand All @@ -88,6 +87,7 @@ export function _findHasMany(adapter, store, internalModel, link, relationship)

return promise.then(adapterPayload => {
assert(`You made a 'findHasMany' request for a ${internalModel.modelName}'s '${relationship.key}' relationship, using link '${link}' , but the adapter's response did not have any data`, payloadIsNotBlank(adapterPayload));
let serializer = serializerForAdapter(store, adapter, relationship.type);
let payload = normalizeResponseHelper(serializer, store, modelClass, adapterPayload, null, 'findHasMany');
let internalModelArray = store._push(payload);

Expand All @@ -100,14 +100,14 @@ export function _findBelongsTo(adapter, store, internalModel, link, relationship
let snapshot = internalModel.createSnapshot();
let modelClass = store.modelFor(relationship.type);
let promise = adapter.findBelongsTo(store, snapshot, link, relationship);
let serializer = serializerForAdapter(store, adapter, relationship.type);
let label = `DS: Handle Adapter#findBelongsTo of ${internalModel.modelName} : ${relationship.type}`;

promise = Promise.resolve(promise, label);
promise = _guard(promise, _bind(_objectIsAlive, store));
promise = _guard(promise, _bind(_objectIsAlive, internalModel));

return promise.then(adapterPayload => {
let serializer = serializerForAdapter(store, adapter, relationship.type);
let payload = normalizeResponseHelper(serializer, store, modelClass, adapterPayload, null, 'findBelongsTo');

if (!payload.data) {
Expand All @@ -123,14 +123,14 @@ export function _findAll(adapter, store, modelName, sinceToken, options) {
let recordArray = store.peekAll(modelName);
let snapshotArray = recordArray._createSnapshot(options);
let promise = adapter.findAll(store, modelClass, sinceToken, snapshotArray);
let serializer = serializerForAdapter(store, adapter, modelName);
let label = "DS: Handle Adapter#findAll of " + modelClass;

promise = Promise.resolve(promise, label);
promise = _guard(promise, _bind(_objectIsAlive, store));

return promise.then(adapterPayload => {
assert(`You made a 'findAll' request for '${modelName}' records, but the adapter's response did not have any data`, payloadIsNotBlank(adapterPayload));
let serializer = serializerForAdapter(store, adapter, modelName);
let payload = normalizeResponseHelper(serializer, store, modelClass, adapterPayload, null, 'findAll');

store._push(payload);
Expand All @@ -144,15 +144,15 @@ export function _query(adapter, store, modelName, query, recordArray) {
let modelClass = store.modelFor(modelName); // adapter.query needs the class
let promise = adapter.query(store, modelClass, query, recordArray);

let serializerToken = heimdall.start('initial-serializerFor-lookup');
let serializer = serializerForAdapter(store, adapter, modelName);
heimdall.stop(serializerToken);
let label = `DS: Handle Adapter#query of ${modelClass}`;

promise = Promise.resolve(promise, label);
promise = _guard(promise, _bind(_objectIsAlive, store));

return promise.then(adapterPayload => {
let serializerToken = heimdall.start('initial-serializerFor-lookup');
let serializer = serializerForAdapter(store, adapter, modelName);
heimdall.stop(serializerToken);
let normalizeToken = heimdall.start('finders#_query::normalizeResponseHelper');
let payload = normalizeResponseHelper(serializer, store, modelClass, adapterPayload, null, 'query');
heimdall.stop(normalizeToken);
Expand All @@ -168,13 +168,13 @@ export function _query(adapter, store, modelName, query, recordArray) {
export function _queryRecord(adapter, store, modelName, query) {
let modelClass = store.modelFor(modelName); // adapter.queryRecord needs the class
let promise = adapter.queryRecord(store, modelClass, query);
let serializer = serializerForAdapter(store, adapter, modelName);
let label = `DS: Handle Adapter#queryRecord of ${modelName}`;

promise = Promise.resolve(promise, label);
promise = _guard(promise, _bind(_objectIsAlive, store));

return promise.then(adapterPayload => {
let serializer = serializerForAdapter(store, adapter, modelName);
let payload = normalizeResponseHelper(serializer, store, modelClass, adapterPayload, null, 'queryRecord');

assert(`Expected the primary data returned by the serializer for a 'queryRecord' response to be a single object or null but instead it was an array.`, !Array.isArray(payload.data), {
Expand Down
285 changes: 285 additions & 0 deletions tests/unit/store/finders-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,285 @@
import setupStore from 'dummy/tests/helpers/store';
import Ember from 'ember';

import {module, test} from 'qunit';

const { run } = Ember;
const { defer } = Ember.RSVP;

import DS from 'ember-data';

module('unit/store/finders', {
beforeEach() {
this.Person = DS.Model.extend({
updatedAt: DS.attr('string'),
name: DS.attr('string'),
firstName: DS.attr('string'),
lastName: DS.attr('string')
});

this.Dog = DS.Model.extend({
name: DS.attr('string')
});

this.env = setupStore({ person: this.Person, dog: this.Dog });
this.store = this.env.store;
this.adapter = this.env.adapter;
},

afterEach() {
run(this.env.container, 'destroy');
}
});

test('findRecord does not load a serializer until the adapter promise resolves', function(assert) {
assert.expect(2);

let deferedFind = defer();

this.env.registry.register('adapter:person', DS.Adapter.extend({
findRecord: () => deferedFind.promise
}));

let serializerLoaded = false;
let serializerFor = this.store.serializerFor;
this.store.serializerFor = (modelName) => {
if (modelName === 'person') {
serializerLoaded = true;
}
return serializerFor.call(this.store, modelName);
};

let storePromise = run(() => this.store.findRecord('person', 1));
assert.equal(false, serializerLoaded, 'serializer is not eagerly loaded');

return run(() => {
deferedFind.resolve({ id: 1, name: 'John Churchill' });
return storePromise.then(() => {
assert.equal(true, serializerLoaded, 'serializer is loaded');
});
});
});

test('findMany does not load a serializer until the adapter promise resolves', function(assert) {
assert.expect(2);

let deferedFind = defer();

this.env.registry.register('adapter:person', DS.Adapter.extend({
findMany: () => deferedFind.promise
}));

let serializerLoaded = false;
let serializerFor = this.store.serializerFor;
this.store.serializerFor = (modelName) => {
if (modelName === 'person') {
serializerLoaded = true;
}
return serializerFor.call(this.store, modelName);
};

let storePromise = run(() => {
this.store.findRecord('person', 1)
return this.store.findRecord('person', 2);
});
assert.equal(false, serializerLoaded, 'serializer is not eagerly loaded');

return run(() => {
deferedFind.resolve([{ id: 1, name: 'John Churchill' }, { id: 2, name: 'Louis Joseph' }]);
return storePromise.then(() => {
assert.equal(true, serializerLoaded, 'serializer is loaded');
});
});
});

test('findHasMany does not load a serializer until the adapter promise resolves', function(assert) {
assert.expect(2);

let deferedFind = defer();

this.env.registry.register('adapter:person', DS.Adapter.extend({
findHasMany: () => deferedFind.promise
}));

this.Person.reopen({
dogs: DS.hasMany('dog', { async: true })
});

let serializerLoaded = false;
let serializerFor = this.store.serializerFor;
this.store.serializerFor = (modelName) => {
if (modelName === 'dog') {
serializerLoaded = true;
}
return serializerFor.call(this.store, modelName);
};

let storePromise = run(() => {
this.env.store.push({
data: {
type: 'person',
id: '1',
attributes: {
name: 'John Churchill'
},
relationships: {
dogs: {
links: {
related: 'http://exmaple.com/person/1/dogs'
}
}
}
}
});

return this.store.peekRecord('person', 1).get('dogs');
});
assert.equal(false, serializerLoaded, 'serializer is not eagerly loaded');

return run(() => {
deferedFind.resolve([{ id: 1, name: 'Scooby' }, { id: 2, name: 'Scrappy' }]);
return storePromise.then(() => {
assert.equal(true, serializerLoaded, 'serializer is loaded');
});
});
});

test('findBelongsTo does not load a serializer until the adapter promise resolves', function(assert) {
assert.expect(2);

let deferedFind = defer();

this.env.registry.register('adapter:person', DS.Adapter.extend({
findBelongsTo: () => deferedFind.promise
}));

this.Person.reopen({
favoriteDog: DS.belongsTo('dog', { async: true })
});

let serializerLoaded = false;
let serializerFor = this.store.serializerFor;
this.store.serializerFor = (modelName) => {
if (modelName === 'dog') {
serializerLoaded = true;
}
return serializerFor.call(this.store, modelName);
};

let storePromise = run(() => {
this.env.store.push({
data: {
type: 'person',
id: '1',
attributes: {
name: 'John Churchill'
},
relationships: {
favoriteDog: {
links: {
related: 'http://exmaple.com/person/1/favorite-dog'
}
}
}
}
});

return this.store.peekRecord('person', 1).get('favoriteDog');
});
assert.equal(false, serializerLoaded, 'serializer is not eagerly loaded');

return run(() => {
deferedFind.resolve({ id: 1, name: 'Scooby' });
return storePromise.then(() => {
assert.equal(true, serializerLoaded, 'serializer is loaded');
});
});
});

test('findAll does not load a serializer until the adapter promise resolves', function(assert) {
assert.expect(2);

let deferedFind = defer();

this.env.registry.register('adapter:person', DS.Adapter.extend({
findAll: () => deferedFind.promise
}));

let serializerLoaded = false;
let serializerFor = this.store.serializerFor;
this.store.serializerFor = (modelName) => {
if (modelName === 'person') {
serializerLoaded = true;
}
return serializerFor.call(this.store, modelName);
};

let storePromise = run(() => this.store.findAll('person'));
assert.equal(false, serializerLoaded, 'serializer is not eagerly loaded');

return run(() => {
deferedFind.resolve([{ id: 1, name: 'John Churchill' }]);
return storePromise.then(() => {
assert.equal(true, serializerLoaded, 'serializer is loaded');
});
});
});

test('query does not load a serializer until the adapter promise resolves', function(assert) {
assert.expect(2);

let deferedFind = defer();

this.env.registry.register('adapter:person', DS.Adapter.extend({
query: () => deferedFind.promise
}));

let serializerLoaded = false;
let serializerFor = this.store.serializerFor;
this.store.serializerFor = (modelName) => {
if (modelName === 'person') {
serializerLoaded = true;
}
return serializerFor.call(this.store, modelName);
};

let storePromise = run(() => this.store.query('person', { first_duke_of_marlborough: true }));
assert.equal(false, serializerLoaded, 'serializer is not eagerly loaded');

return run(() => {
deferedFind.resolve([{ id: 1, name: 'John Churchill' }]);
return storePromise.then(() => {
assert.equal(true, serializerLoaded, 'serializer is loaded');
});
});
});

test('queryRecord does not load a serializer until the adapter promise resolves', function(assert) {
assert.expect(2);

let deferedFind = defer();

this.env.registry.register('adapter:person', DS.Adapter.extend({
queryRecord: () => deferedFind.promise
}));

let serializerLoaded = false;
let serializerFor = this.store.serializerFor;
this.store.serializerFor = (modelName) => {
if (modelName === 'person') {
serializerLoaded = true;
}
return serializerFor.call(this.store, modelName);
};

let storePromise = run(() => this.store.queryRecord('person', { first_duke_of_marlborough: true }));
assert.equal(false, serializerLoaded, 'serializer is not eagerly loaded');

return run(() => {
deferedFind.resolve({ id: 1, name: 'John Churchill' });
return storePromise.then(() => {
assert.equal(true, serializerLoaded, 'serializer is loaded');
});
});
});