From cf9f70a7a3de972910bb5c77c306b1def6b4b479 Mon Sep 17 00:00:00 2001 From: bmac Date: Tue, 9 May 2017 22:25:38 -0400 Subject: [PATCH] Trap exceptions that are thrown from adapter methods and reject the store promise with the exception. Closes #4945 --- addon/-private/system/store.js | 2 +- addon/-private/system/store/finders.js | 10 ++-- tests/integration/store-test.js | 66 ++++++++++++++++++++++++++ 3 files changed, 72 insertions(+), 6 deletions(-) diff --git a/addon/-private/system/store.js b/addon/-private/system/store.js index 40fa112405f..78f284a0dc1 100644 --- a/addon/-private/system/store.js +++ b/addon/-private/system/store.js @@ -2869,7 +2869,7 @@ function _commit(adapter, store, operation, snapshot) { let modelClass = store._modelFor(modelName); assert(`You tried to update a record but you have no adapter (for ${modelName})`, adapter); assert(`You tried to update a record but your adapter (for ${modelName}) does not implement '${operation}'`, typeof adapter[operation] === 'function'); - let promise = adapter[operation](store, modelClass, snapshot); + let promise = Promise.resolve().then(() => adapter[operation](store, modelClass, snapshot)); let serializer = serializerForAdapter(store, adapter, modelName); let label = `DS: Extract and notify about ${operation} completion of ${internalModel}`; diff --git a/addon/-private/system/store/finders.js b/addon/-private/system/store/finders.js index 0103a0084a1..414833edb07 100644 --- a/addon/-private/system/store/finders.js +++ b/addon/-private/system/store/finders.js @@ -21,7 +21,7 @@ function payloadIsNotBlank(adapterPayload) { 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 promise = Promise.resolve().then(() => adapter.findRecord(store, modelClass, id, snapshot)); let label = `DS: Handle Adapter#findRecord of '${modelName}' with id: '${id}'`; promise = Promise.resolve(promise, label); @@ -116,7 +116,7 @@ export function _findAll(adapter, store, modelName, sinceToken, options) { let modelClass = store.modelFor(modelName); // adapter.findAll depends on the class let recordArray = store.peekAll(modelName); let snapshotArray = recordArray._createSnapshot(options); - let promise = adapter.findAll(store, modelClass, sinceToken, snapshotArray); + let promise = Promise.resolve().then(() => adapter.findAll(store, modelClass, sinceToken, snapshotArray)); let label = "DS: Handle Adapter#findAll of " + modelClass; promise = Promise.resolve(promise, label); @@ -140,9 +140,9 @@ export function _query(adapter, store, modelName, query, recordArray) { let promise; if (adapter.query.length > 3) { recordArray = recordArray || store.recordArrayManager.createAdapterPopulatedRecordArray(modelName, query); - promise = adapter.query(store, modelClass, query, recordArray); + promise = Promise.resolve().then(() => adapter.query(store, modelClass, query, recordArray)); } else { - promise = adapter.query(store, modelClass, query); + promise = Promise.resolve().then(() => adapter.query(store, modelClass, query)); } let label = `DS: Handle Adapter#query of ${modelClass}`; @@ -172,7 +172,7 @@ 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 promise = Promise.resolve().then(() => adapter.queryRecord(store, modelClass, query)); let label = `DS: Handle Adapter#queryRecord of ${modelName}`; promise = Promise.resolve(promise, label); diff --git a/tests/integration/store-test.js b/tests/integration/store-test.js index bf33dc85f4e..21cc58a623a 100644 --- a/tests/integration/store-test.js +++ b/tests/integration/store-test.js @@ -914,4 +914,70 @@ testInDebug('store#queryRecord should assert when normalized payload of adapter }, /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./); }); +test('The store should trap exceptions that are thrown from adapter#findRecord', function(assert) { + assert.expect(1) + env.adapter.findRecord = function() { + throw new Error('Refusing to find record'); + }; + + run(() => { + store.findRecord('car', 1).catch(error => { + assert.equal(error.message, 'Refusing to find record') + }) + }); +}); +test('The store should trap exceptions that are thrown from adapter#findAll', function(assert) { + assert.expect(1) + env.adapter.findAll = function() { + throw new Error('Refusing to find all records'); + }; + + run(() => { + store.findAll('car').catch(error => { + assert.equal(error.message, 'Refusing to find all records') + }) + }); +}); + +test('The store should trap exceptions that are thrown from adapter#query', function(assert) { + assert.expect(1) + env.adapter.query = function() { + throw new Error('Refusing to query records'); + }; + + run(() => { + store.query('car', {}).catch(error => { + assert.equal(error.message, 'Refusing to query records') + }) + }); +}); + +test('The store should trap exceptions that are thrown from adapter#queryRecord', function(assert) { + assert.expect(1) + env.adapter.queryRecord = function() { + throw new Error('Refusing to query record'); + }; + + run(() => { + store.queryRecord('car', {}).catch(error => { + assert.equal(error.message, 'Refusing to query record') + }) + }); +}); + + +test('The store should trap exceptions that are thrown from adapter#createRecord', function(assert) { + assert.expect(1) + env.adapter.createRecord = function() { + throw new Error('Refusing to serialize'); + }; + + run(() => { + let car = store.createRecord('car') + + car.save().catch(error => { + assert.equal(error.message, 'Refusing to serialize') + }) + }); +});