From eb4b29ac1b1d5fd77e204e91611dd1186d5b86f3 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Tue, 14 Jul 2020 18:47:35 -0400 Subject: [PATCH] =?UTF-8?q?chore:=20=F0=9F=A4=96=20introduce=20Scope=20mod?= =?UTF-8?q?el=20with=20self=20relationships?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- addons/api/addon/generated/models/scope.js | 37 +++++++++++++++++++ addons/api/addon/models/scope.js | 20 ++++++++++ addons/api/addon/serializers/application.js | 9 +++++ addons/api/app/models/scope.js | 1 + addons/api/tests/unit/models/scope-test.js | 41 +++++++++++++++++++++ 5 files changed, 108 insertions(+) create mode 100644 addons/api/addon/generated/models/scope.js create mode 100644 addons/api/addon/models/scope.js create mode 100644 addons/api/app/models/scope.js create mode 100644 addons/api/tests/unit/models/scope-test.js diff --git a/addons/api/addon/generated/models/scope.js b/addons/api/addon/generated/models/scope.js new file mode 100644 index 000000000..a7448b034 --- /dev/null +++ b/addons/api/addon/generated/models/scope.js @@ -0,0 +1,37 @@ +import BaseModel from '../../models/base'; +import { attr } from '@ember-data/model'; + +/** + * + */ +export default class GeneratedScopeModel extends BaseModel { + + // =attributes + + @attr('string', { + description: 'The type of the resource' + }) type; + + @attr('string', { + description: 'Optional name for identification purposes', + }) + name; + + @attr('string', { + description: 'Optional user-set description for identification purposes', + }) + description; + + @attr('date', { + description: 'The time this resource was created\nOutput only.', + readOnly: true, + }) + created_time; + + @attr('date', { + description: 'The time this resource was last updated\nOutput only.', + readOnly: true, + }) + updated_time; + +} diff --git a/addons/api/addon/models/scope.js b/addons/api/addon/models/scope.js new file mode 100644 index 000000000..abf1824eb --- /dev/null +++ b/addons/api/addon/models/scope.js @@ -0,0 +1,20 @@ +import GeneratedScopeModel from '../generated/models/scope'; +import { belongsTo, hasMany } from '@ember-data/model'; + +export default class ScopeModel extends GeneratedScopeModel { + + // =relationships + + /** + * The parent scope, if any. + * @type {ScopeModel} + */ + @belongsTo('scope', {inverse: 'childrenScopes'}) parentScope; + + /** + * The inverse of parent scope, children. + * @type {ScopeModelp[]} + */ + @hasMany('scope', {inverse: 'parentScope'}) childrenScopes; + +} diff --git a/addons/api/addon/serializers/application.js b/addons/api/addon/serializers/application.js index d5b1e333e..60d386ba3 100644 --- a/addons/api/addon/serializers/application.js +++ b/addons/api/addon/serializers/application.js @@ -17,6 +17,15 @@ export default class ApplicationSerializer extends RESTSerializer { return underscore(attr); } + /** + * Generates an underscored key for the relationship ending in `_id`. + * @param {string} rel + * @return {string} + */ + keyForRelationship(rel) { + return `${underscore(rel)}_id`; + } + /** * If an attribute is annotated as readOnly in the model, don't serialize it. * Otherwise delegate to default attribute serializer. diff --git a/addons/api/app/models/scope.js b/addons/api/app/models/scope.js new file mode 100644 index 000000000..66442e397 --- /dev/null +++ b/addons/api/app/models/scope.js @@ -0,0 +1 @@ +export { default } from 'api/models/scope'; diff --git a/addons/api/tests/unit/models/scope-test.js b/addons/api/tests/unit/models/scope-test.js new file mode 100644 index 000000000..2e6fcdd60 --- /dev/null +++ b/addons/api/tests/unit/models/scope-test.js @@ -0,0 +1,41 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; +import setupMirage from 'ember-cli-mirage/test-support/setup-mirage'; + +module('Unit | Model | scope', function(hooks) { + setupTest(hooks); + setupMirage(hooks); + + test('it exists', function(assert) { + let store = this.owner.lookup('service:store'); + let model = store.createRecord('scope', {}); + assert.ok(model); + }); + + test('it has self-referential parent and child relationships', async function(assert) { + assert.expect(9); + const store = this.owner.lookup('service:store'); + this.server.get('/v1/scopes', () => ({ + items: [ + {id: 'global', type: 'global'}, + {id: 'o_1', type: 'org', parent_scope_id: 'global'}, + {id: 'o_2', type: 'org', parent_scope_id: 'global'}, + {id: 'p_1', type: 'project', parent_scope_id: 'o_1'}, + {id: 'p_2', type: 'project', parent_scope_id: 'o_1'}, + {id: 'p_3', type: 'project', parent_scope_id: 'o_2'} + ] + })); + const scopes = await store.findAll('scope'); + // check integrity of scope relationships + assert.notOk(await scopes.firstObject.get('parentScope'), 'Global scope has no parent'); + assert.equal(await scopes.firstObject.get('childrenScopes.length'), 2, 'Global scope has two children scopes'); + assert.equal(await scopes.objectAt(1).get('parentScope.id'), 'global', 'Org 1 parent scope is global'); + assert.equal(await scopes.objectAt(2).get('parentScope.id'), 'global', 'Org 2 parent scope is global'); + assert.equal(await scopes.objectAt(1).get('childrenScopes.length'), 2, 'Org 1 has two children scopes'); + assert.equal(await scopes.objectAt(2).get('childrenScopes.length'), 1, 'Org 2 has one child scope'); + assert.equal(await scopes.objectAt(3).get('parentScope.id'), 'o_1', 'Project 1 parent scope is org 1'); + assert.equal(await scopes.objectAt(4).get('parentScope.id'), 'o_1', 'Project 2 parent scope is org 1'); + assert.equal(await scopes.objectAt(5).get('parentScope.id'), 'o_2', 'Project 3 parent scope is org 2'); + }); + +});