Merge pull request #72 from hashicorp/fix-empty-list-responses

fix: 🐛 prenormalize findAll responses in application adapter
This commit is contained in:
Susmitha Girumala authored and GitHub committed 2020-06-25 17:40:30 -04:00
commit 0ba19e8bed
7 files changed
+119

No files matched your search

+50
View File
@@ -6,6 +6,36 @@ import { dasherize } from '@ember/string';
import { pluralize } from 'ember-inflector';
import { isArray } from '@ember/array';
/**
* Returns true if the payload is an empty object, false otherwise.
* Taken from Ember Data fetch-manager and "positivized" the name.
* @param {object} adapterPayload
* @return {boolean}
*/
function payloadIsBlank(adapterPayload) {
if (Array.isArray(adapterPayload)) {
return false;
} else {
return Object.keys(adapterPayload || {}).length === 0;
}
}
/**
* If the resonse is empty, returns a new payload containing an empty `items`
* array, as expected by the serializer. Otherwise, returns the response.
*
* Normally this would be performed 100% inside the serializer.
* However, the fetch manager, which we cannot easily override, glues the
* adapter and serializers together. Before it forwards a response from the
* adapter on to the serializer, it checks for an "empty" response. If the
* response is empty, fetch manager throws and error and never forwards it on.
* @param {object} response
* @return {object}
*/
function prenormalizeArrayResponse(response) {
return payloadIsBlank(response) ? {items: []} : response;
}
export default class ApplicationAdapter extends RESTAdapter {
// =attributes
@@ -36,6 +66,26 @@ export default class ApplicationAdapter extends RESTAdapter {
return dasherize(pluralize(type));
}
/**
* Intercepts "empty" responses and adds an empty `items` array.
* @override
* @method findAll
* @return {Promise} promise
*/
findAll() {
return super.findAll(...arguments).then(prenormalizeArrayResponse);
}
/**
* Intercepts "empty" responses and adds an empty `items` array.
* @override
* @method query
* @return {Promise} promise
*/
query() {
return super.query(...arguments).then(prenormalizeArrayResponse);
}
/**
* Overrides default ajax method by rewritting PUT requests as PATCH.
* PATCH is the request method used by our API for updates.
+1
View File
@@ -35,6 +35,7 @@
"ember-cli-dependency-checker": "^3.2.0",
"ember-cli-eslint": "^5.1.0",
"ember-cli-inject-live-reload": "^2.0.2",
"ember-cli-mirage": "^1.1.7",
"ember-cli-sri": "^2.1.1",
"ember-cli-template-lint": "^1.0.0-beta.3",
"ember-cli-uglify": "^3.0.0",
+26
View File
@@ -0,0 +1,26 @@
export default function() {
// These comments are here to help you get started. Feel free to delete them.
/*
Config (with defaults).
Note: these only affect routes defined *after* them!
*/
// this.urlPrefix = ''; // make this `http://localhost:8080`, for example, if your API is on a different server
// this.namespace = ''; // make this `/api`, for example, if your API is namespaced
// this.timing = 400; // delay for each request, automatically set to 0 during testing
/*
Shorthand cheatsheet:
this.get('/posts');
this.post('/posts');
this.get('/posts/:id');
this.put('/posts/:id'); // or this.patch
this.del('/posts/:id');
https://www.ember-cli-mirage.com/docs/route-handlers/shorthands
*/
}
@@ -0,0 +1,9 @@
export default function(/* server */) {
/*
Seed your development database using your factories.
This data will not be loaded in your tests.
*/
// server.createList('post', 10);
}
@@ -0,0 +1,4 @@
import { JSONAPISerializer } from 'ember-cli-mirage';
export default JSONAPISerializer.extend({
});
@@ -3,9 +3,12 @@ import { setupTest } from 'ember-qunit';
import config from 'ember-get-config';
import RESTAdapter from '@ember-data/adapter/rest';
import { InvalidError } from '@ember-data/adapter/error';
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
import { Response } from 'miragejs';
module('Unit | Adapter | application', function (hooks) {
setupTest(hooks);
setupMirage(hooks);
test('its namespace is equal to the configured namespace', function (assert) {
assert.expect(2);
@@ -24,12 +27,23 @@ module('Unit | Adapter | application', function (hooks) {
assert.expect(1);
const adapter = this.owner.lookup('adapter:application');
// TODO this is icky, should be changed to a spy or stub
const originalAjax = RESTAdapter.prototype.ajax;
RESTAdapter.prototype.ajax = (url, type) => {
assert.equal(type, 'PATCH');
RESTAdapter.prototype.ajax = originalAjax;
};
adapter.ajax('/', 'PUT');
});
test('it prenormalizes "empty" responses into a form that the fetch-manager will not reject', async function (assert) {
assert.expect(1);
const adapter = this.owner.lookup('adapter:project');
const store = this.owner.lookup('service:store');
this.server.get('/v1/projects', () => new Response({}));
const prenormalized = await adapter.findAll(store, {modelName: 'project'}, null, []);
assert.deepEqual(prenormalized, {items: []});
});
test('it correctly identifies 400 responses as invalid', function (assert) {
assert.expect(2);
const adapter = this.owner.lookup('adapter:application');
+15
View File
@@ -7330,6 +7330,21 @@ ember-cli-mirage@^1.1.6:
lodash-es "^4.17.11"
miragejs "^0.1.31"
ember-cli-mirage@^1.1.7:
version "1.1.7"
resolved "https://registry.yarnpkg.com/ember-cli-mirage/-/ember-cli-mirage-1.1.7.tgz#846ec8e5199b15fa670da30dfa1708a8ee11e9dc"
integrity sha512-4kVLXXvgHYlv30ChM47U4xYnUtphIoEj+BQwXX0QT7pvD3KQxYnmfZSwlevSEVUy2UhbsBUPw6merENb+dIQ0A==
dependencies:
broccoli-file-creator "^2.1.1"
broccoli-funnel "^3.0.2"
broccoli-merge-trees "^4.2.0"
ember-auto-import "^1.2.19"
ember-cli-babel "^7.5.0"
ember-get-config "^0.2.2"
ember-inflector "^2.0.0 || ^3.0.0"
lodash-es "^4.17.11"
miragejs "^0.1.31"
ember-cli-normalize-entity-name@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/ember-cli-normalize-entity-name/-/ember-cli-normalize-entity-name-1.0.0.tgz#0b14f7bcbc599aa117b5fddc81e4fd03c4bad5b7"