diff --git a/addons/api/addon/adapters/application.js b/addons/api/addon/adapters/application.js index 214b80d3c..bfc109b3f 100644 --- a/addons/api/addon/adapters/application.js +++ b/addons/api/addon/adapters/application.js @@ -71,10 +71,16 @@ export default class ApplicationAdapter extends RESTAdapter.extend( */ urlPrefix(path, parentURL, modelName, id, snapshot={}) { const prefix = super.urlPrefix(...arguments); - let scopePath = getWithDefault(snapshot, 'adapterOptions.scopeID', ''); - // Ensure a slash is added between prefix + scope path if needed. - if (scopePath && prefix.charAt(prefix.length - 1) !== '/') { - scopePath = `/scopes/${scopePath}`; + const isScope = modelName === 'scope'; + let scopePath = ''; + // Only non-scope resources need a scope path, since scope resources + // aren't technically "scoped" the same way. + if (!isScope) { + scopePath = getWithDefault(snapshot, 'adapterOptions.scopeID', ''); + // Ensure a slash is added between prefix + scope path if needed. + if (scopePath && prefix.charAt(prefix.length - 1) !== '/') { + scopePath = `/scopes/${scopePath}`; + } } return `${prefix}${scopePath}`; } diff --git a/addons/api/addon/serializers/application.js b/addons/api/addon/serializers/application.js index 84bcc6ed5..c839eb78c 100644 --- a/addons/api/addon/serializers/application.js +++ b/addons/api/addon/serializers/application.js @@ -1,5 +1,6 @@ import RESTSerializer from '@ember-data/serializer/rest'; import { underscore } from '@ember/string'; +import { get } from '@ember/object'; import { copy } from 'ember-copy'; /** @@ -117,7 +118,7 @@ export default class ApplicationSerializer extends RESTSerializer { primaryModelClass.modelName ); // Copy the unrooted payload under the expected root key name. - transformedPayload[payloadKey] = copy(payload); + transformedPayload[payloadKey] = copy(payload, true); // Return the result of normalizing the transformed payload. return super.normalizeSingleResponse( store, @@ -128,4 +129,21 @@ export default class ApplicationSerializer extends RESTSerializer { ); } + /** + * One consequence of using model fragments to represent the embedded scope + * is that they cannot have `id` fields. We still need to know scope IDs, + * so we copy the `scope.id` value into the `scope.scope_id` field. + * @override + * @see FragmentScope + * @param {Model} typeClass + * @param {Object} hash + * @return {Object} + */ + normalize(typeClass, hash) { + const normalizedHash = copy(hash, true); + const scopeID = get(normalizedHash, 'scope.id'); + if (scopeID) normalizedHash.scope.scope_id = scopeID; + return super.normalize(typeClass, normalizedHash); + } + } diff --git a/addons/api/tests/unit/adapters/application-test.js b/addons/api/tests/unit/adapters/application-test.js index 4ea1b2ec5..bea9b215c 100644 --- a/addons/api/tests/unit/adapters/application-test.js +++ b/addons/api/tests/unit/adapters/application-test.js @@ -35,6 +35,16 @@ module('Unit | Adapter | application', function (hooks) { assert.equal(prefix, `v1/scopes/${scopeID}`); }); + test('it does not prefixes URLs with `scopeID` for scope resources', function (assert) { + assert.expect(1); + const scopeID = 'global'; + const adapter = this.owner.lookup('adapter:application'); + const prefix = adapter.urlPrefix(null, null, 'scope', null, { + adapterOptions: {scopeID} + }); + assert.equal(prefix, `v1`); + }); + test('it generates correct default URL suffixes', function (assert) { assert.expect(1); const adapter = this.owner.lookup('adapter:application'); diff --git a/addons/api/tests/unit/serializers/application-test.js b/addons/api/tests/unit/serializers/application-test.js index 83c75341b..4769cfa3f 100644 --- a/addons/api/tests/unit/serializers/application-test.js +++ b/addons/api/tests/unit/serializers/application-test.js @@ -36,9 +36,9 @@ module('Unit | Serializer | application', function (hooks) { const userModelClass = store.createRecord('user').constructor; const payload = { items: [ - { id: 1, name: 'User 1' }, - { id: 2, name: 'User 2' }, - { id: 3, name: 'User 3' }, + { id: 1, name: 'User 1', scope: { id: 'o_123' } }, + { id: 2, name: 'User 2', scope: { id: 'o_123' } }, + { id: 3, name: 'User 3', scope: { id: 'o_123' } }, ], }; const normalizedArray = serializer.normalizeArrayResponse( @@ -52,19 +52,19 @@ module('Unit | Serializer | application', function (hooks) { { id: '1', type: 'user', - attributes: { name: 'User 1' }, + attributes: { name: 'User 1', scope: { scope_id: 'o_123' } }, relationships: {}, }, { id: '2', type: 'user', - attributes: { name: 'User 2' }, + attributes: { name: 'User 2', scope: { scope_id: 'o_123' } }, relationships: {}, }, { id: '3', type: 'user', - attributes: { name: 'User 3' }, + attributes: { name: 'User 3', scope: { scope_id: 'o_123' } }, relationships: {}, }, ], @@ -79,6 +79,7 @@ module('Unit | Serializer | application', function (hooks) { const payload = { id: '1', name: 'User 1', + scope: { id: 'o_123' } }; const normalized = serializer.normalizeSingleResponse( store, @@ -90,7 +91,7 @@ module('Unit | Serializer | application', function (hooks) { data: { id: '1', type: 'user', - attributes: { name: 'User 1' }, + attributes: { name: 'User 1', scope: { scope_id: 'o_123' } }, relationships: {}, }, });