From eb4b29ac1b1d5fd77e204e91611dd1186d5b86f3 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Tue, 14 Jul 2020 18:47:35 -0400 Subject: [PATCH 01/70] =?UTF-8?q?chore:=20=F0=9F=A4=96=20introduce=20Scope?= =?UTF-8?q?=20model=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'); + }); + +}); From a43efaddfaa9e68d9e1c71d7940a6aaa86415bb6 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Wed, 15 Jul 2020 09:43:30 -0400 Subject: [PATCH 02/70] =?UTF-8?q?chore:=20=F0=9F=A4=96=20add=20isType=20co?= =?UTF-8?q?nvenience=20boolean=20getters/setters?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- addons/api/addon/models/scope.js | 41 ++++++++++++++++++++++ addons/api/tests/unit/models/scope-test.js | 22 ++++++++++-- 2 files changed, 61 insertions(+), 2 deletions(-) diff --git a/addons/api/addon/models/scope.js b/addons/api/addon/models/scope.js index abf1824eb..b62ca94ba 100644 --- a/addons/api/addon/models/scope.js +++ b/addons/api/addon/models/scope.js @@ -1,5 +1,12 @@ import GeneratedScopeModel from '../generated/models/scope'; import { belongsTo, hasMany } from '@ember-data/model'; +import { computed } from '@ember/object'; + +export const scopeTypes = { + global: 'global', + org: 'org', + project: 'project' +}; export default class ScopeModel extends GeneratedScopeModel { @@ -17,4 +24,38 @@ export default class ScopeModel extends GeneratedScopeModel { */ @hasMany('scope', {inverse: 'parentScope'}) childrenScopes; + // =computed + + /** + * @type {boolean} + */ + @computed('type') + get isGlobal() { + return this.type === scopeTypes.global; + } + // There is only one global scope and it cannot be created by clients, + // thus no set. + + /** + * @type {boolean} + */ + @computed('type') + get isOrg() { + return this.type === scopeTypes.org; + } + set isOrg(value) { + if (value) this.type = scopeTypes.org; + } + + /** + * @type {boolean} + */ + @computed('type') + get isProject() { + return this.type === scopeTypes.project; + } + set isProject(value) { + if (value) this.type = scopeTypes.project; + } + } diff --git a/addons/api/tests/unit/models/scope-test.js b/addons/api/tests/unit/models/scope-test.js index 2e6fcdd60..07cf265ec 100644 --- a/addons/api/tests/unit/models/scope-test.js +++ b/addons/api/tests/unit/models/scope-test.js @@ -7,8 +7,9 @@ module('Unit | Model | scope', function(hooks) { setupMirage(hooks); test('it exists', function(assert) { - let store = this.owner.lookup('service:store'); - let model = store.createRecord('scope', {}); + assert.expect(1); + const store = this.owner.lookup('service:store'); + const model = store.createRecord('scope', {}); assert.ok(model); }); @@ -38,4 +39,21 @@ module('Unit | Model | scope', function(hooks) { assert.equal(await scopes.objectAt(5).get('parentScope.id'), 'o_2', 'Project 3 parent scope is org 2'); }); + test('it has isType boolean getters and setters', async function(assert) { + assert.expect(9); + const store = this.owner.lookup('service:store'); + const model = store.createRecord('scope', {type: 'global'}); + assert.ok(model.isGlobal); + assert.notOk(model.isOrg); + assert.notOk(model.isProject); + model.isOrg = true; + assert.notOk(model.isGlobal); + assert.ok(model.isOrg); + assert.notOk(model.isProject); + model.isProject = true; + assert.notOk(model.isGlobal); + assert.notOk(model.isOrg); + assert.ok(model.isProject); + }); + }); From 9261941c1bb553866bcd08cfd006c84ec34496a6 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Wed, 15 Jul 2020 11:08:59 -0400 Subject: [PATCH 03/70] chore: rip out scope service --- addons/api/addon/adapters/auth-method.js | 16 --- addons/api/addon/adapters/group.js | 14 -- addons/api/addon/adapters/host-catalog.js | 6 - addons/api/addon/adapters/project.js | 14 -- addons/api/addon/adapters/role.js | 14 -- addons/api/addon/adapters/user.js | 14 -- addons/api/addon/services/scope.js | 123 ------------------ addons/api/app/adapters/auth-method.js | 1 - addons/api/app/adapters/group.js | 1 - addons/api/app/adapters/host-catalog.js | 1 - addons/api/app/adapters/project.js | 1 - addons/api/app/adapters/role.js | 1 - addons/api/app/adapters/user.js | 1 - addons/api/app/services/scope.js | 1 - .../api/tests/dummy/app/routes/application.js | 3 - .../tests/unit/adapters/application-test.js | 2 +- .../tests/unit/adapters/auth-method-test.js | 16 --- addons/api/tests/unit/adapters/group-test.js | 21 --- .../tests/unit/adapters/host-catalog-test.js | 11 -- .../api/tests/unit/adapters/project-test.js | 16 --- addons/api/tests/unit/adapters/role-test.js | 16 --- addons/api/tests/unit/adapters/user-test.js | 21 --- .../tests/unit/mixins/scope-adapter-test.js | 10 +- addons/api/tests/unit/services/scope-test.js | 66 ---------- 24 files changed, 6 insertions(+), 384 deletions(-) delete mode 100644 addons/api/addon/adapters/auth-method.js delete mode 100644 addons/api/addon/adapters/group.js delete mode 100644 addons/api/addon/adapters/host-catalog.js delete mode 100644 addons/api/addon/adapters/project.js delete mode 100644 addons/api/addon/adapters/role.js delete mode 100644 addons/api/addon/adapters/user.js delete mode 100644 addons/api/addon/services/scope.js delete mode 100644 addons/api/app/adapters/auth-method.js delete mode 100644 addons/api/app/adapters/group.js delete mode 100644 addons/api/app/adapters/host-catalog.js delete mode 100644 addons/api/app/adapters/project.js delete mode 100644 addons/api/app/adapters/role.js delete mode 100644 addons/api/app/adapters/user.js delete mode 100644 addons/api/app/services/scope.js delete mode 100644 addons/api/tests/unit/adapters/auth-method-test.js delete mode 100644 addons/api/tests/unit/adapters/group-test.js delete mode 100644 addons/api/tests/unit/adapters/host-catalog-test.js delete mode 100644 addons/api/tests/unit/adapters/project-test.js delete mode 100644 addons/api/tests/unit/adapters/role-test.js delete mode 100644 addons/api/tests/unit/adapters/user-test.js delete mode 100644 addons/api/tests/unit/services/scope-test.js diff --git a/addons/api/addon/adapters/auth-method.js b/addons/api/addon/adapters/auth-method.js deleted file mode 100644 index 2da687841..000000000 --- a/addons/api/addon/adapters/auth-method.js +++ /dev/null @@ -1,16 +0,0 @@ -import ApplicationAdapter from './application'; -import ScopeAdapterMixin from '../mixins/scope-adapter'; - -export default class AuthMethodAdapter extends ApplicationAdapter.extend( - ScopeAdapterMixin -) { - - // =attributes - - /** - * Auth method is scoped to org only. - * @type {boolean} - */ - includeProject = false; - -} diff --git a/addons/api/addon/adapters/group.js b/addons/api/addon/adapters/group.js deleted file mode 100644 index b077634f4..000000000 --- a/addons/api/addon/adapters/group.js +++ /dev/null @@ -1,14 +0,0 @@ -import ApplicationAdapter from './application'; -import ScopeAdapterMixin from '../mixins/scope-adapter'; - -export default class GroupAdapter extends ApplicationAdapter.extend( - ScopeAdapterMixin -) { - // =attributes - - /** - * Group is scoped to org only, for now. - * @type {boolean} - */ - includeProject = false; -} diff --git a/addons/api/addon/adapters/host-catalog.js b/addons/api/addon/adapters/host-catalog.js deleted file mode 100644 index 79d4b9444..000000000 --- a/addons/api/addon/adapters/host-catalog.js +++ /dev/null @@ -1,6 +0,0 @@ -import ApplicationAdapter from './application'; -import ScopeAdapterMixin from '../mixins/scope-adapter'; - -export default class HostCatalogAdapter extends ApplicationAdapter.extend( - ScopeAdapterMixin -) { } diff --git a/addons/api/addon/adapters/project.js b/addons/api/addon/adapters/project.js deleted file mode 100644 index a3a02d3e7..000000000 --- a/addons/api/addon/adapters/project.js +++ /dev/null @@ -1,14 +0,0 @@ -import ApplicationAdapter from './application'; -import ScopeAdapterMixin from '../mixins/scope-adapter'; - -export default class ProjectAdapter extends ApplicationAdapter.extend( - ScopeAdapterMixin -) { - // =attributes - - /** - * Project is scoped to org only, technically. - * @type {boolean} - */ - includeProject = false; -} diff --git a/addons/api/addon/adapters/role.js b/addons/api/addon/adapters/role.js deleted file mode 100644 index c9b0c13b0..000000000 --- a/addons/api/addon/adapters/role.js +++ /dev/null @@ -1,14 +0,0 @@ -import ApplicationAdapter from './application'; -import ScopeAdapterMixin from '../mixins/scope-adapter'; - -export default class RoleAdapter extends ApplicationAdapter.extend( - ScopeAdapterMixin -) { - // =attributes - - /** - * Role is scoped to org only, for now. - * @type {boolean} - */ - includeProject = false; -} diff --git a/addons/api/addon/adapters/user.js b/addons/api/addon/adapters/user.js deleted file mode 100644 index b32743c79..000000000 --- a/addons/api/addon/adapters/user.js +++ /dev/null @@ -1,14 +0,0 @@ -import ApplicationAdapter from './application'; -import ScopeAdapterMixin from '../mixins/scope-adapter'; - -export default class UserAdapter extends ApplicationAdapter.extend( - ScopeAdapterMixin -) { - // =attributes - - /** - * Project is scoped to org only, technically. - * @type {boolean} - */ - includeProject = false; -} diff --git a/addons/api/addon/services/scope.js b/addons/api/addon/services/scope.js deleted file mode 100644 index 370bdfb8c..000000000 --- a/addons/api/addon/services/scope.js +++ /dev/null @@ -1,123 +0,0 @@ -import Service from '@ember/service'; -import { tracked } from '@glimmer/tracking'; -import { computed } from '@ember/object'; - -/** - * This module includes two parts: - * - * - `Scope` class: a data structure representing an org/project tuple - * - `ScopeService`: manages the scope and exposes it to the application - */ - -/** - * Represents a _scope_, which is defined as an organization/project tuple. - * This class is intended as a simple data structure and is used by the - * scope service (see below). - * Immutable. - */ -class Scope { - /** - * @type {?object} - * @private - */ - #org = null; - - /** - * @type {?object} - * @private - */ - #project = null; - - /** - * Initializes an instance of `Scope` with optional org and project. - * A scope with only project is invalid and in thi scenario, project will - * be null. - * @param {?object} org - * @param {?object} project - */ - constructor(org = null, project = null) { - if (org) this.#org = org; - if (org && project) this.#project = project; - } - - /** - * Org for this scope. Immutable. - * @type {?object} - */ - get org() { - return this.#org; - } - - /** - * Project for this scope. - * @type {?object} - */ - get project() { - return this.#project; - } - - /** - * Returns the JSON representation of this scope. - * @return {?object} - */ - toJSON() { - const { org, project } = this; - return { org, project }; - } -} - -export { Scope }; - -/** - * The scope service manages the current scope, making it available throughout - * the Ember application. Use `service.org` and `service.project` directly, - * rather than interacting with the internal `scope` member. - * - * @example - * // inject the scope service - * import { inject as service } from '@ember/service'; - * class MyClass { - * @service scope; - * } - * - * // set an org, project, or both - * this.scope.org = {id: 1}; - * this.scope.project = {id: 2}; - * - * // retrieve the org and/or project - * const org = this.scope.org; - * const project = this.scope.project; - */ -export default class ScopeService extends Service { - /** - * The currently selected scope. This is an internal member only. - * @type {?Scope} - */ - @tracked scope = new Scope(); - - /** - * When setting org, `service.org = {...}`, a new scope is instantiated, - * since scopes are immutable. - * @type {?object} - */ - @computed('scope.org') - get org() { - return this.scope ? this.scope.org : null; - } - set org(org) { - this.scope = new Scope(org, this.project); - } - - /** - * When setting project, `service.project = {...}`, a new scope is - * instantiated, since scopes are immutable. - * @type {?object} - */ - @computed('scope.project') - get project() { - return this.scope ? this.scope.project : null; - } - set project(project) { - this.scope = new Scope(this.org, project); - } -} diff --git a/addons/api/app/adapters/auth-method.js b/addons/api/app/adapters/auth-method.js deleted file mode 100644 index 02c325d6e..000000000 --- a/addons/api/app/adapters/auth-method.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from 'api/adapters/auth-method'; diff --git a/addons/api/app/adapters/group.js b/addons/api/app/adapters/group.js deleted file mode 100644 index bb77ff26c..000000000 --- a/addons/api/app/adapters/group.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from 'api/adapters/group'; diff --git a/addons/api/app/adapters/host-catalog.js b/addons/api/app/adapters/host-catalog.js deleted file mode 100644 index 00197a624..000000000 --- a/addons/api/app/adapters/host-catalog.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from 'api/adapters/host-catalog'; diff --git a/addons/api/app/adapters/project.js b/addons/api/app/adapters/project.js deleted file mode 100644 index 0908e069c..000000000 --- a/addons/api/app/adapters/project.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from 'api/adapters/project'; diff --git a/addons/api/app/adapters/role.js b/addons/api/app/adapters/role.js deleted file mode 100644 index f12325923..000000000 --- a/addons/api/app/adapters/role.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from 'api/adapters/role'; diff --git a/addons/api/app/adapters/user.js b/addons/api/app/adapters/user.js deleted file mode 100644 index 78f340b99..000000000 --- a/addons/api/app/adapters/user.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from 'api/adapters/user'; diff --git a/addons/api/app/services/scope.js b/addons/api/app/services/scope.js deleted file mode 100644 index 6c4b4d6c8..000000000 --- a/addons/api/app/services/scope.js +++ /dev/null @@ -1 +0,0 @@ -export { default, Scope } from 'api/services/scope'; diff --git a/addons/api/tests/dummy/app/routes/application.js b/addons/api/tests/dummy/app/routes/application.js index 036973710..4190b97c0 100644 --- a/addons/api/tests/dummy/app/routes/application.js +++ b/addons/api/tests/dummy/app/routes/application.js @@ -1,9 +1,6 @@ import Route from '@ember/routing/route'; -import { inject as service } from '@ember/service'; export default class ApplicationRoute extends Route { - @service scope; - model() { this.store.findAll('project'); } diff --git a/addons/api/tests/unit/adapters/application-test.js b/addons/api/tests/unit/adapters/application-test.js index 366c44c4e..f84fc92ff 100644 --- a/addons/api/tests/unit/adapters/application-test.js +++ b/addons/api/tests/unit/adapters/application-test.js @@ -37,7 +37,7 @@ module('Unit | Adapter | application', function (hooks) { 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 adapter = this.owner.lookup('adapter:application'); const store = this.owner.lookup('service:store'); this.server.get('/v1/projects', () => new Response({})); const prenormalized = await adapter.findAll(store, {modelName: 'project'}, null, []); diff --git a/addons/api/tests/unit/adapters/auth-method-test.js b/addons/api/tests/unit/adapters/auth-method-test.js deleted file mode 100644 index d7c2f176c..000000000 --- a/addons/api/tests/unit/adapters/auth-method-test.js +++ /dev/null @@ -1,16 +0,0 @@ -import { module, test } from 'qunit'; -import { setupTest } from 'ember-qunit'; -import config from 'ember-get-config'; - -module('Unit | Adapter | auth method', function (hooks) { - setupTest(hooks); - - test('it generates URL prefixes without project scope', function (assert) { - assert.expect(1); - const service = this.owner.lookup('service:scope'); - service.org = { id: 1 }; - service.project = { id: 2 }; - const adapter = this.owner.lookup('adapter:auth-method'); - assert.equal(adapter.urlPrefix(), `${config.api.namespace}/orgs/1`); - }); -}); diff --git a/addons/api/tests/unit/adapters/group-test.js b/addons/api/tests/unit/adapters/group-test.js deleted file mode 100644 index 2c1c53d3c..000000000 --- a/addons/api/tests/unit/adapters/group-test.js +++ /dev/null @@ -1,21 +0,0 @@ -import { module, test } from 'qunit'; -import { setupTest } from 'ember-qunit'; -import config from 'ember-get-config'; - -module('Unit | Adapter | group', function(hooks) { - setupTest(hooks); - - test('it exists', function(assert) { - let adapter = this.owner.lookup('adapter:group'); - assert.ok(adapter); - }); - - test('it generates URL prefixes without project scope', function (assert) { - assert.expect(1); - const service = this.owner.lookup('service:scope'); - service.org = { id: 1 }; - service.project = { id: 2 }; - const adapter = this.owner.lookup('adapter:group'); - assert.equal(adapter.urlPrefix(), `${config.api.namespace}/orgs/1`); - }); -}); diff --git a/addons/api/tests/unit/adapters/host-catalog-test.js b/addons/api/tests/unit/adapters/host-catalog-test.js deleted file mode 100644 index 6250cc547..000000000 --- a/addons/api/tests/unit/adapters/host-catalog-test.js +++ /dev/null @@ -1,11 +0,0 @@ -import { module, test } from 'qunit'; -import { setupTest } from 'ember-qunit'; - -module('Unit | Adapter | host catalog', function(hooks) { - setupTest(hooks); - - test('it exists', function(assert) { - let adapter = this.owner.lookup('adapter:host-catalog'); - assert.ok(adapter); - }); -}); diff --git a/addons/api/tests/unit/adapters/project-test.js b/addons/api/tests/unit/adapters/project-test.js deleted file mode 100644 index 90576b2bd..000000000 --- a/addons/api/tests/unit/adapters/project-test.js +++ /dev/null @@ -1,16 +0,0 @@ -import { module, test } from 'qunit'; -import { setupTest } from 'ember-qunit'; -import config from 'ember-get-config'; - -module('Unit | Adapter | project', function (hooks) { - setupTest(hooks); - - test('it generates URL prefixes without project scope', function (assert) { - assert.expect(1); - const service = this.owner.lookup('service:scope'); - service.org = { id: 1 }; - service.project = { id: 2 }; - const adapter = this.owner.lookup('adapter:project'); - assert.equal(adapter.urlPrefix(), `${config.api.namespace}/orgs/1`); - }); -}); diff --git a/addons/api/tests/unit/adapters/role-test.js b/addons/api/tests/unit/adapters/role-test.js deleted file mode 100644 index adb7b1abd..000000000 --- a/addons/api/tests/unit/adapters/role-test.js +++ /dev/null @@ -1,16 +0,0 @@ -import { module, test } from 'qunit'; -import { setupTest } from 'ember-qunit'; -import config from 'ember-get-config'; - -module('Unit | Adapter | role', function(hooks) { - setupTest(hooks); - - test('it generates URL prefixes without project scope', function (assert) { - assert.expect(1); - const service = this.owner.lookup('service:scope'); - service.org = { id: 1 }; - service.project = { id: 2 }; - const adapter = this.owner.lookup('adapter:role'); - assert.equal(adapter.urlPrefix(), `${config.api.namespace}/orgs/1`); - }); -}); diff --git a/addons/api/tests/unit/adapters/user-test.js b/addons/api/tests/unit/adapters/user-test.js deleted file mode 100644 index 17dc82ff6..000000000 --- a/addons/api/tests/unit/adapters/user-test.js +++ /dev/null @@ -1,21 +0,0 @@ -import { module, test } from 'qunit'; -import { setupTest } from 'ember-qunit'; -import config from 'ember-get-config'; - -module('Unit | Adapter | user', function(hooks) { - setupTest(hooks); - - test('it exists', function(assert) { - let adapter = this.owner.lookup('adapter:user'); - assert.ok(adapter); - }); - - test('it generates URL prefixes without project scope', function (assert) { - assert.expect(1); - const service = this.owner.lookup('service:scope'); - service.org = { id: 1 }; - service.project = { id: 2 }; - const adapter = this.owner.lookup('adapter:user'); - assert.equal(adapter.urlPrefix(), `${config.api.namespace}/orgs/1`); - }); -}); diff --git a/addons/api/tests/unit/mixins/scope-adapter-test.js b/addons/api/tests/unit/mixins/scope-adapter-test.js index 5e042ae93..9b6f8a455 100644 --- a/addons/api/tests/unit/mixins/scope-adapter-test.js +++ b/addons/api/tests/unit/mixins/scope-adapter-test.js @@ -1,14 +1,14 @@ -import RESTAdapter from '@ember-data/adapter/rest'; -import ScopeAdapterMixin from 'api/mixins/scope-adapter'; -import { module, test } from 'qunit'; +// import RESTAdapter from '@ember-data/adapter/rest'; +// import ScopeAdapterMixin from 'api/mixins/scope-adapter'; +import { module/*, test */} from 'qunit'; import { setupTest } from 'ember-qunit'; module('Unit | Mixin | scope-adapter', function (hooks) { setupTest(hooks); + /* test('it does not interfere with basic URLs', function (assert) { assert.expect(1); - //const service = this.owner.lookup('service:scope'); const ScopeAdapter = RESTAdapter.extend(ScopeAdapterMixin); this.owner.register('adapter:scope', ScopeAdapter); const adapter = this.owner.lookup('adapter:scope'); @@ -18,7 +18,6 @@ module('Unit | Mixin | scope-adapter', function (hooks) { test('it does not interfere with namespaced URLs', function (assert) { assert.expect(1); - //const service = this.owner.lookup('service:scope'); const ScopeAdapter = RESTAdapter.extend(ScopeAdapterMixin, { namespace: 'api/v1', }); @@ -65,4 +64,5 @@ module('Unit | Mixin | scope-adapter', function (hooks) { let url = adapter._buildURL('model', 1); assert.equal(url, '/api/v1/orgs/1/models/1'); }); + */ }); diff --git a/addons/api/tests/unit/services/scope-test.js b/addons/api/tests/unit/services/scope-test.js deleted file mode 100644 index c94b1bfd5..000000000 --- a/addons/api/tests/unit/services/scope-test.js +++ /dev/null @@ -1,66 +0,0 @@ -import { module, test } from 'qunit'; -import { setupTest } from 'ember-qunit'; -import { Scope } from '../../../services/scope'; - -module('Unit | Service | scope class', function (hooks) { - setupTest(hooks); - - test('it provides a scope class', function (assert) { - assert.expect(1); - assert.ok(Scope); - }); - - test('it has initializable org and project members', function (assert) { - assert.expect(2); - const org = { id: 1 }; - const project = { id: 2 }; - const scope = new Scope(org, project); - assert.equal(scope.org.id, 1); - assert.equal(scope.project.id, 2); - }); - - test('it has a null project if org is null, even if project was initialized to non-null', function (assert) { - assert.expect(2); - const org = null; - const project = { id: 2 }; - const scope = new Scope(org, project); - assert.notOk(scope.org); - assert.notOk(scope.project); - }); - - test('it serializes to JSON', function (assert) { - assert.expect(2); - let scope = new Scope({ id: 1 }); - assert.deepEqual(scope.toJSON(), { - org: { id: 1 }, - project: null, - }); - scope = new Scope({ id: 1 }, { id: 2 }); - assert.deepEqual(scope.toJSON(), { - org: { id: 1 }, - project: { id: 2 }, - }); - }); -}); - -module('Unit | Service | scope', function (hooks) { - setupTest(hooks); - - test('it returns nulls when scope is not set', function (assert) { - assert.expect(2); - const service = this.owner.lookup('service:scope'); - assert.equal(service.org, null); - assert.equal(service.project, null); - }); - - test('it aliases org and project to a scope instance', function (assert) { - assert.expect(2); - const service = this.owner.lookup('service:scope'); - const org = { id: 1 }; - const project = { id: 2 }; - service.org = org; - service.project = project; - assert.equal(service.org, service.scope.org); - assert.equal(service.project, service.scope.project); - }); -}); From 20361ef00cd41ae02184db2cf735f00ac770457e Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Wed, 15 Jul 2020 11:52:39 -0400 Subject: [PATCH 04/70] chore: rip out scope adapter mixin --- addons/api/addon/mixins/scope-adapter.js | 53 --------------- .../tests/unit/mixins/scope-adapter-test.js | 68 ------------------- 2 files changed, 121 deletions(-) delete mode 100644 addons/api/addon/mixins/scope-adapter.js delete mode 100644 addons/api/tests/unit/mixins/scope-adapter-test.js diff --git a/addons/api/addon/mixins/scope-adapter.js b/addons/api/addon/mixins/scope-adapter.js deleted file mode 100644 index 705e5f1c0..000000000 --- a/addons/api/addon/mixins/scope-adapter.js +++ /dev/null @@ -1,53 +0,0 @@ -import Mixin from '@ember/object/mixin'; -import { inject as service } from '@ember/service'; - -/** - * Add this mixin to resource adapters which should be accessed via an - * org/project-scoped URL. This mixin appends a scope path based on the scope - * reported by `ScopeService`. - */ -export default Mixin.create({ - // =services - - scope: service(), - - // =properties - - /** - * If true and the current scope has a project, will include the project in - * the resource path. Set to false for resources that are scoped exclusively - * to org. - * @type {boolean} - */ - includeProject: true, - - // =methods - - /** - * Appends a scope path to the URL prefix, if a scope is set. - * @override - * @return {string} - */ - urlPrefix() { - const prefix = this._super(...arguments); - let scopePath = this.scopePath; - // Ensure a slash is added between prefix + scope path if needed. - if (scopePath && prefix.charAt(prefix.length - 1) !== '/') { - scopePath = `/${scopePath}`; - } - return `${prefix}${scopePath}`; - }, - - /** - * Generates a scope path for the current scope as reported by `ScopeService`. - * If no scope is set, returns empty string. - * @return {string} - */ - get scopePath() { - const { org, project } = this.scope; - const scopePath = []; - if (org) scopePath.push('orgs', org.id); - if (project && this.includeProject) scopePath.push('projects', project.id); - return scopePath.join('/'); - }, -}); diff --git a/addons/api/tests/unit/mixins/scope-adapter-test.js b/addons/api/tests/unit/mixins/scope-adapter-test.js deleted file mode 100644 index 9b6f8a455..000000000 --- a/addons/api/tests/unit/mixins/scope-adapter-test.js +++ /dev/null @@ -1,68 +0,0 @@ -// import RESTAdapter from '@ember-data/adapter/rest'; -// import ScopeAdapterMixin from 'api/mixins/scope-adapter'; -import { module/*, test */} from 'qunit'; -import { setupTest } from 'ember-qunit'; - -module('Unit | Mixin | scope-adapter', function (hooks) { - setupTest(hooks); - - /* - test('it does not interfere with basic URLs', function (assert) { - assert.expect(1); - const ScopeAdapter = RESTAdapter.extend(ScopeAdapterMixin); - this.owner.register('adapter:scope', ScopeAdapter); - const adapter = this.owner.lookup('adapter:scope'); - const url = adapter._buildURL('model', 1); - assert.equal(url, '/models/1'); - }); - - test('it does not interfere with namespaced URLs', function (assert) { - assert.expect(1); - const ScopeAdapter = RESTAdapter.extend(ScopeAdapterMixin, { - namespace: 'api/v1', - }); - this.owner.register('adapter:scope', ScopeAdapter); - const adapter = this.owner.lookup('adapter:scope'); - const url = adapter._buildURL('model', 1); - assert.equal(url, '/api/v1/models/1'); - }); - - test('it adds a scope to basic URLs when scope is set', function (assert) { - assert.expect(4); - const service = this.owner.lookup('service:scope'); - service.org = { id: 1 }; - const ScopeAdapter = RESTAdapter.extend(ScopeAdapterMixin, { - namespace: 'api/v1', - }); - this.owner.register('adapter:scope', ScopeAdapter); - const adapter = this.owner.lookup('adapter:scope'); - let url = adapter._buildURL('model', 1); - assert.equal(url, '/api/v1/orgs/1/models/1'); - service.project = { id: 2 }; - url = adapter._buildURL('model', 1); - assert.equal(url, '/api/v1/orgs/1/projects/2/models/1'); - service.org = { id: 'foobar' }; - url = adapter._buildURL('model', 1); - assert.equal(url, '/api/v1/orgs/foobar/projects/2/models/1'); - // can't have a project without an org - service.org = null; - url = adapter._buildURL('model', 1); - assert.equal(url, '/api/v1/models/1'); - }); - - test('it does not add a project scope to basic URLs when includeProject is false', function (assert) { - assert.expect(1); - const service = this.owner.lookup('service:scope'); - service.org = { id: 1 }; - service.project = { id: 2 }; - const ScopeAdapter = RESTAdapter.extend(ScopeAdapterMixin, { - namespace: 'api/v1', - includeProject: false, - }); - this.owner.register('adapter:scope', ScopeAdapter); - const adapter = this.owner.lookup('adapter:scope'); - let url = adapter._buildURL('model', 1); - assert.equal(url, '/api/v1/orgs/1/models/1'); - }); - */ -}); From 5a438c7bc9deeee6a44c1ef77279bbc40fbfef42 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Wed, 15 Jul 2020 12:41:58 -0400 Subject: [PATCH 05/70] chore: add support for scope relationship to all models --- addons/api/addon/models/base.js | 10 ++++++++ addons/api/addon/models/scope.js | 2 +- addons/api/tests/unit/models/base-test.js | 29 +++++++++++++++++++++++ 3 files changed, 40 insertions(+), 1 deletion(-) diff --git a/addons/api/addon/models/base.js b/addons/api/addon/models/base.js index a680bce7a..e9a773d7c 100644 --- a/addons/api/addon/models/base.js +++ b/addons/api/addon/models/base.js @@ -1,7 +1,17 @@ import Model from '@ember-data/model'; +import { belongsTo } from '@ember-data/model'; import { computed } from '@ember/object'; export default class BaseModel extends Model { + + // =relationships + + /** + * Scope of this resource, if any. + * @type {ScopeModel} + */ + @belongsTo('scope', {async: false, inverse: false}) scope; + // =attributes /** diff --git a/addons/api/addon/models/scope.js b/addons/api/addon/models/scope.js index b62ca94ba..4030f7852 100644 --- a/addons/api/addon/models/scope.js +++ b/addons/api/addon/models/scope.js @@ -24,7 +24,7 @@ export default class ScopeModel extends GeneratedScopeModel { */ @hasMany('scope', {inverse: 'parentScope'}) childrenScopes; - // =computed + // =attributes /** * @type {boolean} diff --git a/addons/api/tests/unit/models/base-test.js b/addons/api/tests/unit/models/base-test.js index 670168d48..0a48639a9 100644 --- a/addons/api/tests/unit/models/base-test.js +++ b/addons/api/tests/unit/models/base-test.js @@ -11,6 +11,35 @@ module('Unit | Model | base', function (hooks) { assert.ok(model); }); + test('it may have a scope relationship', function (assert) { + assert.expect(1); + const store = this.owner.lookup('service:store'); + store.push({ + data: { + id: 'o_1', + type: 'scope' + } + }); + store.push({ + data: { + id: '123abc', + type: 'project', + attributes: {}, + relationships: { + scope: { + data: { + id: 'o_1', + type: 'scope' + } + } + } + } + }); + const scope = store.peekRecord('scope', 'o_1'); + const model = store.peekRecord('project', '123abc'); + assert.equal(model.scope, scope); + }); + test('it has a displayName attribute', function (assert) { assert.expect(3); const store = this.owner.lookup('service:store'); From 3b37c0f92a4517fe500e31149f6dd84ef572ea89 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Wed, 15 Jul 2020 14:29:05 -0400 Subject: [PATCH 06/70] chore: update test to account for scope --- addons/api/tests/unit/serializers/application-test.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/addons/api/tests/unit/serializers/application-test.js b/addons/api/tests/unit/serializers/application-test.js index a9f999501..b93171257 100644 --- a/addons/api/tests/unit/serializers/application-test.js +++ b/addons/api/tests/unit/serializers/application-test.js @@ -14,14 +14,17 @@ module('Unit | Serializer | application', function (hooks) { test('it serializes records', function (assert) { assert.expect(1); const store = this.owner.lookup('service:store'); + const scope = store.createRecord('scope', {id: 'global'}); const record = store.createRecord('project', { name: 'Project', description: 'Description', + scope }); const serializedRecord = record.serialize(); assert.deepEqual(serializedRecord, { name: 'Project', description: 'Description', + scope_id: 'global' //disabled: false, // TODO: disabled is temporarily disabled }); }); From 89b736744fe1f0493310d65ff7a66a795bb2c377 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Wed, 15 Jul 2020 15:52:21 -0400 Subject: [PATCH 07/70] chore: add support for prefix and suffix with enhanced API --- addons/api/addon/adapters/application.js | 5 +- addons/api/addon/mixins/adapter-build-url.js | 78 +++++++++++++++++++ .../unit/mixins/adapter-build-url-test.js | 55 +++++++++++++ 3 files changed, 137 insertions(+), 1 deletion(-) create mode 100644 addons/api/addon/mixins/adapter-build-url.js create mode 100644 addons/api/tests/unit/mixins/adapter-build-url-test.js diff --git a/addons/api/addon/adapters/application.js b/addons/api/addon/adapters/application.js index 182fa3493..85bf2f829 100644 --- a/addons/api/addon/adapters/application.js +++ b/addons/api/addon/adapters/application.js @@ -1,4 +1,5 @@ import RESTAdapter from '@ember-data/adapter/rest'; +import AdapterBuildURLMixin from '../mixins/adapter-build-url'; import config from 'ember-get-config'; import { get } from '@ember/object'; import { InvalidError } from '@ember-data/adapter/error'; @@ -36,7 +37,9 @@ function prenormalizeArrayResponse(response) { return payloadIsBlank(response) ? {items: []} : response; } -export default class ApplicationAdapter extends RESTAdapter { +export default class ApplicationAdapter extends RESTAdapter.extend( + AdapterBuildURLMixin +) { // =attributes /** diff --git a/addons/api/addon/mixins/adapter-build-url.js b/addons/api/addon/mixins/adapter-build-url.js new file mode 100644 index 000000000..8ff6d3474 --- /dev/null +++ b/addons/api/addon/mixins/adapter-build-url.js @@ -0,0 +1,78 @@ +import Mixin from '@ember/object/mixin'; +import { get } from '@ember/object'; + +/** + * Overrides URL-building methods in an adpater for greater flexibility. + * Specifically enhances calls to `urlPrefix` with `modelName`, `id`, and + * `snapshot`, which aren't normally passed. And adds support for a + * `urlSuffix` method, the return value of which is appended to the end of + * a generated URL. + * + * While this mixin provides an enhanced API, it doesn't actually change the + * way URLs are generated by default. Override methods in the adapter that + * uses this mixin to alter generated URLs. + */ +export default Mixin.create({ + + // =methods + + + /** + * This override behaves identically to the default method by default. + * It exists only to document the additional paramters to the method. + * @override + * @param {string} path + * @param {string} parentURL + * @param {string} modelName + * @param {string} id + * @param {object} snapshot + * @return {string} urlPrefix + */ + urlPrefix(/* path, parentURL, modelName, id, snapshot */) { + // What's this super syntax? + // Ember mixins are still based on the older Ember Object, rather than + // native ES classes. Thus we have to call super in this strange way. + // https://blog.emberjs.com/2019/01/26/emberjs-native-class-update-2019-edition.html + return this._super(...arguments); + }, + + /** + * Generates a string to be appended to a URL. Empty string by default. + * @param {string} modelName + * @param {string} id + * @param {?object} snapshot + * @return {string} + */ + urlSuffix(/* modelName, id, snapshot */) { + return ''; + }, + + /** + * Overrides the default `_buildURL` with a nearly identical version. + * This method simply passes arguments into the `urlPrefix` method for + * convenience, where by default it receives no arguments. + * + * Adds a suffix, generated by the `urlSuffix` method, to the URL. + * @override + * @param {String} modelName + * @param {String} id + * @return {String} url + */ + _buildURL(modelName, id, snapshot) { + let path; + let url = []; + const host = get(this, 'host'); + const prefix = this.urlPrefix(null, null, modelName, id, snapshot); + const suffix = this.urlSuffix(modelName, id, snapshot); + if (modelName) { + path = this.pathForType(modelName); + if (path) url.push(path); + } + if (id) url.push(encodeURIComponent(id)); + if (prefix) url.unshift(prefix); + url = url.join('/'); + if (!host && url && url.charAt(0) !== '/') url = '/' + url; + return `${url}${suffix}`; + } + +}); diff --git a/addons/api/tests/unit/mixins/adapter-build-url-test.js b/addons/api/tests/unit/mixins/adapter-build-url-test.js new file mode 100644 index 000000000..560f3f8fc --- /dev/null +++ b/addons/api/tests/unit/mixins/adapter-build-url-test.js @@ -0,0 +1,55 @@ +import RESTAdapter from '@ember-data/adapter/rest'; +import AdapterBuildURLMixin from 'api/mixins/adapter-build-url'; +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Mixin | build-url', function(hooks) { + setupTest(hooks); + + test('it does not interfere with basic URLs', function (assert) { + assert.expect(1); + const Adapter = RESTAdapter.extend(AdapterBuildURLMixin); + this.owner.register('adapter:adapter', Adapter); + const adapter = this.owner.lookup('adapter:adapter'); + const url = adapter._buildURL('model', 1); + assert.equal(url, '/models/1'); + }); + + test('it does not interfere with namespaced URLs', function (assert) { + assert.expect(1); + const Adapter = RESTAdapter.extend(AdapterBuildURLMixin, { + namespace: 'api/v1' + }); + this.owner.register('adapter:adapter', Adapter); + const adapter = this.owner.lookup('adapter:adapter'); + const url = adapter._buildURL('model', 1); + assert.equal(url, '/api/v1/models/1'); + }); + + test('it can prepend a prefix', function (assert) { + assert.expect(1); + const Adapter = RESTAdapter.extend(AdapterBuildURLMixin, { + urlPrefix() { + return '/foo/bars' + } + }); + this.owner.register('adapter:adapter', Adapter); + const adapter = this.owner.lookup('adapter:adapter'); + const url = adapter._buildURL('model', 1); + assert.equal(url, '/foo/bars/models/1'); + }); + + test('it can append a suffix', function (assert) { + assert.expect(1); + const Adapter = RESTAdapter.extend(AdapterBuildURLMixin, { + urlSuffix() { + return ':method' + } + }); + this.owner.register('adapter:adapter', Adapter); + const adapter = this.owner.lookup('adapter:adapter'); + const url = adapter._buildURL('model', 1); + assert.equal(url, '/models/1:method'); + }); + +}); From 9f48f8c85eb89762fd8eeff15e25640bb03b33c5 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Wed, 15 Jul 2020 16:23:38 -0400 Subject: [PATCH 08/70] chore: add support for scope_id in URL prefixes and method in URL suffixes --- addons/api/addon/adapters/application.js | 36 ++++- addons/api/addon/mixins/adapter-build-url.js | 133 ++++++++++++++---- .../tests/unit/adapters/application-test.js | 57 +++++++- 3 files changed, 195 insertions(+), 31 deletions(-) diff --git a/addons/api/addon/adapters/application.js b/addons/api/addon/adapters/application.js index 85bf2f829..8a072b077 100644 --- a/addons/api/addon/adapters/application.js +++ b/addons/api/addon/adapters/application.js @@ -1,7 +1,7 @@ import RESTAdapter from '@ember-data/adapter/rest'; import AdapterBuildURLMixin from '../mixins/adapter-build-url'; import config from 'ember-get-config'; -import { get } from '@ember/object'; +import { get, getWithDefault } from '@ember/object'; import { InvalidError } from '@ember-data/adapter/error'; import { dasherize } from '@ember/string'; import { pluralize } from 'ember-inflector'; @@ -58,6 +58,40 @@ export default class ApplicationAdapter extends RESTAdapter.extend( // =methods + /** + * Prepends a scope to the URL prefix, if a scope is passed via the + * snapshot's `adapterOptions.scope_id` field. + * @override + * @param {string} path + * @param {string} parentURL + * @param {string} modelName + * @param {string} id + * @param {object} snapshot + * @return {string} + */ + urlPrefix(path, parentURL, modelName, id, snapshot={}) { + const prefix = super.urlPrefix(...arguments); + let scopePath = getWithDefault(snapshot, 'adapterOptions.scope_id', ''); + // Ensure a slash is added between prefix + scope path if needed. + if (scopePath && prefix.charAt(prefix.length - 1) !== '/') { + scopePath = `/${scopePath}`; + } + return `${prefix}${scopePath}`; + } + + /** + * Appends a custom method after a colon, if a method is passed via the + * snapshot's `adapterOptions.method` field. + * @param {string} modelName + * @param {string} id + * @param {object} snapshot + * @return {string} + */ + urlSuffix(modelName, id, snapshot={}) { + const method = getWithDefault(snapshot, 'adapterOptions.method', ''); + return method ? `:${method}` : ''; + } + /** * Transforms the type to a dasherized string used in our API paths. * diff --git a/addons/api/addon/mixins/adapter-build-url.js b/addons/api/addon/mixins/adapter-build-url.js index 8ff6d3474..264364d0d 100644 --- a/addons/api/addon/mixins/adapter-build-url.js +++ b/addons/api/addon/mixins/adapter-build-url.js @@ -16,36 +16,35 @@ export default Mixin.create({ // =methods + /** + * This override behaves identically to the default method by default. + * It exists only to document the additional paramters to the method. + * @override + * @param {string} path + * @param {string} parentURL + * @param {string} modelName + * @param {string} id + * @param {object} snapshot + * @return {string} urlPrefix + */ + urlPrefix(/* path, parentURL, modelName, id, snapshot */) { + // What's this super syntax? + // Ember mixins are still based on the older Ember Object, rather than + // native ES classes. Thus we have to call super in this strange way. + // https://blog.emberjs.com/2019/01/26/emberjs-native-class-update-2019-edition.html + return this._super(...arguments); + }, - /** - * This override behaves identically to the default method by default. - * It exists only to document the additional paramters to the method. - * @override - * @param {string} path - * @param {string} parentURL - * @param {string} modelName - * @param {string} id - * @param {object} snapshot - * @return {string} urlPrefix - */ - urlPrefix(/* path, parentURL, modelName, id, snapshot */) { - // What's this super syntax? - // Ember mixins are still based on the older Ember Object, rather than - // native ES classes. Thus we have to call super in this strange way. - // https://blog.emberjs.com/2019/01/26/emberjs-native-class-update-2019-edition.html - return this._super(...arguments); - }, - - /** - * Generates a string to be appended to a URL. Empty string by default. - * @param {string} modelName - * @param {string} id - * @param {?object} snapshot - * @return {string} - */ - urlSuffix(/* modelName, id, snapshot */) { - return ''; - }, + /** + * Generates a string to be appended to a URL. Empty string by default. + * @param {string} modelName + * @param {string} id + * @param {?object} snapshot + * @return {string} + */ + urlSuffix(/* modelName, id, snapshot */) { + return ''; + }, /** * Overrides the default `_buildURL` with a nearly identical version. @@ -73,6 +72,82 @@ export default Mixin.create({ url = url.join('/'); if (!host && url && url.charAt(0) !== '/') url = '/' + url; return `${url}${suffix}`; + }, + + /** + * Overrides the default method simply to pass an additional argument + * `snapshot` to the `_buildURL` method. + * @override + * @param {string} id + * @param {string} modelName + * @param {object} snapshot + * @return {string} + */ + urlForFindRecord(id, modelName, snapshot) { + return this._buildURL(modelName, id, snapshot); + }, + + /** + * Overrides the default method simply to pass an additional argument + * `snapshot` to the `_buildURL` method. + * @override + * @param {string} modelName + * @param {object} snapshot + * @return {string} + */ + urlForFindAll(modelName, snapshot) { + return this._buildURL(modelName, null, snapshot); + }, + + /** + * Overrides the default method simply to pass an additional argument + * `snapshot` to the `_buildURL` method. + * @override + * @param {string} id + * @param {string} modelName + * @param {object} snapshot + * @return {string} + */ + urlForFindBelongsTo(id, modelName, snapshot) { + return this._buildURL(modelName, id, snapshot); + }, + + /** + * Overrides the default method simply to pass an additional argument + * `snapshot` to the `_buildURL` method. + * @override + * @param {string} modelName + * @param {object} snapshot + * @return {string} + */ + urlForCreateRecord(modelName, snapshot) { + return this._buildURL(modelName, null, snapshot); + }, + + /** + * Overrides the default method simply to pass an additional argument + * `snapshot` to the `_buildURL` method. + * @override + * @param {string} id + * @param {string} modelName + * @param {object} snapshot + * @return {string} + */ + urlForUpdateRecord(id, modelName, snapshot) { + return this._buildURL(modelName, id, snapshot); + }, + + /** + * Overrides the default method simply to pass an additional argument + * `snapshot` to the `_buildURL` method. + * @override + * @param {string} id + * @param {string} modelName + * @param {object} snapshot + * @return {string} + */ + urlForDeleteRecord(id, modelName, snapshot) { + return this._buildURL(modelName, id, snapshot); } }); diff --git a/addons/api/tests/unit/adapters/application-test.js b/addons/api/tests/unit/adapters/application-test.js index f84fc92ff..bec9d581b 100644 --- a/addons/api/tests/unit/adapters/application-test.js +++ b/addons/api/tests/unit/adapters/application-test.js @@ -18,9 +18,64 @@ module('Unit | Adapter | application', function (hooks) { }); test('it generates correct URL prefixes', function (assert) { + assert.expect(2); + const adapter = this.owner.lookup('adapter:application'); + assert.ok(config.api.namespace); + assert.equal(adapter.urlPrefix(), config.api.namespace); + }); + + test('it generates URL prefixes with optional `scope_id` from adapterOptions', function (assert) { + assert.expect(2); + const scope_id = 'global'; + const adapter = this.owner.lookup('adapter:application'); + const prefix = adapter.urlPrefix(null, null, null, null, { + adapterOptions: {scope_id} + }); + assert.ok(config.api.namespace); + assert.equal(prefix, `v1/${scope_id}`); + }); + + test('it generates correct default URL suffixes', function (assert) { assert.expect(1); const adapter = this.owner.lookup('adapter:application'); - assert.equal(adapter.urlPrefix(), config.api.namespace); + assert.equal(adapter.urlSuffix(), ''); + }); + + test('it generates URL suffixes with optional `method` from adapterOptions', function (assert) { + assert.expect(2); + const method = 'set-something'; + const adapter = this.owner.lookup('adapter:application'); + const suffix = adapter.urlSuffix(null, null, { + adapterOptions: {method} + }); + assert.ok(config.api.namespace); + assert.equal(suffix, ':set-something'); + }); + + test('it generates correct complete URLs', function (assert) { + assert.expect(6); + const scope_id = 'o_1'; + const method = 'my-custom-method'; + const mockSnapshot = { + adapterOptions: { + scope_id, + method + } + }; + const adapter = this.owner.lookup('adapter:application'); + // test URL generation for each request type... + const findRecordURL = adapter.buildURL('project', '1', mockSnapshot, 'findRecord'); + assert.equal(findRecordURL, '/v1/o_1/projects/1:my-custom-method'); + const findAllURL = adapter.buildURL('project', null, mockSnapshot, 'findAll'); + assert.equal(findAllURL, '/v1/o_1/projects:my-custom-method'); + const findBelongsToURL = adapter.buildURL('project', '2', mockSnapshot, 'findBelongsTo'); + assert.equal(findBelongsToURL, '/v1/o_1/projects/2:my-custom-method'); + const createRecordURL = adapter.buildURL('project', null, mockSnapshot, 'createRecord'); + assert.equal(createRecordURL, '/v1/o_1/projects:my-custom-method'); + const updateRecordURL = adapter.buildURL('project', '3', mockSnapshot, 'updateRecord'); + assert.equal(updateRecordURL, '/v1/o_1/projects/3:my-custom-method'); + const deleteRecordURL = adapter.buildURL('project', '4', mockSnapshot, 'deleteRecord'); + assert.equal(deleteRecordURL, '/v1/o_1/projects/4:my-custom-method'); }); test('it rewrites PUT to PATCH, but leaves others unchanged', function (assert) { From 41f5bc25d847e33260ea238d44add57fa3a1ad77 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Wed, 15 Jul 2020 16:47:02 -0400 Subject: [PATCH 09/70] add support to model save method for scope_id and custom methods --- addons/api/addon/models/base.js | 29 +++++++++++ addons/api/tests/unit/models/base-test.js | 60 +++++++++++++++++++++++ 2 files changed, 89 insertions(+) diff --git a/addons/api/addon/models/base.js b/addons/api/addon/models/base.js index e9a773d7c..9b2820a06 100644 --- a/addons/api/addon/models/base.js +++ b/addons/api/addon/models/base.js @@ -39,4 +39,33 @@ export default class BaseModel extends Model { get cannotSave() { return !this.canSave; } + + // =methods + + /** + * If this model instance has a related scope, the scope's ID will be passed + * via `adapterOptions.scope_id` automatically. End users may optionally + * pass a custom scope ID to override. + * + * @example + * model.save(); // passes the model's current scope ID + * model.save({adapterOptions: {scope_id: 'global'}}); // override scope ID + * + * End users may specify a _custom method_ via `adapterOptions.method`. + * Custom methods are appended to the record's URL by the adapter. + * + * @example + * // Results in a URL: `/v1/model-name/id:my-custom-method` + * model.save({adapterOptions: {method: 'my-custom-method'}}); + * + * @return {Promise} + */ + save(options={adapterOptions: {}}) { + const scopeID = this.belongsTo('scope').id(); + if (scopeID && !options.adapterOptions.scope_id) { + options.adapterOptions.scope_id = scopeID; + } + return super.save(options); + } + } diff --git a/addons/api/tests/unit/models/base-test.js b/addons/api/tests/unit/models/base-test.js index 0a48639a9..7b463859f 100644 --- a/addons/api/tests/unit/models/base-test.js +++ b/addons/api/tests/unit/models/base-test.js @@ -1,8 +1,10 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; +import setupMirage from 'ember-cli-mirage/test-support/setup-mirage'; module('Unit | Model | base', function (hooks) { setupTest(hooks); + setupMirage(hooks); // Replace this with your real tests. test('it exists', function (assert) { @@ -82,4 +84,62 @@ module('Unit | Model | base', function (hooks) { assert.equal(model.cannotSave, true); model.transitionTo('loaded'); }); + + test('it saves records to a scoped URL', async function (assert) { + assert.expect(3); + const store = this.owner.lookup('service:store'); + const scope = store.createRecord('scope', {id: 'o_123'}); + const model = store.createRecord('user', { scope }); + this.server.post('/v1/o_123/users', () => { + assert.ok(true, 'Correctly scoped create record URL was requested.') + return {}; + }); + await model.save(); + this.server.patch('/v1/o_123/users', () => { + assert.ok(true, 'Correctly scoped update record URL was requested.') + return {}; + }); + await model.save(); + this.server.delete('/v1/o_123/users', () => { + assert.ok(true, 'Correctly scoped delete record URL was requested.') + return {}; + }); + await model.destroyRecord(); + }); + + test('it saves to a custom scoped URL if requested', async function (assert) { + assert.expect(3); + const store = this.owner.lookup('service:store'); + const customScopeID = 'global'; + const scope = store.createRecord('scope', {id: 'o_123'}); + const model = store.createRecord('user', { scope }); + this.server.post('/v1/global/users', () => { + assert.ok(true, 'Correctly scoped create record URL was requested.') + return {}; + }); + await model.save({adapterOptions: {scope_id: customScopeID}}); + this.server.patch('/v1/global/users', () => { + assert.ok(true, 'Correctly scoped update record URL was requested.') + return {}; + }); + await model.save({adapterOptions: {scope_id: customScopeID}}); + this.server.delete('/v1/global/users', () => { + assert.ok(true, 'Correctly scoped delete record URL was requested.') + return {}; + }); + await model.destroyRecord({adapterOptions: {scope_id: customScopeID}}); + }); + + test('it saves records to a URL with a custom method if requested', async function (assert) { + assert.expect(1); + const store = this.owner.lookup('service:store'); + const method = 'my-custom-method'; + const scope = store.createRecord('scope', {id: 'o_123'}); + const model = store.createRecord('user', { scope }); + this.server.post(`/v1/o_123/users:${method}`, () => { + assert.ok(true, 'Correctly scoped create record URL was requested.') + return {}; + }); + await model.save({adapterOptions: {method}}); + }); }); From e30ff96d05580221a9aa25703f4b5cd61d7f6417 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Wed, 15 Jul 2020 17:00:51 -0400 Subject: [PATCH 10/70] test: store.findAll will request records from a specified scope ID --- addons/api/tests/unit/adapters/application-test.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/addons/api/tests/unit/adapters/application-test.js b/addons/api/tests/unit/adapters/application-test.js index bec9d581b..4e7342471 100644 --- a/addons/api/tests/unit/adapters/application-test.js +++ b/addons/api/tests/unit/adapters/application-test.js @@ -78,6 +78,16 @@ module('Unit | Adapter | application', function (hooks) { assert.equal(deleteRecordURL, '/v1/o_1/projects/4:my-custom-method'); }); + test('it can request records through the store from a specified scope', async function (assert) { + assert.expect(1); + const store = this.owner.lookup('service:store'); + this.server.get('/v1/p_456/groups', () => { + assert.ok(true, 'Scoped resource URL was requested.'); + return {items: []}; + }); + await store.findAll('group', {adapterOptions: {scope_id: 'p_456'}}); + }); + test('it rewrites PUT to PATCH, but leaves others unchanged', function (assert) { assert.expect(1); const adapter = this.owner.lookup('adapter:application'); From e673decb3e28964e7973e2f3fbfbca2111af9218 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Wed, 15 Jul 2020 17:11:07 -0400 Subject: [PATCH 11/70] test: ensure that a scope's parentScope can be loaded async --- addons/api/tests/unit/models/scope-test.js | 28 ++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/addons/api/tests/unit/models/scope-test.js b/addons/api/tests/unit/models/scope-test.js index 07cf265ec..25edd13d1 100644 --- a/addons/api/tests/unit/models/scope-test.js +++ b/addons/api/tests/unit/models/scope-test.js @@ -39,6 +39,34 @@ module('Unit | Model | scope', function(hooks) { assert.equal(await scopes.objectAt(5).get('parentScope.id'), 'o_2', 'Project 3 parent scope is org 2'); }); + test('it can load the parent scope if it is not already loaded', async function(assert) { + assert.expect(3); + const store = this.owner.lookup('service:store'); + store.push({ + data: { + id: 'p_1', + type: 'scope', + attributes: {}, + relationships: { + parentScope: { + data: { + id: 'o_1', + type: 'scope' + } + } + } + } + }); + this.server.get('/v1/scopes/o_1', () => { + assert.ok(true, 'Correct parent scope was requested.'); + return { id: 'o_1' }; + }); + const project = store.peekRecord('scope', 'p_1'); + assert.equal(project.belongsTo('parentScope').id(), 'o_1'); + const org = await project.get('parentScope'); + assert.equal(org.id, 'o_1'); + }); + test('it has isType boolean getters and setters', async function(assert) { assert.expect(9); const store = this.owner.lookup('service:store'); From ae018db4109d791c2835f85055b56f69c2c0f585 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Wed, 15 Jul 2020 17:17:11 -0400 Subject: [PATCH 12/70] chore: rip out org and project models --- addons/api/addon/generated/models/org.js | 33 --------------- addons/api/addon/generated/models/project.js | 36 ---------------- addons/api/addon/models/org.js | 3 -- addons/api/addon/models/project.js | 3 -- addons/api/addon/serializers/application.js | 2 +- addons/api/app/models/org.js | 1 - addons/api/app/models/project.js | 1 - .../api/tests/dummy/app/routes/application.js | 2 +- .../tests/unit/adapters/application-test.js | 28 ++++++------- addons/api/tests/unit/models/base-test.js | 14 +++---- addons/api/tests/unit/models/org-test.js | 13 ------ addons/api/tests/unit/models/project-test.js | 12 ------ .../unit/serializers/application-test.js | 42 +++++++++---------- 13 files changed, 44 insertions(+), 146 deletions(-) delete mode 100644 addons/api/addon/generated/models/org.js delete mode 100644 addons/api/addon/generated/models/project.js delete mode 100644 addons/api/addon/models/org.js delete mode 100644 addons/api/addon/models/project.js delete mode 100644 addons/api/app/models/org.js delete mode 100644 addons/api/app/models/project.js delete mode 100644 addons/api/tests/unit/models/org-test.js delete mode 100644 addons/api/tests/unit/models/project-test.js diff --git a/addons/api/addon/generated/models/org.js b/addons/api/addon/generated/models/org.js deleted file mode 100644 index 64cea0154..000000000 --- a/addons/api/addon/generated/models/org.js +++ /dev/null @@ -1,33 +0,0 @@ -import BaseModel from '../../models/base'; -import { attr } from '@ember-data/model'; - -/** - * - */ -export default class GeneratedOrgModel extends BaseModel { - - // =attributes - - @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/generated/models/project.js b/addons/api/addon/generated/models/project.js deleted file mode 100644 index abfe583d4..000000000 --- a/addons/api/addon/generated/models/project.js +++ /dev/null @@ -1,36 +0,0 @@ -import BaseModel from '../../models/base'; -import { attr } from '@ember-data/model'; - -/** - * Project contains all fields related to a Project resource - */ -export default class GeneratedProjectModel extends BaseModel { - // =attributes - - @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; - - @attr('boolean', { - description: 'Whether the resource is disabled', - }) - disabled; -} diff --git a/addons/api/addon/models/org.js b/addons/api/addon/models/org.js deleted file mode 100644 index 5b6bb4136..000000000 --- a/addons/api/addon/models/org.js +++ /dev/null @@ -1,3 +0,0 @@ -import GeneratedOrgModel from '../generated/models/org'; - -export default class OrgModel extends GeneratedOrgModel {} diff --git a/addons/api/addon/models/project.js b/addons/api/addon/models/project.js deleted file mode 100644 index 063ed5110..000000000 --- a/addons/api/addon/models/project.js +++ /dev/null @@ -1,3 +0,0 @@ -import GeneratedProjectModel from '../generated/models/project'; - -export default class ProjectModel extends GeneratedProjectModel {} diff --git a/addons/api/addon/serializers/application.js b/addons/api/addon/serializers/application.js index 60d386ba3..a6b989fa4 100644 --- a/addons/api/addon/serializers/application.js +++ b/addons/api/addon/serializers/application.js @@ -100,7 +100,7 @@ export default class ApplicationSerializer extends RESTSerializer { /** * In our API, singluar resources are _unrooted_, whereas Ember Data expects - * them to be rooted under their model name, e.g. `{project: {…}}`. + * them to be rooted under their model name, e.g. `{modelName: {…}}`. * This method makes this transformation to accommodate Ember Data. * * @method normalizeSingleResponse diff --git a/addons/api/app/models/org.js b/addons/api/app/models/org.js deleted file mode 100644 index 8ef9d3f59..000000000 --- a/addons/api/app/models/org.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from 'api/models/org'; diff --git a/addons/api/app/models/project.js b/addons/api/app/models/project.js deleted file mode 100644 index 1fc732fe1..000000000 --- a/addons/api/app/models/project.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from 'api/models/project'; diff --git a/addons/api/tests/dummy/app/routes/application.js b/addons/api/tests/dummy/app/routes/application.js index 4190b97c0..4d3bf9a90 100644 --- a/addons/api/tests/dummy/app/routes/application.js +++ b/addons/api/tests/dummy/app/routes/application.js @@ -2,6 +2,6 @@ import Route from '@ember/routing/route'; export default class ApplicationRoute extends Route { model() { - this.store.findAll('project'); + this.store.findAll('scope'); } } diff --git a/addons/api/tests/unit/adapters/application-test.js b/addons/api/tests/unit/adapters/application-test.js index 4e7342471..503b75762 100644 --- a/addons/api/tests/unit/adapters/application-test.js +++ b/addons/api/tests/unit/adapters/application-test.js @@ -64,18 +64,18 @@ module('Unit | Adapter | application', function (hooks) { }; const adapter = this.owner.lookup('adapter:application'); // test URL generation for each request type... - const findRecordURL = adapter.buildURL('project', '1', mockSnapshot, 'findRecord'); - assert.equal(findRecordURL, '/v1/o_1/projects/1:my-custom-method'); - const findAllURL = adapter.buildURL('project', null, mockSnapshot, 'findAll'); - assert.equal(findAllURL, '/v1/o_1/projects:my-custom-method'); - const findBelongsToURL = adapter.buildURL('project', '2', mockSnapshot, 'findBelongsTo'); - assert.equal(findBelongsToURL, '/v1/o_1/projects/2:my-custom-method'); - const createRecordURL = adapter.buildURL('project', null, mockSnapshot, 'createRecord'); - assert.equal(createRecordURL, '/v1/o_1/projects:my-custom-method'); - const updateRecordURL = adapter.buildURL('project', '3', mockSnapshot, 'updateRecord'); - assert.equal(updateRecordURL, '/v1/o_1/projects/3:my-custom-method'); - const deleteRecordURL = adapter.buildURL('project', '4', mockSnapshot, 'deleteRecord'); - assert.equal(deleteRecordURL, '/v1/o_1/projects/4:my-custom-method'); + const findRecordURL = adapter.buildURL('user', '1', mockSnapshot, 'findRecord'); + assert.equal(findRecordURL, '/v1/o_1/users/1:my-custom-method'); + const findAllURL = adapter.buildURL('user', null, mockSnapshot, 'findAll'); + assert.equal(findAllURL, '/v1/o_1/users:my-custom-method'); + const findBelongsToURL = adapter.buildURL('user', '2', mockSnapshot, 'findBelongsTo'); + assert.equal(findBelongsToURL, '/v1/o_1/users/2:my-custom-method'); + const createRecordURL = adapter.buildURL('user', null, mockSnapshot, 'createRecord'); + assert.equal(createRecordURL, '/v1/o_1/users:my-custom-method'); + const updateRecordURL = adapter.buildURL('user', '3', mockSnapshot, 'updateRecord'); + assert.equal(updateRecordURL, '/v1/o_1/users/3:my-custom-method'); + const deleteRecordURL = adapter.buildURL('user', '4', mockSnapshot, 'deleteRecord'); + assert.equal(deleteRecordURL, '/v1/o_1/users/4:my-custom-method'); }); test('it can request records through the store from a specified scope', async function (assert) { @@ -104,8 +104,8 @@ module('Unit | Adapter | application', function (hooks) { assert.expect(1); const adapter = this.owner.lookup('adapter:application'); const store = this.owner.lookup('service:store'); - this.server.get('/v1/projects', () => new Response({})); - const prenormalized = await adapter.findAll(store, {modelName: 'project'}, null, []); + this.server.get('/v1/users', () => new Response({})); + const prenormalized = await adapter.findAll(store, {modelName: 'user'}, null, []); assert.deepEqual(prenormalized, {items: []}); }); diff --git a/addons/api/tests/unit/models/base-test.js b/addons/api/tests/unit/models/base-test.js index 7b463859f..066ba456e 100644 --- a/addons/api/tests/unit/models/base-test.js +++ b/addons/api/tests/unit/models/base-test.js @@ -25,7 +25,7 @@ module('Unit | Model | base', function (hooks) { store.push({ data: { id: '123abc', - type: 'project', + type: 'user', attributes: {}, relationships: { scope: { @@ -38,7 +38,7 @@ module('Unit | Model | base', function (hooks) { } }); const scope = store.peekRecord('scope', 'o_1'); - const model = store.peekRecord('project', '123abc'); + const model = store.peekRecord('user', '123abc'); assert.equal(model.scope, scope); }); @@ -48,11 +48,11 @@ module('Unit | Model | base', function (hooks) { store.push({ data: { id: '123abc', - type: 'project', + type: 'user', attributes: {}, }, }); - const model = store.peekRecord('project', '123abc'); + const model = store.peekRecord('user', '123abc'); assert.equal(model.name, null); assert.equal(model.displayName, '123abc'); model.name = 'Test'; @@ -65,15 +65,15 @@ module('Unit | Model | base', function (hooks) { store.push({ data: { id: '1', - type: 'project', + type: 'user', attributes: {}, }, }); - const model = store.peekRecord('project', '1'); + const model = store.peekRecord('user', '1'); assert.equal(model.canSave, false); assert.equal(model.cannotSave, true); // Should be able to save if dirty - model.name = 'Project'; + model.name = 'User'; assert.equal(model.hasDirtyAttributes, true); assert.equal(model.canSave, true); assert.equal(model.cannotSave, false); diff --git a/addons/api/tests/unit/models/org-test.js b/addons/api/tests/unit/models/org-test.js deleted file mode 100644 index 2f042eadc..000000000 --- a/addons/api/tests/unit/models/org-test.js +++ /dev/null @@ -1,13 +0,0 @@ -import { module, test } from 'qunit'; -import { setupTest } from 'ember-qunit'; - -module('Unit | Model | org', function(hooks) { - setupTest(hooks); - - // Replace this with your real tests. - test('it exists', function(assert) { - let store = this.owner.lookup('service:store'); - let model = store.createRecord('org', {}); - assert.ok(model); - }); -}); diff --git a/addons/api/tests/unit/models/project-test.js b/addons/api/tests/unit/models/project-test.js deleted file mode 100644 index 9b2a93d73..000000000 --- a/addons/api/tests/unit/models/project-test.js +++ /dev/null @@ -1,12 +0,0 @@ -import { module, test } from 'qunit'; -import { setupTest } from 'ember-qunit'; - -module('Unit | Model | project', function (hooks) { - setupTest(hooks); - - test('it exists', function (assert) { - let store = this.owner.lookup('service:store'); - let model = store.createRecord('project', {}); - assert.ok(model); - }); -}); diff --git a/addons/api/tests/unit/serializers/application-test.js b/addons/api/tests/unit/serializers/application-test.js index b93171257..be99bacb6 100644 --- a/addons/api/tests/unit/serializers/application-test.js +++ b/addons/api/tests/unit/serializers/application-test.js @@ -15,14 +15,14 @@ module('Unit | Serializer | application', function (hooks) { assert.expect(1); const store = this.owner.lookup('service:store'); const scope = store.createRecord('scope', {id: 'global'}); - const record = store.createRecord('project', { - name: 'Project', + const record = store.createRecord('user', { + name: 'User', description: 'Description', scope }); const serializedRecord = record.serialize(); assert.deepEqual(serializedRecord, { - name: 'Project', + name: 'User', description: 'Description', scope_id: 'global' //disabled: false, // TODO: disabled is temporarily disabled @@ -32,18 +32,18 @@ module('Unit | Serializer | application', function (hooks) { test('it normalizes array records from an `items` root key', function (assert) { assert.expect(1); const store = this.owner.lookup('service:store'); - const serializer = store.serializerFor('project'); - const projectModelClass = store.createRecord('project').constructor; + const serializer = store.serializerFor('user'); + const userModelClass = store.createRecord('user').constructor; const payload = { items: [ - { id: 1, name: 'Project 1' }, - { id: 2, name: 'Project 2' }, - { id: 3, name: 'Project 3' }, + { id: 1, name: 'User 1' }, + { id: 2, name: 'User 2' }, + { id: 3, name: 'User 3' }, ], }; const normalizedArray = serializer.normalizeArrayResponse( store, - projectModelClass, + userModelClass, payload ); assert.deepEqual(normalizedArray, { @@ -51,20 +51,20 @@ module('Unit | Serializer | application', function (hooks) { data: [ { id: '1', - type: 'project', - attributes: { name: 'Project 1' }, + type: 'user', + attributes: { name: 'User 1' }, relationships: {}, }, { id: '2', - type: 'project', - attributes: { name: 'Project 2' }, + type: 'user', + attributes: { name: 'User 2' }, relationships: {}, }, { id: '3', - type: 'project', - attributes: { name: 'Project 3' }, + type: 'user', + attributes: { name: 'User 3' }, relationships: {}, }, ], @@ -74,23 +74,23 @@ module('Unit | Serializer | application', function (hooks) { test('it normalizes single, unrooted records', function (assert) { assert.expect(1); const store = this.owner.lookup('service:store'); - const serializer = store.serializerFor('project'); - const projectModelClass = store.createRecord('project').constructor; + const serializer = store.serializerFor('user'); + const userModelClass = store.createRecord('user').constructor; const payload = { id: '1', - name: 'Project 1', + name: 'User 1', }; const normalized = serializer.normalizeSingleResponse( store, - projectModelClass, + userModelClass, payload ); assert.deepEqual(normalized, { included: [], data: { id: '1', - type: 'project', - attributes: { name: 'Project 1' }, + type: 'user', + attributes: { name: 'User 1' }, relationships: {}, }, }); From d847ede74249307c968a4a7ad74ffa1861239b8f Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Thu, 16 Jul 2020 12:54:29 -0400 Subject: [PATCH 13/70] chore: do not serialize scope or parentScope into request payloads --- addons/api/addon/serializers/application.js | 9 +++++++++ addons/api/tests/unit/serializers/application-test.js | 3 +-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/addons/api/addon/serializers/application.js b/addons/api/addon/serializers/application.js index a6b989fa4..9aeb6c083 100644 --- a/addons/api/addon/serializers/application.js +++ b/addons/api/addon/serializers/application.js @@ -6,6 +6,14 @@ import { copy } from 'ember-copy'; * Manages serialization/normalization of data to/from the API. */ export default class ApplicationSerializer extends RESTSerializer { + + // =attributes + + attrs = { + scope: { serialize: false }, + parentScope: { serialize: false } + }; + // =methods /** @@ -129,4 +137,5 @@ export default class ApplicationSerializer extends RESTSerializer { requestType ); } + } diff --git a/addons/api/tests/unit/serializers/application-test.js b/addons/api/tests/unit/serializers/application-test.js index be99bacb6..e076babb6 100644 --- a/addons/api/tests/unit/serializers/application-test.js +++ b/addons/api/tests/unit/serializers/application-test.js @@ -23,8 +23,7 @@ module('Unit | Serializer | application', function (hooks) { const serializedRecord = record.serialize(); assert.deepEqual(serializedRecord, { name: 'User', - description: 'Description', - scope_id: 'global' + description: 'Description' //disabled: false, // TODO: disabled is temporarily disabled }); }); From 433ae8c022e6ccfcffdc4652ce84607129d16240 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Thu, 16 Jul 2020 18:18:25 -0400 Subject: [PATCH 14/70] refactor: change parentScope to scope --- addons/api/addon/models/scope.js | 4 +-- addons/api/addon/serializers/application.js | 3 +- addons/api/tests/unit/models/scope-test.js | 34 ++++++++++----------- 3 files changed, 20 insertions(+), 21 deletions(-) diff --git a/addons/api/addon/models/scope.js b/addons/api/addon/models/scope.js index 4030f7852..f61146690 100644 --- a/addons/api/addon/models/scope.js +++ b/addons/api/addon/models/scope.js @@ -16,13 +16,13 @@ export default class ScopeModel extends GeneratedScopeModel { * The parent scope, if any. * @type {ScopeModel} */ - @belongsTo('scope', {inverse: 'childrenScopes'}) parentScope; + @belongsTo('scope', {inverse: 'children'}) scope; /** * The inverse of parent scope, children. * @type {ScopeModelp[]} */ - @hasMany('scope', {inverse: 'parentScope'}) childrenScopes; + @hasMany('scope', {inverse: 'scope'}) children; // =attributes diff --git a/addons/api/addon/serializers/application.js b/addons/api/addon/serializers/application.js index 9aeb6c083..8eaf3c18b 100644 --- a/addons/api/addon/serializers/application.js +++ b/addons/api/addon/serializers/application.js @@ -10,8 +10,7 @@ export default class ApplicationSerializer extends RESTSerializer { // =attributes attrs = { - scope: { serialize: false }, - parentScope: { serialize: false } + scope: { serialize: false } }; // =methods diff --git a/addons/api/tests/unit/models/scope-test.js b/addons/api/tests/unit/models/scope-test.js index 25edd13d1..37762f9ce 100644 --- a/addons/api/tests/unit/models/scope-test.js +++ b/addons/api/tests/unit/models/scope-test.js @@ -19,24 +19,24 @@ module('Unit | Model | scope', function(hooks) { 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'} + {id: 'o_1', type: 'org', scope_id: 'global'}, + {id: 'o_2', type: 'org', scope_id: 'global'}, + {id: 'p_1', type: 'project', scope_id: 'o_1'}, + {id: 'p_2', type: 'project', scope_id: 'o_1'}, + {id: 'p_3', type: 'project', 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'); + assert.notOk(await scopes.firstObject.get('scope'), 'Global scope has no parent'); + assert.equal(await scopes.firstObject.get('children.length'), 2, 'Global scope has two children scopes'); + assert.equal(await scopes.objectAt(1).get('scope.id'), 'global', 'Org 1 parent scope is global'); + assert.equal(await scopes.objectAt(2).get('scope.id'), 'global', 'Org 2 parent scope is global'); + assert.equal(await scopes.objectAt(1).get('children.length'), 2, 'Org 1 has two children scopes'); + assert.equal(await scopes.objectAt(2).get('children.length'), 1, 'Org 2 has one child scope'); + assert.equal(await scopes.objectAt(3).get('scope.id'), 'o_1', 'Project 1 parent scope is org 1'); + assert.equal(await scopes.objectAt(4).get('scope.id'), 'o_1', 'Project 2 parent scope is org 1'); + assert.equal(await scopes.objectAt(5).get('scope.id'), 'o_2', 'Project 3 parent scope is org 2'); }); test('it can load the parent scope if it is not already loaded', async function(assert) { @@ -48,7 +48,7 @@ module('Unit | Model | scope', function(hooks) { type: 'scope', attributes: {}, relationships: { - parentScope: { + scope: { data: { id: 'o_1', type: 'scope' @@ -62,8 +62,8 @@ module('Unit | Model | scope', function(hooks) { return { id: 'o_1' }; }); const project = store.peekRecord('scope', 'p_1'); - assert.equal(project.belongsTo('parentScope').id(), 'o_1'); - const org = await project.get('parentScope'); + assert.equal(project.belongsTo('scope').id(), 'o_1'); + const org = await project.get('scope'); assert.equal(org.id, 'o_1'); }); From 45b419e641baf2272bdc852164ff2bb213eab208 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Thu, 16 Jul 2020 18:22:44 -0400 Subject: [PATCH 15/70] chore: rename adapterOptions.scope_id to adapterOptions.scopeID --- addons/api/addon/adapters/application.js | 4 ++-- addons/api/addon/models/base.js | 8 ++++---- addons/api/tests/unit/adapters/application-test.js | 14 +++++++------- addons/api/tests/unit/models/base-test.js | 6 +++--- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/addons/api/addon/adapters/application.js b/addons/api/addon/adapters/application.js index 8a072b077..4d3a86b5e 100644 --- a/addons/api/addon/adapters/application.js +++ b/addons/api/addon/adapters/application.js @@ -60,7 +60,7 @@ export default class ApplicationAdapter extends RESTAdapter.extend( /** * Prepends a scope to the URL prefix, if a scope is passed via the - * snapshot's `adapterOptions.scope_id` field. + * snapshot's `adapterOptions.scopeID` field. * @override * @param {string} path * @param {string} parentURL @@ -71,7 +71,7 @@ export default class ApplicationAdapter extends RESTAdapter.extend( */ urlPrefix(path, parentURL, modelName, id, snapshot={}) { const prefix = super.urlPrefix(...arguments); - let scopePath = getWithDefault(snapshot, 'adapterOptions.scope_id', ''); + 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 = `/${scopePath}`; diff --git a/addons/api/addon/models/base.js b/addons/api/addon/models/base.js index 9b2820a06..a1716da97 100644 --- a/addons/api/addon/models/base.js +++ b/addons/api/addon/models/base.js @@ -44,12 +44,12 @@ export default class BaseModel extends Model { /** * If this model instance has a related scope, the scope's ID will be passed - * via `adapterOptions.scope_id` automatically. End users may optionally + * via `adapterOptions.scopeID` automatically. End users may optionally * pass a custom scope ID to override. * * @example * model.save(); // passes the model's current scope ID - * model.save({adapterOptions: {scope_id: 'global'}}); // override scope ID + * model.save({adapterOptions: {scopeID: 'global'}}); // override scope ID * * End users may specify a _custom method_ via `adapterOptions.method`. * Custom methods are appended to the record's URL by the adapter. @@ -62,8 +62,8 @@ export default class BaseModel extends Model { */ save(options={adapterOptions: {}}) { const scopeID = this.belongsTo('scope').id(); - if (scopeID && !options.adapterOptions.scope_id) { - options.adapterOptions.scope_id = scopeID; + if (scopeID && !options.adapterOptions.scopeID) { + options.adapterOptions.scopeID = scopeID; } return super.save(options); } diff --git a/addons/api/tests/unit/adapters/application-test.js b/addons/api/tests/unit/adapters/application-test.js index 503b75762..b3c59adf3 100644 --- a/addons/api/tests/unit/adapters/application-test.js +++ b/addons/api/tests/unit/adapters/application-test.js @@ -24,15 +24,15 @@ module('Unit | Adapter | application', function (hooks) { assert.equal(adapter.urlPrefix(), config.api.namespace); }); - test('it generates URL prefixes with optional `scope_id` from adapterOptions', function (assert) { + test('it generates URL prefixes with optional `scopeID` from adapterOptions', function (assert) { assert.expect(2); - const scope_id = 'global'; + const scopeID = 'global'; const adapter = this.owner.lookup('adapter:application'); const prefix = adapter.urlPrefix(null, null, null, null, { - adapterOptions: {scope_id} + adapterOptions: {scopeID} }); assert.ok(config.api.namespace); - assert.equal(prefix, `v1/${scope_id}`); + assert.equal(prefix, `v1/${scopeID}`); }); test('it generates correct default URL suffixes', function (assert) { @@ -54,11 +54,11 @@ module('Unit | Adapter | application', function (hooks) { test('it generates correct complete URLs', function (assert) { assert.expect(6); - const scope_id = 'o_1'; + const scopeID = 'o_1'; const method = 'my-custom-method'; const mockSnapshot = { adapterOptions: { - scope_id, + scopeID, method } }; @@ -85,7 +85,7 @@ module('Unit | Adapter | application', function (hooks) { assert.ok(true, 'Scoped resource URL was requested.'); return {items: []}; }); - await store.findAll('group', {adapterOptions: {scope_id: 'p_456'}}); + await store.findAll('group', {adapterOptions: {scopeID: 'p_456'}}); }); test('it rewrites PUT to PATCH, but leaves others unchanged', function (assert) { diff --git a/addons/api/tests/unit/models/base-test.js b/addons/api/tests/unit/models/base-test.js index 066ba456e..516dbf240 100644 --- a/addons/api/tests/unit/models/base-test.js +++ b/addons/api/tests/unit/models/base-test.js @@ -117,17 +117,17 @@ module('Unit | Model | base', function (hooks) { assert.ok(true, 'Correctly scoped create record URL was requested.') return {}; }); - await model.save({adapterOptions: {scope_id: customScopeID}}); + await model.save({adapterOptions: {scopeID: customScopeID}}); this.server.patch('/v1/global/users', () => { assert.ok(true, 'Correctly scoped update record URL was requested.') return {}; }); - await model.save({adapterOptions: {scope_id: customScopeID}}); + await model.save({adapterOptions: {scopeID: customScopeID}}); this.server.delete('/v1/global/users', () => { assert.ok(true, 'Correctly scoped delete record URL was requested.') return {}; }); - await model.destroyRecord({adapterOptions: {scope_id: customScopeID}}); + await model.destroyRecord({adapterOptions: {scopeID: customScopeID}}); }); test('it saves records to a URL with a custom method if requested', async function (assert) { From d9795daf1b53ef354febe67cba3c9561c18cb964 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Thu, 16 Jul 2020 18:45:08 -0400 Subject: [PATCH 16/70] refactor: relationships are expressed as objects {id} rather than string ID fields --- addons/api/addon/serializers/application.js | 17 ++++++++++++++++- addons/api/tests/unit/models/scope-test.js | 10 +++++----- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/addons/api/addon/serializers/application.js b/addons/api/addon/serializers/application.js index 8eaf3c18b..0a9a23874 100644 --- a/addons/api/addon/serializers/application.js +++ b/addons/api/addon/serializers/application.js @@ -30,7 +30,7 @@ export default class ApplicationSerializer extends RESTSerializer { * @return {string} */ keyForRelationship(rel) { - return `${underscore(rel)}_id`; + return underscore(rel); } /** @@ -137,4 +137,19 @@ export default class ApplicationSerializer extends RESTSerializer { ); } + /** + * Returns the default extraction with one difference. If the extracted + * relationship does not include a `type` field as required by JSON API + * (the Ember internal record representation), one is added. + * @override + * @param {Object} relationshipModelName + * @param {Object} relationshipHash + * @return {Object} + */ + extractRelationship(relationshipModelName/*, relationshipHash*/) { + const relationship = super.extractRelationship(...arguments); + if (!relationship.type) relationship.type = relationshipModelName; + return relationship; + } + } diff --git a/addons/api/tests/unit/models/scope-test.js b/addons/api/tests/unit/models/scope-test.js index 37762f9ce..ff309f64b 100644 --- a/addons/api/tests/unit/models/scope-test.js +++ b/addons/api/tests/unit/models/scope-test.js @@ -19,11 +19,11 @@ module('Unit | Model | scope', function(hooks) { this.server.get('/v1/scopes', () => ({ items: [ {id: 'global', type: 'global'}, - {id: 'o_1', type: 'org', scope_id: 'global'}, - {id: 'o_2', type: 'org', scope_id: 'global'}, - {id: 'p_1', type: 'project', scope_id: 'o_1'}, - {id: 'p_2', type: 'project', scope_id: 'o_1'}, - {id: 'p_3', type: 'project', scope_id: 'o_2'} + {id: 'o_1', type: 'org', scope: {id: 'global'}}, + {id: 'o_2', type: 'org', scope: {id: 'global'}}, + {id: 'p_1', type: 'project', scope: {id: 'o_1'}}, + {id: 'p_2', type: 'project', scope: {id: 'o_1'}}, + {id: 'p_3', type: 'project', scope: {id: 'o_2'}}, ] })); const scopes = await store.findAll('scope'); From 3d432950d663db4268286689c13b278ba1ca0240 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Wed, 22 Jul 2020 11:47:33 -0400 Subject: [PATCH 17/70] refactor: represent scopes as fragments and special-case serialization of parent IDs on scopes themselves --- addons/api/addon/models/base.js | 24 +++++-- .../api/addon/models/fragment-parent-scope.js | 36 +++++++++++ addons/api/addon/models/scope.js | 15 ----- addons/api/addon/serializers/application.js | 24 ------- addons/api/addon/serializers/scope.js | 35 +++++++++++ .../api/app/models/fragment-parent-scope.js | 1 + addons/api/app/serializers/scope.js | 1 + addons/api/package.json | 1 + addons/api/tests/unit/models/base-test.js | 18 +++--- addons/api/tests/unit/models/scope-test.js | 62 ++++--------------- .../unit/serializers/application-test.js | 7 ++- .../api/tests/unit/serializers/scope-test.js | 32 ++++++++++ 12 files changed, 148 insertions(+), 108 deletions(-) create mode 100644 addons/api/addon/models/fragment-parent-scope.js create mode 100644 addons/api/addon/serializers/scope.js create mode 100644 addons/api/app/models/fragment-parent-scope.js create mode 100644 addons/api/app/serializers/scope.js create mode 100644 addons/api/tests/unit/serializers/scope-test.js diff --git a/addons/api/addon/models/base.js b/addons/api/addon/models/base.js index a1716da97..b823ef826 100644 --- a/addons/api/addon/models/base.js +++ b/addons/api/addon/models/base.js @@ -1,19 +1,33 @@ import Model from '@ember-data/model'; -import { belongsTo } from '@ember-data/model'; +import { fragment } from 'ember-data-model-fragments/attributes'; import { computed } from '@ember/object'; +import { get } from '@ember/object'; export default class BaseModel extends Model { // =relationships /** - * Scope of this resource, if any. - * @type {ScopeModel} + * Scope of this resource, if any, represented as a JSON fragment. + * @type {FragmentParentScope} */ - @belongsTo('scope', {async: false, inverse: false}) scope; + @fragment('fragment-parent-scope') scope; // =attributes + /** + * Convenience for getting and setting the parent scope ID. + * @type {string + */ + @computed('scope.scope_id') + get scopeID() { + return get(this, 'scope.scope_id'); + } + set scopeID(id) { + if (!this.scope) this.scope = {}; + this.scope.scope_id = id; + } + /** * Names are optional on models in our API. Thus we need to fallback on ID * for display purposes. @@ -61,7 +75,7 @@ export default class BaseModel extends Model { * @return {Promise} */ save(options={adapterOptions: {}}) { - const scopeID = this.belongsTo('scope').id(); + const scopeID = this.scopeID; if (scopeID && !options.adapterOptions.scopeID) { options.adapterOptions.scopeID = scopeID; } diff --git a/addons/api/addon/models/fragment-parent-scope.js b/addons/api/addon/models/fragment-parent-scope.js new file mode 100644 index 000000000..2f4831a0e --- /dev/null +++ b/addons/api/addon/models/fragment-parent-scope.js @@ -0,0 +1,36 @@ +import Fragment from 'ember-data-model-fragments/fragment'; +import { computed } from '@ember/object'; +import { attr } from '@ember-data/model'; +import { scopeTypes } from './scope'; + +export default class FragmentParentScope extends Fragment { + + // =attributes + + @attr('string', { + description: 'ID of the related scope' + }) scope_id; + + @attr('string', { + description: 'The type of the resource' + }) type; + + /** + * @type {boolean} + */ + @computed('type') + get isGlobal() { return this.type === scopeTypes.global; } + + /** + * @type {boolean} + */ + @computed('type') + get isOrg() { return this.type === scopeTypes.org; } + + /** + * @type {boolean} + */ + @computed('type') + get isProject() { return this.type === scopeTypes.project; } + +} diff --git a/addons/api/addon/models/scope.js b/addons/api/addon/models/scope.js index f61146690..061501759 100644 --- a/addons/api/addon/models/scope.js +++ b/addons/api/addon/models/scope.js @@ -1,5 +1,4 @@ import GeneratedScopeModel from '../generated/models/scope'; -import { belongsTo, hasMany } from '@ember-data/model'; import { computed } from '@ember/object'; export const scopeTypes = { @@ -10,20 +9,6 @@ export const scopeTypes = { export default class ScopeModel extends GeneratedScopeModel { - // =relationships - - /** - * The parent scope, if any. - * @type {ScopeModel} - */ - @belongsTo('scope', {inverse: 'children'}) scope; - - /** - * The inverse of parent scope, children. - * @type {ScopeModelp[]} - */ - @hasMany('scope', {inverse: 'scope'}) children; - // =attributes /** diff --git a/addons/api/addon/serializers/application.js b/addons/api/addon/serializers/application.js index 0a9a23874..84bcc6ed5 100644 --- a/addons/api/addon/serializers/application.js +++ b/addons/api/addon/serializers/application.js @@ -24,15 +24,6 @@ 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); - } - /** * If an attribute is annotated as readOnly in the model, don't serialize it. * Otherwise delegate to default attribute serializer. @@ -137,19 +128,4 @@ export default class ApplicationSerializer extends RESTSerializer { ); } - /** - * Returns the default extraction with one difference. If the extracted - * relationship does not include a `type` field as required by JSON API - * (the Ember internal record representation), one is added. - * @override - * @param {Object} relationshipModelName - * @param {Object} relationshipHash - * @return {Object} - */ - extractRelationship(relationshipModelName/*, relationshipHash*/) { - const relationship = super.extractRelationship(...arguments); - if (!relationship.type) relationship.type = relationshipModelName; - return relationship; - } - } diff --git a/addons/api/addon/serializers/scope.js b/addons/api/addon/serializers/scope.js new file mode 100644 index 000000000..ebb4db7ef --- /dev/null +++ b/addons/api/addon/serializers/scope.js @@ -0,0 +1,35 @@ +import ApplicationSerializer from './application'; + +export default class ScopeSerializer extends ApplicationSerializer { + + // =attributes + + attrs = { + scope: { serialize: true } + }; + + // =methods + + /** + * While most resources do not serialize scope into a request body (scope + * is expressed in the URL), scopes themselves _do serialize scope_ as a + * string field `parent_scope_id`. + * Otherwise delegate to default attribute serializer. + * + * @override + * @method serializeAttribute + * @param {Snapshot} snapshot + * @param {Object} json + * @param {String} key + * @param {Object} attribute + */ + serializeAttribute(snapshot, json, key) { + let value = super.serializeAttribute(...arguments); + if (key === 'scope' && this._canSerialize(key)) { + json.parent_scope_id = snapshot.attr('scope').attr('scope_id'); + delete json[key]; + } + return value; + } + +} diff --git a/addons/api/app/models/fragment-parent-scope.js b/addons/api/app/models/fragment-parent-scope.js new file mode 100644 index 000000000..fdfccb452 --- /dev/null +++ b/addons/api/app/models/fragment-parent-scope.js @@ -0,0 +1 @@ +export { default } from 'api/models/fragment-parent-scope'; diff --git a/addons/api/app/serializers/scope.js b/addons/api/app/serializers/scope.js new file mode 100644 index 000000000..cdc15f3a2 --- /dev/null +++ b/addons/api/app/serializers/scope.js @@ -0,0 +1 @@ +export { default } from 'api/serializers/scope'; diff --git a/addons/api/package.json b/addons/api/package.json index 00d40d798..5c52d99d5 100644 --- a/addons/api/package.json +++ b/addons/api/package.json @@ -41,6 +41,7 @@ "ember-cli-uglify": "^3.0.0", "ember-copy": "^1.0.0", "ember-data": "^3.16.0", + "ember-data-model-fragments": "5.0.0-beta.0", "ember-disable-prototype-extensions": "^1.1.3", "ember-export-application-global": "^2.0.1", "ember-get-config": "^0.2.4", diff --git a/addons/api/tests/unit/models/base-test.js b/addons/api/tests/unit/models/base-test.js index 516dbf240..3d8336e83 100644 --- a/addons/api/tests/unit/models/base-test.js +++ b/addons/api/tests/unit/models/base-test.js @@ -26,20 +26,17 @@ module('Unit | Model | base', function (hooks) { data: { id: '123abc', type: 'user', - attributes: {}, - relationships: { + attributes: { scope: { - data: { - id: 'o_1', - type: 'scope' - } + scope_id: 'o_1', + type: 'scope' } } } }); const scope = store.peekRecord('scope', 'o_1'); const model = store.peekRecord('user', '123abc'); - assert.equal(model.scope, scope); + assert.equal(model.scope.scope_id, scope.id); }); test('it has a displayName attribute', function (assert) { @@ -88,8 +85,7 @@ module('Unit | Model | base', function (hooks) { test('it saves records to a scoped URL', async function (assert) { assert.expect(3); const store = this.owner.lookup('service:store'); - const scope = store.createRecord('scope', {id: 'o_123'}); - const model = store.createRecord('user', { scope }); + const model = store.createRecord('user', { scopeID: 'o_123' }); this.server.post('/v1/o_123/users', () => { assert.ok(true, 'Correctly scoped create record URL was requested.') return {}; @@ -111,7 +107,7 @@ module('Unit | Model | base', function (hooks) { assert.expect(3); const store = this.owner.lookup('service:store'); const customScopeID = 'global'; - const scope = store.createRecord('scope', {id: 'o_123'}); + const scope = {scope_id: 'o_123'}; const model = store.createRecord('user', { scope }); this.server.post('/v1/global/users', () => { assert.ok(true, 'Correctly scoped create record URL was requested.') @@ -134,7 +130,7 @@ module('Unit | Model | base', function (hooks) { assert.expect(1); const store = this.owner.lookup('service:store'); const method = 'my-custom-method'; - const scope = store.createRecord('scope', {id: 'o_123'}); + const scope = {scope_id: 'o_123'}; const model = store.createRecord('user', { scope }); this.server.post(`/v1/o_123/users:${method}`, () => { assert.ok(true, 'Correctly scoped create record URL was requested.') diff --git a/addons/api/tests/unit/models/scope-test.js b/addons/api/tests/unit/models/scope-test.js index ff309f64b..ee2e212b9 100644 --- a/addons/api/tests/unit/models/scope-test.js +++ b/addons/api/tests/unit/models/scope-test.js @@ -6,65 +6,27 @@ module('Unit | Model | scope', function(hooks) { setupTest(hooks); setupMirage(hooks); - test('it exists', function(assert) { - assert.expect(1); - const store = this.owner.lookup('service:store'); - const model = store.createRecord('scope', {}); - assert.ok(model); - }); - - test('it has self-referential parent and child relationships', async function(assert) { - assert.expect(9); + test('it may have a scope fragment', async function(assert) { + assert.expect(6); const store = this.owner.lookup('service:store'); this.server.get('/v1/scopes', () => ({ items: [ {id: 'global', type: 'global'}, - {id: 'o_1', type: 'org', scope: {id: 'global'}}, - {id: 'o_2', type: 'org', scope: {id: 'global'}}, - {id: 'p_1', type: 'project', scope: {id: 'o_1'}}, - {id: 'p_2', type: 'project', scope: {id: 'o_1'}}, - {id: 'p_3', type: 'project', scope: {id: 'o_2'}}, + {id: 'o_1', type: 'org', scope: {scope_id: 'global'}}, + {id: 'o_2', type: 'org', scope: {scope_id: 'global'}}, + {id: 'p_1', type: 'project', scope: {scope_id: 'o_1'}}, + {id: 'p_2', type: 'project', scope: {scope_id: 'o_1'}}, + {id: 'p_3', type: 'project', scope: {scope_id: 'o_2'}}, ] })); const scopes = await store.findAll('scope'); // check integrity of scope relationships assert.notOk(await scopes.firstObject.get('scope'), 'Global scope has no parent'); - assert.equal(await scopes.firstObject.get('children.length'), 2, 'Global scope has two children scopes'); - assert.equal(await scopes.objectAt(1).get('scope.id'), 'global', 'Org 1 parent scope is global'); - assert.equal(await scopes.objectAt(2).get('scope.id'), 'global', 'Org 2 parent scope is global'); - assert.equal(await scopes.objectAt(1).get('children.length'), 2, 'Org 1 has two children scopes'); - assert.equal(await scopes.objectAt(2).get('children.length'), 1, 'Org 2 has one child scope'); - assert.equal(await scopes.objectAt(3).get('scope.id'), 'o_1', 'Project 1 parent scope is org 1'); - assert.equal(await scopes.objectAt(4).get('scope.id'), 'o_1', 'Project 2 parent scope is org 1'); - assert.equal(await scopes.objectAt(5).get('scope.id'), 'o_2', 'Project 3 parent scope is org 2'); - }); - - test('it can load the parent scope if it is not already loaded', async function(assert) { - assert.expect(3); - const store = this.owner.lookup('service:store'); - store.push({ - data: { - id: 'p_1', - type: 'scope', - attributes: {}, - relationships: { - scope: { - data: { - id: 'o_1', - type: 'scope' - } - } - } - } - }); - this.server.get('/v1/scopes/o_1', () => { - assert.ok(true, 'Correct parent scope was requested.'); - return { id: 'o_1' }; - }); - const project = store.peekRecord('scope', 'p_1'); - assert.equal(project.belongsTo('scope').id(), 'o_1'); - const org = await project.get('scope'); - assert.equal(org.id, 'o_1'); + assert.equal(await scopes.objectAt(1).get('scope.scope_id'), 'global', 'Org 1 parent scope is global'); + assert.equal(await scopes.objectAt(2).get('scope.scope_id'), 'global', 'Org 2 parent scope is global'); + assert.equal(await scopes.objectAt(3).get('scope.scope_id'), 'o_1', 'Project 1 parent scope is org 1'); + assert.equal(await scopes.objectAt(4).get('scope.scope_id'), 'o_1', 'Project 2 parent scope is org 1'); + assert.equal(await scopes.objectAt(5).get('scope.scope_id'), 'o_2', 'Project 3 parent scope is org 2'); }); test('it has isType boolean getters and setters', async function(assert) { diff --git a/addons/api/tests/unit/serializers/application-test.js b/addons/api/tests/unit/serializers/application-test.js index e076babb6..83c75341b 100644 --- a/addons/api/tests/unit/serializers/application-test.js +++ b/addons/api/tests/unit/serializers/application-test.js @@ -14,17 +14,18 @@ module('Unit | Serializer | application', function (hooks) { test('it serializes records', function (assert) { assert.expect(1); const store = this.owner.lookup('service:store'); - const scope = store.createRecord('scope', {id: 'global'}); const record = store.createRecord('user', { name: 'User', description: 'Description', - scope + scope: { + scope_id: 'global', + type: 'global' + } }); const serializedRecord = record.serialize(); assert.deepEqual(serializedRecord, { name: 'User', description: 'Description' - //disabled: false, // TODO: disabled is temporarily disabled }); }); diff --git a/addons/api/tests/unit/serializers/scope-test.js b/addons/api/tests/unit/serializers/scope-test.js new file mode 100644 index 000000000..1f1823da1 --- /dev/null +++ b/addons/api/tests/unit/serializers/scope-test.js @@ -0,0 +1,32 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Serializer | scope', function(hooks) { + setupTest(hooks); + + // Replace this with your real tests. + test('it exists', function(assert) { + let store = this.owner.lookup('service:store'); + let serializer = store.serializerFor('scope'); + + assert.ok(serializer); + }); + + test('it serializes records', function (assert) { + assert.expect(1); + const store = this.owner.lookup('service:store'); + const record = store.createRecord('scope', { + type: 'org', + name: 'User', + description: 'Description', + scopeID: 'global' + }); + const serializedRecord = record.serialize(); + assert.deepEqual(serializedRecord, { + type: 'org', + name: 'User', + description: 'Description', + parent_scope_id: 'global' + }); + }); +}); From fd145968e36041244099414687e23375bad8f7d2 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Wed, 22 Jul 2020 12:08:10 -0400 Subject: [PATCH 18/70] chore: add scope conveniences --- addons/api/addon/models/base.js | 30 + addons/api/addon/serializers/scope.js | 7 +- addons/api/tests/unit/models/base-test.js | 13 +- yarn.lock | 864 +++++++++++++++++++++- 4 files changed, 907 insertions(+), 7 deletions(-) diff --git a/addons/api/addon/models/base.js b/addons/api/addon/models/base.js index b823ef826..640e54c81 100644 --- a/addons/api/addon/models/base.js +++ b/addons/api/addon/models/base.js @@ -3,6 +3,28 @@ import { fragment } from 'ember-data-model-fragments/attributes'; import { computed } from '@ember/object'; import { get } from '@ember/object'; +/** + * Base model class provides common functionality for all models. + * + * One of the most interesting features of most resources is the + * `scope` field, which arrives from the API as an embedded JSON fragment. + * It contains information such as the parent scope ID and type. This scope ID + * is then used when generation scoped resource URLs via the + * `adapterOptions.scopeID` option (see `save` method below). + * + * For convenience, developers may interact with scope in a number of ways. + * The simplest method, when you don't care about other information, is to set + * the `scopeID`, e.g. `record.scopeID = 'o_123'`. The scope JSON fragment + * may be set directly, `record.scope = { scope_id: 'o_123' }`. + * For convenience, you may even set the scope using a `Scope` model: + * `record.scopeModel = scopeModelInstance`, which automatically converts the + * scope to a JSON fragment of the proper shape and assigns it to `scope`. + * + * A record's scope may be read in one of two ways: + * + * 1) As a JSON fragment: `record.scope`, + * 2) Or as an ID: `record.scopeID`. + */ export default class BaseModel extends Model { // =relationships @@ -15,6 +37,14 @@ export default class BaseModel extends Model { // =attributes + set scopeModel(model) { + if (model) { + const json = model.serialize(); + json.scope_id = model.id; + this.scope = json; + } + } + /** * Convenience for getting and setting the parent scope ID. * @type {string diff --git a/addons/api/addon/serializers/scope.js b/addons/api/addon/serializers/scope.js index ebb4db7ef..da578ea0f 100644 --- a/addons/api/addon/serializers/scope.js +++ b/addons/api/addon/serializers/scope.js @@ -24,9 +24,10 @@ export default class ScopeSerializer extends ApplicationSerializer { * @param {Object} attribute */ serializeAttribute(snapshot, json, key) { - let value = super.serializeAttribute(...arguments); - if (key === 'scope' && this._canSerialize(key)) { - json.parent_scope_id = snapshot.attr('scope').attr('scope_id'); + const value = super.serializeAttribute(...arguments); + const parentScope = snapshot.attr('scope'); + if (key === 'scope' && this._canSerialize(key) && parentScope) { + json.parent_scope_id = parentScope.attr('scope_id'); delete json[key]; } return value; diff --git a/addons/api/tests/unit/models/base-test.js b/addons/api/tests/unit/models/base-test.js index 3d8336e83..99d74a81f 100644 --- a/addons/api/tests/unit/models/base-test.js +++ b/addons/api/tests/unit/models/base-test.js @@ -13,7 +13,7 @@ module('Unit | Model | base', function (hooks) { assert.ok(model); }); - test('it may have a scope relationship', function (assert) { + test('it may have a scope fragment', function (assert) { assert.expect(1); const store = this.owner.lookup('service:store'); store.push({ @@ -39,6 +39,17 @@ module('Unit | Model | base', function (hooks) { assert.equal(model.scope.scope_id, scope.id); }); + test('it may accept a `scopeModel` for convenience, instead of a fragment', function (assert) { + assert.expect(3); + const store = this.owner.lookup('service:store'); + const scope = store.createRecord('scope', {id: 'o_1', type: 'org'}); + const model = store.createRecord('user', '123abc'); + assert.notEqual(model.scopeID, scope.id); + model.scopeModel = scope; + assert.equal(model.scopeID, scope.id); + assert.equal(model.scope.isOrg, true); + }); + test('it has a displayName attribute', function (assert) { assert.expect(3); const store = this.owner.lookup('service:store'); diff --git a/yarn.lock b/yarn.lock index 5c4a10c5f..c7cf88da0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -16,6 +16,22 @@ dependencies: "@babel/highlight" "^7.8.3" +"@babel/code-frame@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.4.tgz#168da1a36e90da68ae8d49c0f1b48c7c6249213a" + integrity sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg== + dependencies: + "@babel/highlight" "^7.10.4" + +"@babel/compat-data@^7.10.4": + version "7.10.5" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.10.5.tgz#d38425e67ea96b1480a3f50404d1bf85676301a6" + integrity sha512-mPVoWNzIpYJHbWje0if7Ck36bpbtTvIxOi9+6WSK9wjGEXearAqlwBoTQvVjsAY2VIwgcs8V940geY3okzRCEw== + dependencies: + browserslist "^4.12.0" + invariant "^2.2.4" + semver "^5.5.0" + "@babel/compat-data@^7.9.6": version "7.9.6" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.9.6.tgz#3f604c40e420131affe6f2c8052e9a275ae2049b" @@ -47,6 +63,37 @@ semver "^5.4.1" source-map "^0.5.0" +"@babel/core@^7.10.2": + version "7.10.5" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.10.5.tgz#1f15e2cca8ad9a1d78a38ddba612f5e7cdbbd330" + integrity sha512-O34LQooYVDXPl7QWCdW9p4NR+QlzOr7xShPPJz8GsuCU3/8ua/wqTr7gmnxXv+WBESiGU/G5s16i6tUvHkNb+w== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/generator" "^7.10.5" + "@babel/helper-module-transforms" "^7.10.5" + "@babel/helpers" "^7.10.4" + "@babel/parser" "^7.10.5" + "@babel/template" "^7.10.4" + "@babel/traverse" "^7.10.5" + "@babel/types" "^7.10.5" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.1" + json5 "^2.1.2" + lodash "^4.17.19" + resolve "^1.3.2" + semver "^5.4.1" + source-map "^0.5.0" + +"@babel/generator@^7.10.5": + version "7.10.5" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.10.5.tgz#1b903554bc8c583ee8d25f1e8969732e6b829a69" + integrity sha512-3vXxr3FEW7E7lJZiWQ3bM4+v/Vyr9C+hpolQ8BGFr9Y8Ri2tFLWTixmwKBafDujO1WVah4fhZBeU1bieKdghig== + dependencies: + "@babel/types" "^7.10.5" + jsesc "^2.5.1" + source-map "^0.5.0" + "@babel/generator@^7.4.0", "@babel/generator@^7.9.6": version "7.9.6" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.9.6.tgz#5408c82ac5de98cda0d77d8124e99fa1f2170a43" @@ -57,6 +104,13 @@ lodash "^4.17.13" source-map "^0.5.0" +"@babel/helper-annotate-as-pure@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.10.4.tgz#5bf0d495a3f757ac3bda48b5bf3b3ba309c72ba3" + integrity sha512-XQlqKQP4vXFB7BN8fEEerrmYvHp3fK/rBkRFz9jaJbzK0B1DSfej9Kc7ZzE8Z/OnId1jpJdNAZ3BFQjWG68rcA== + dependencies: + "@babel/types" "^7.10.4" + "@babel/helper-annotate-as-pure@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.8.3.tgz#60bc0bc657f63a0924ff9a4b4a0b24a13cf4deee" @@ -64,6 +118,14 @@ dependencies: "@babel/types" "^7.8.3" +"@babel/helper-builder-binary-assignment-operator-visitor@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.10.4.tgz#bb0b75f31bf98cbf9ff143c1ae578b87274ae1a3" + integrity sha512-L0zGlFrGWZK4PbT8AszSfLTM5sDU1+Az/En9VrdT8/LmEiJt4zXt+Jve9DCAnQcbqDhCI+29y/L93mrDzddCcg== + dependencies: + "@babel/helper-explode-assignable-expression" "^7.10.4" + "@babel/types" "^7.10.4" + "@babel/helper-builder-binary-assignment-operator-visitor@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.8.3.tgz#c84097a427a061ac56a1c30ebf54b7b22d241503" @@ -89,6 +151,17 @@ "@babel/helper-annotate-as-pure" "^7.8.3" "@babel/types" "^7.9.0" +"@babel/helper-compilation-targets@^7.10.2", "@babel/helper-compilation-targets@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.10.4.tgz#804ae8e3f04376607cc791b9d47d540276332bd2" + integrity sha512-a3rYhlsGV0UHNDvrtOXBg8/OpfV0OKTkxKPzIplS1zpx7CygDcWWxckxZeDd3gzPzC4kUT0A4nVFDK0wGMh4MQ== + dependencies: + "@babel/compat-data" "^7.10.4" + browserslist "^4.12.0" + invariant "^2.2.4" + levenary "^1.1.1" + semver "^5.5.0" + "@babel/helper-compilation-targets@^7.8.7", "@babel/helper-compilation-targets@^7.9.6": version "7.9.6" resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.9.6.tgz#1e05b7ccc9d38d2f8b40b458b380a04dcfadd38a" @@ -100,6 +173,18 @@ levenary "^1.1.1" semver "^5.5.0" +"@babel/helper-create-class-features-plugin@^7.10.4", "@babel/helper-create-class-features-plugin@^7.10.5": + version "7.10.5" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.10.5.tgz#9f61446ba80e8240b0a5c85c6fdac8459d6f259d" + integrity sha512-0nkdeijB7VlZoLT3r/mY3bUkw3T8WG/hNw+FATs/6+pG2039IJWjTYL0VTISqsNHMUTEnwbVnc89WIJX9Qed0A== + dependencies: + "@babel/helper-function-name" "^7.10.4" + "@babel/helper-member-expression-to-functions" "^7.10.5" + "@babel/helper-optimise-call-expression" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-replace-supers" "^7.10.4" + "@babel/helper-split-export-declaration" "^7.10.4" + "@babel/helper-create-class-features-plugin@^7.5.5", "@babel/helper-create-class-features-plugin@^7.8.3", "@babel/helper-create-class-features-plugin@^7.9.6": version "7.9.6" resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.9.6.tgz#965c8b0a9f051801fd9d3b372ca0ccf200a90897" @@ -112,6 +197,15 @@ "@babel/helper-replace-supers" "^7.9.6" "@babel/helper-split-export-declaration" "^7.8.3" +"@babel/helper-create-regexp-features-plugin@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.10.4.tgz#fdd60d88524659a0b6959c0579925e425714f3b8" + integrity sha512-2/hu58IEPKeoLF45DBwx3XFqsbCXmkdAay4spVr2x0jYgRxrSNp+ePwvSsy9g6YSaNDcKIQVPXk1Ov8S2edk2g== + dependencies: + "@babel/helper-annotate-as-pure" "^7.10.4" + "@babel/helper-regex" "^7.10.4" + regexpu-core "^4.7.0" + "@babel/helper-create-regexp-features-plugin@^7.8.3", "@babel/helper-create-regexp-features-plugin@^7.8.8": version "7.8.8" resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.8.8.tgz#5d84180b588f560b7864efaeea89243e58312087" @@ -121,6 +215,15 @@ "@babel/helper-regex" "^7.8.3" regexpu-core "^4.7.0" +"@babel/helper-define-map@^7.10.4": + version "7.10.5" + resolved "https://registry.yarnpkg.com/@babel/helper-define-map/-/helper-define-map-7.10.5.tgz#b53c10db78a640800152692b13393147acb9bb30" + integrity sha512-fMw4kgFB720aQFXSVaXr79pjjcW5puTCM16+rECJ/plGS+zByelE8l9nCpV1GibxTnFVmUuYG9U8wYfQHdzOEQ== + dependencies: + "@babel/helper-function-name" "^7.10.4" + "@babel/types" "^7.10.5" + lodash "^4.17.19" + "@babel/helper-define-map@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/helper-define-map/-/helper-define-map-7.8.3.tgz#a0655cad5451c3760b726eba875f1cd8faa02c15" @@ -130,6 +233,14 @@ "@babel/types" "^7.8.3" lodash "^4.17.13" +"@babel/helper-explode-assignable-expression@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.10.4.tgz#40a1cd917bff1288f699a94a75b37a1a2dbd8c7c" + integrity sha512-4K71RyRQNPRrR85sr5QY4X3VwG4wtVoXZB9+L3r1Gp38DhELyHCtovqydRi7c1Ovb17eRGiQ/FD5s8JdU0Uy5A== + dependencies: + "@babel/traverse" "^7.10.4" + "@babel/types" "^7.10.4" + "@babel/helper-explode-assignable-expression@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.8.3.tgz#a728dc5b4e89e30fc2dfc7d04fa28a930653f982" @@ -138,6 +249,15 @@ "@babel/traverse" "^7.8.3" "@babel/types" "^7.8.3" +"@babel/helper-function-name@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz#d2d3b20c59ad8c47112fa7d2a94bc09d5ef82f1a" + integrity sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ== + dependencies: + "@babel/helper-get-function-arity" "^7.10.4" + "@babel/template" "^7.10.4" + "@babel/types" "^7.10.4" + "@babel/helper-function-name@^7.8.3", "@babel/helper-function-name@^7.9.5": version "7.9.5" resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.9.5.tgz#2b53820d35275120e1874a82e5aabe1376920a5c" @@ -147,6 +267,13 @@ "@babel/template" "^7.8.3" "@babel/types" "^7.9.5" +"@babel/helper-get-function-arity@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz#98c1cbea0e2332f33f9a4661b8ce1505b2c19ba2" + integrity sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A== + dependencies: + "@babel/types" "^7.10.4" + "@babel/helper-get-function-arity@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz#b894b947bd004381ce63ea1db9f08547e920abd5" @@ -154,6 +281,13 @@ dependencies: "@babel/types" "^7.8.3" +"@babel/helper-hoist-variables@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.10.4.tgz#d49b001d1d5a68ca5e6604dda01a6297f7c9381e" + integrity sha512-wljroF5PgCk2juF69kanHVs6vrLwIPNp6DLD+Lrl3hoQ3PpPPikaDRNFA+0t81NOoMt2DL6WW/mdU8k4k6ZzuA== + dependencies: + "@babel/types" "^7.10.4" + "@babel/helper-hoist-variables@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.8.3.tgz#1dbe9b6b55d78c9b4183fc8cdc6e30ceb83b7134" @@ -161,6 +295,13 @@ dependencies: "@babel/types" "^7.8.3" +"@babel/helper-member-expression-to-functions@^7.10.4", "@babel/helper-member-expression-to-functions@^7.10.5": + version "7.10.5" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.10.5.tgz#172f56e7a63e78112f3a04055f24365af702e7ee" + integrity sha512-HiqJpYD5+WopCXIAbQDG0zye5XYVvcO9w/DHp5GsaGkRUaamLj2bEtu6i8rnGGprAhHM3qidCMgp71HF4endhA== + dependencies: + "@babel/types" "^7.10.5" + "@babel/helper-member-expression-to-functions@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.8.3.tgz#659b710498ea6c1d9907e0c73f206eee7dadc24c" @@ -175,6 +316,26 @@ dependencies: "@babel/types" "^7.8.3" +"@babel/helper-module-imports@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.10.4.tgz#4c5c54be04bd31670a7382797d75b9fa2e5b5620" + integrity sha512-nEQJHqYavI217oD9+s5MUBzk6x1IlvoS9WTPfgG43CbMEeStE0v+r+TucWdx8KFGowPGvyOkDT9+7DHedIDnVw== + dependencies: + "@babel/types" "^7.10.4" + +"@babel/helper-module-transforms@^7.10.4", "@babel/helper-module-transforms@^7.10.5": + version "7.10.5" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.10.5.tgz#120c271c0b3353673fcdfd8c053db3c544a260d6" + integrity sha512-4P+CWMJ6/j1W915ITJaUkadLObmCRRSC234uctJfn/vHrsLNxsR8dwlcXv9ZhJWzl77awf+mWXSZEKt5t0OnlA== + dependencies: + "@babel/helper-module-imports" "^7.10.4" + "@babel/helper-replace-supers" "^7.10.4" + "@babel/helper-simple-access" "^7.10.4" + "@babel/helper-split-export-declaration" "^7.10.4" + "@babel/template" "^7.10.4" + "@babel/types" "^7.10.5" + lodash "^4.17.19" + "@babel/helper-module-transforms@^7.9.0": version "7.9.0" resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.9.0.tgz#43b34dfe15961918707d247327431388e9fe96e5" @@ -188,6 +349,13 @@ "@babel/types" "^7.9.0" lodash "^4.17.13" +"@babel/helper-optimise-call-expression@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.4.tgz#50dc96413d594f995a77905905b05893cd779673" + integrity sha512-n3UGKY4VXwXThEiKrgRAoVPBMqeoPgHVqiHZOanAJCG9nQUL2pLRQirUzl0ioKclHGpGqRgIOkgcIJaIWLpygg== + dependencies: + "@babel/types" "^7.10.4" + "@babel/helper-optimise-call-expression@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.8.3.tgz#7ed071813d09c75298ef4f208956006b6111ecb9" @@ -200,6 +368,18 @@ resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz#9ea293be19babc0f52ff8ca88b34c3611b208670" integrity sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ== +"@babel/helper-plugin-utils@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz#2f75a831269d4f677de49986dff59927533cf375" + integrity sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg== + +"@babel/helper-regex@^7.10.4": + version "7.10.5" + resolved "https://registry.yarnpkg.com/@babel/helper-regex/-/helper-regex-7.10.5.tgz#32dfbb79899073c415557053a19bd055aae50ae0" + integrity sha512-68kdUAzDrljqBrio7DYAEgCoJHxppJOERHOgOrDN7WjOzP0ZQ1LsSDRXcemzVZaLvjaJsJEESb6qt+znNuENDg== + dependencies: + lodash "^4.17.19" + "@babel/helper-regex@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/helper-regex/-/helper-regex-7.8.3.tgz#139772607d51b93f23effe72105b319d2a4c6965" @@ -207,6 +387,17 @@ dependencies: lodash "^4.17.13" +"@babel/helper-remap-async-to-generator@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.10.4.tgz#fce8bea4e9690bbe923056ded21e54b4e8b68ed5" + integrity sha512-86Lsr6NNw3qTNl+TBcF1oRZMaVzJtbWTyTko+CQL/tvNvcGYEFKbLXDPxtW0HKk3McNOk4KzY55itGWCAGK5tg== + dependencies: + "@babel/helper-annotate-as-pure" "^7.10.4" + "@babel/helper-wrap-function" "^7.10.4" + "@babel/template" "^7.10.4" + "@babel/traverse" "^7.10.4" + "@babel/types" "^7.10.4" + "@babel/helper-remap-async-to-generator@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.8.3.tgz#273c600d8b9bf5006142c1e35887d555c12edd86" @@ -218,6 +409,16 @@ "@babel/traverse" "^7.8.3" "@babel/types" "^7.8.3" +"@babel/helper-replace-supers@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.10.4.tgz#d585cd9388ea06e6031e4cd44b6713cbead9e6cf" + integrity sha512-sPxZfFXocEymYTdVK1UNmFPBN+Hv5mJkLPsYWwGBxZAxaWfFu+xqp7b6qWD0yjNuNL2VKc6L5M18tOXUP7NU0A== + dependencies: + "@babel/helper-member-expression-to-functions" "^7.10.4" + "@babel/helper-optimise-call-expression" "^7.10.4" + "@babel/traverse" "^7.10.4" + "@babel/types" "^7.10.4" + "@babel/helper-replace-supers@^7.8.3", "@babel/helper-replace-supers@^7.8.6", "@babel/helper-replace-supers@^7.9.6": version "7.9.6" resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.9.6.tgz#03149d7e6a5586ab6764996cd31d6981a17e1444" @@ -228,6 +429,14 @@ "@babel/traverse" "^7.9.6" "@babel/types" "^7.9.6" +"@babel/helper-simple-access@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.10.4.tgz#0f5ccda2945277a2a7a2d3a821e15395edcf3461" + integrity sha512-0fMy72ej/VEvF8ULmX6yb5MtHG4uH4Dbd6I/aHDb/JVg0bbivwt9Wg+h3uMvX+QSFtwr5MeItvazbrc4jtRAXw== + dependencies: + "@babel/template" "^7.10.4" + "@babel/types" "^7.10.4" + "@babel/helper-simple-access@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.8.3.tgz#7f8109928b4dab4654076986af575231deb639ae" @@ -236,6 +445,13 @@ "@babel/template" "^7.8.3" "@babel/types" "^7.8.3" +"@babel/helper-split-export-declaration@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.10.4.tgz#2c70576eaa3b5609b24cb99db2888cc3fc4251d1" + integrity sha512-pySBTeoUff56fL5CBU2hWm9TesA4r/rOkI9DyJLvvgz09MB9YtfIYe3iBriVaYNaPe+Alua0vBIOVOLs2buWhg== + dependencies: + "@babel/types" "^7.10.4" + "@babel/helper-split-export-declaration@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz#31a9f30070f91368a7182cf05f831781065fc7a9" @@ -243,11 +459,26 @@ dependencies: "@babel/types" "^7.8.3" +"@babel/helper-validator-identifier@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz#a78c7a7251e01f616512d31b10adcf52ada5e0d2" + integrity sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw== + "@babel/helper-validator-identifier@^7.9.0", "@babel/helper-validator-identifier@^7.9.5": version "7.9.5" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.5.tgz#90977a8e6fbf6b431a7dc31752eee233bf052d80" integrity sha512-/8arLKUFq882w4tWGj9JYzRpAlZgiWUJ+dtteNTDqrRBz9Iguck9Rn3ykuBDoUwh2TO4tSAJlrxDUOXWklJe4g== +"@babel/helper-wrap-function@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.10.4.tgz#8a6f701eab0ff39f765b5a1cfef409990e624b87" + integrity sha512-6py45WvEF0MhiLrdxtRjKjufwLL1/ob2qDJgg5JgNdojBAZSAKnAjkyOCNug6n+OBl4VW76XjvgSFTdaMcW0Ug== + dependencies: + "@babel/helper-function-name" "^7.10.4" + "@babel/template" "^7.10.4" + "@babel/traverse" "^7.10.4" + "@babel/types" "^7.10.4" + "@babel/helper-wrap-function@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.8.3.tgz#9dbdb2bb55ef14aaa01fe8c99b629bd5352d8610" @@ -258,6 +489,15 @@ "@babel/traverse" "^7.8.3" "@babel/types" "^7.8.3" +"@babel/helpers@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.10.4.tgz#2abeb0d721aff7c0a97376b9e1f6f65d7a475044" + integrity sha512-L2gX/XeUONeEbI78dXSrJzGdz4GQ+ZTA/aazfUsFaWjSe95kiCuOZ5HsXvkiw3iwF+mFHSRUfJU8t6YavocdXA== + dependencies: + "@babel/template" "^7.10.4" + "@babel/traverse" "^7.10.4" + "@babel/types" "^7.10.4" + "@babel/helpers@^7.9.6": version "7.9.6" resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.9.6.tgz#092c774743471d0bb6c7de3ad465ab3d3486d580" @@ -276,11 +516,34 @@ chalk "^2.0.0" js-tokens "^4.0.0" +"@babel/highlight@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.10.4.tgz#7d1bdfd65753538fabe6c38596cdb76d9ac60143" + integrity sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA== + dependencies: + "@babel/helper-validator-identifier" "^7.10.4" + chalk "^2.0.0" + js-tokens "^4.0.0" + "@babel/parser@^7.1.6", "@babel/parser@^7.3.4", "@babel/parser@^7.4.2", "@babel/parser@^7.4.3", "@babel/parser@^7.4.5", "@babel/parser@^7.6.0", "@babel/parser@^7.7.0", "@babel/parser@^7.8.6", "@babel/parser@^7.9.6": version "7.9.6" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.9.6.tgz#3b1bbb30dabe600cd72db58720998376ff653bc7" integrity sha512-AoeIEJn8vt+d/6+PXDRPaksYhnlbMIiejioBZvvMQsOjW/JYK6k/0dKnvvP3EhK5GfMBWDPtrxRtegWdAcdq9Q== +"@babel/parser@^7.10.4", "@babel/parser@^7.10.5": + version "7.10.5" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.10.5.tgz#e7c6bf5a7deff957cec9f04b551e2762909d826b" + integrity sha512-wfryxy4bE1UivvQKSQDU4/X6dr+i8bctjUjj8Zyt3DQy7NtPizJXT8M52nqpNKL+nq2PW8lxk4ZqLj0fD4B4hQ== + +"@babel/plugin-proposal-async-generator-functions@^7.10.4": + version "7.10.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.10.5.tgz#3491cabf2f7c179ab820606cec27fed15e0e8558" + integrity sha512-cNMCVezQbrRGvXJwm9fu/1sJj9bHdGAgKodZdLqOQIpfoH3raqmRPBM17+lh7CzhiKRRBrGtZL9WcjxSoGYUSg== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-remap-async-to-generator" "^7.10.4" + "@babel/plugin-syntax-async-generators" "^7.8.0" + "@babel/plugin-proposal-async-generator-functions@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.8.3.tgz#bad329c670b382589721b27540c7d288601c6e6f" @@ -298,6 +561,23 @@ "@babel/helper-create-class-features-plugin" "^7.8.3" "@babel/helper-plugin-utils" "^7.8.3" +"@babel/plugin-proposal-class-properties@^7.10.1", "@babel/plugin-proposal-class-properties@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.10.4.tgz#a33bf632da390a59c7a8c570045d1115cd778807" + integrity sha512-vhwkEROxzcHGNu2mzUC0OFFNXdZ4M23ib8aRRcJSsW8BZK9pQMD7QB7csl97NBbgGZO7ZyHUyKDnxzOaP4IrCg== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-proposal-decorators@^7.10.1": + version "7.10.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.10.5.tgz#42898bba478bc4b1ae242a703a953a7ad350ffb4" + integrity sha512-Sc5TAQSZuLzgY0664mMDn24Vw2P8g/VhyLyGPaWiHahhgLqeZvcGeyBZOrJW0oSKIK2mvQ22a1ENXBIQLhrEiQ== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.10.5" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-decorators" "^7.10.4" + "@babel/plugin-proposal-decorators@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.8.3.tgz#2156860ab65c5abf068c3f67042184041066543e" @@ -307,6 +587,14 @@ "@babel/helper-plugin-utils" "^7.8.3" "@babel/plugin-syntax-decorators" "^7.8.3" +"@babel/plugin-proposal-dynamic-import@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.10.4.tgz#ba57a26cb98b37741e9d5bca1b8b0ddf8291f17e" + integrity sha512-up6oID1LeidOOASNXgv/CFbgBqTuKJ0cJjz6An5tWD+NVBNlp3VNSBxv2ZdU7SYl3NxJC7agAQDApZusV6uFwQ== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-dynamic-import" "^7.8.0" + "@babel/plugin-proposal-dynamic-import@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.8.3.tgz#38c4fe555744826e97e2ae930b0fb4cc07e66054" @@ -315,6 +603,14 @@ "@babel/helper-plugin-utils" "^7.8.3" "@babel/plugin-syntax-dynamic-import" "^7.8.0" +"@babel/plugin-proposal-json-strings@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.10.4.tgz#593e59c63528160233bd321b1aebe0820c2341db" + integrity sha512-fCL7QF0Jo83uy1K0P2YXrfX11tj3lkpN7l4dMv9Y9VkowkhkQDwFHFd8IiwyK5MZjE8UpbgokkgtcReH88Abaw== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-json-strings" "^7.8.0" + "@babel/plugin-proposal-json-strings@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.8.3.tgz#da5216b238a98b58a1e05d6852104b10f9a70d6b" @@ -323,6 +619,14 @@ "@babel/helper-plugin-utils" "^7.8.3" "@babel/plugin-syntax-json-strings" "^7.8.0" +"@babel/plugin-proposal-nullish-coalescing-operator@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.10.4.tgz#02a7e961fc32e6d5b2db0649e01bf80ddee7e04a" + integrity sha512-wq5n1M3ZUlHl9sqT2ok1T2/MTt6AXE0e1Lz4WzWBr95LsAZ5qDXe4KnFuauYyEyLiohvXFMdbsOTMyLZs91Zlw== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.0" + "@babel/plugin-proposal-nullish-coalescing-operator@^7.4.4", "@babel/plugin-proposal-nullish-coalescing-operator@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.8.3.tgz#e4572253fdeed65cddeecfdab3f928afeb2fd5d2" @@ -331,6 +635,14 @@ "@babel/helper-plugin-utils" "^7.8.3" "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.0" +"@babel/plugin-proposal-numeric-separator@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.10.4.tgz#ce1590ff0a65ad12970a609d78855e9a4c1aef06" + integrity sha512-73/G7QoRoeNkLZFxsoCCvlg4ezE4eM+57PnOqgaPOozd5myfj7p0muD1mRVJvbUWbOzD+q3No2bWbaKy+DJ8DA== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" + "@babel/plugin-proposal-numeric-separator@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.8.3.tgz#5d6769409699ec9b3b68684cd8116cedff93bad8" @@ -348,6 +660,23 @@ "@babel/plugin-syntax-object-rest-spread" "^7.8.0" "@babel/plugin-transform-parameters" "^7.9.5" +"@babel/plugin-proposal-object-rest-spread@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.10.4.tgz#50129ac216b9a6a55b3853fdd923e74bf553a4c0" + integrity sha512-6vh4SqRuLLarjgeOf4EaROJAHjvu9Gl+/346PbDH9yWbJyfnJ/ah3jmYKYtswEyCoWZiidvVHjHshd4WgjB9BA== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-object-rest-spread" "^7.8.0" + "@babel/plugin-transform-parameters" "^7.10.4" + +"@babel/plugin-proposal-optional-catch-binding@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.10.4.tgz#31c938309d24a78a49d68fdabffaa863758554dd" + integrity sha512-LflT6nPh+GK2MnFiKDyLiqSqVHkQnVf7hdoAvyTnnKj9xB3docGRsdPuxp6qqqW19ifK3xgc9U5/FwrSaCNX5g== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.0" + "@babel/plugin-proposal-optional-catch-binding@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.8.3.tgz#9dee96ab1650eed88646ae9734ca167ac4a9c5c9" @@ -356,6 +685,14 @@ "@babel/helper-plugin-utils" "^7.8.3" "@babel/plugin-syntax-optional-catch-binding" "^7.8.0" +"@babel/plugin-proposal-optional-chaining@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.10.4.tgz#750f1255e930a1f82d8cdde45031f81a0d0adff7" + integrity sha512-ZIhQIEeavTgouyMSdZRap4VPPHqJJ3NEs2cuHs5p0erH+iz6khB0qfgU8g7UuJkG88+fBMy23ZiU+nuHvekJeQ== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-optional-chaining" "^7.8.0" + "@babel/plugin-proposal-optional-chaining@^7.6.0", "@babel/plugin-proposal-optional-chaining@^7.9.0": version "7.9.0" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.9.0.tgz#31db16b154c39d6b8a645292472b98394c292a58" @@ -364,6 +701,22 @@ "@babel/helper-plugin-utils" "^7.8.3" "@babel/plugin-syntax-optional-chaining" "^7.8.0" +"@babel/plugin-proposal-private-methods@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.10.4.tgz#b160d972b8fdba5c7d111a145fc8c421fc2a6909" + integrity sha512-wh5GJleuI8k3emgTg5KkJK6kHNsGEr0uBTDBuQUBJwckk9xs1ez79ioheEVVxMLyPscB0LfkbVHslQqIzWV6Bw== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-proposal-unicode-property-regex@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.10.4.tgz#4483cda53041ce3413b7fe2f00022665ddfaa75d" + integrity sha512-H+3fOgPnEXFL9zGYtKQe4IDOPKYlZdF1kqFDQRRb8PK4B8af1vAGK04tF5iQAAsui+mHNBQSAtd2/ndEDe9wuA== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-proposal-unicode-property-regex@^7.4.4", "@babel/plugin-proposal-unicode-property-regex@^7.8.3": version "7.8.8" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.8.8.tgz#ee3a95e90cdc04fe8cd92ec3279fa017d68a0d1d" @@ -379,6 +732,20 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" +"@babel/plugin-syntax-class-properties@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.10.4.tgz#6644e6a0baa55a61f9e3231f6c9eeb6ee46c124c" + integrity sha512-GCSBF7iUle6rNugfURwNmCGG3Z/2+opxAMLs1nND4bhEG5PuxTIggDBoeYYSujAlLtsupzOHYJQgPS3pivwXIA== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-decorators@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.10.4.tgz#6853085b2c429f9d322d02f5a635018cdeb2360c" + integrity sha512-2NaoC6fAk2VMdhY1eerkfHV+lVYC1u8b+jmRJISqANCJlTxYy19HGdIkkQtix2UtkcPuPu+IlDgrVseZnU03bw== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-decorators@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.8.3.tgz#8d2c15a9f1af624b0025f961682a9d53d3001bda" @@ -421,6 +788,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" +"@babel/plugin-syntax-numeric-separator@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97" + integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-numeric-separator@^7.8.0", "@babel/plugin-syntax-numeric-separator@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.8.3.tgz#0e3fb63e09bea1b11e96467271c8308007e7c41f" @@ -449,6 +823,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" +"@babel/plugin-syntax-top-level-await@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.10.4.tgz#4bbeb8917b54fcf768364e0a81f560e33a3ef57d" + integrity sha512-ni1brg4lXEmWyafKr0ccFWkJG0CeMt4WV1oyeBW6EFObF4oOHclbkj5cARxAPQyAQ2UTuplJyK4nfkXIMMFvsQ== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-top-level-await@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.8.3.tgz#3acdece695e6b13aaf57fc291d1a800950c71391" @@ -456,6 +837,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.3" +"@babel/plugin-syntax-typescript@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.10.4.tgz#2f55e770d3501e83af217d782cb7517d7bb34d25" + integrity sha512-oSAEz1YkBCAKr5Yiq8/BNtvSAPwkp/IyUnwZogd8p+F0RuYQQrLeRUzIQhueQTTBy/F+a40uS7OFKxnkRvmvFQ== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-typescript@^7.2.0", "@babel/plugin-syntax-typescript@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.8.3.tgz#c1f659dda97711a569cef75275f7e15dcaa6cabc" @@ -463,6 +851,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.3" +"@babel/plugin-transform-arrow-functions@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.10.4.tgz#e22960d77e697c74f41c501d44d73dbf8a6a64cd" + integrity sha512-9J/oD1jV0ZCBcgnoFWFq1vJd4msoKb/TCpGNFyyLt0zABdcvgK3aYikZ8HjzB14c26bc7E3Q1yugpwGy2aTPNA== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-arrow-functions@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.8.3.tgz#82776c2ed0cd9e1a49956daeb896024c9473b8b6" @@ -470,6 +865,15 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.3" +"@babel/plugin-transform-async-to-generator@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.10.4.tgz#41a5017e49eb6f3cda9392a51eef29405b245a37" + integrity sha512-F6nREOan7J5UXTLsDsZG3DXmZSVofr2tGNwfdrVwkDWHfQckbQXnXSPfD7iO+c/2HGqycwyLST3DnZ16n+cBJQ== + dependencies: + "@babel/helper-module-imports" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-remap-async-to-generator" "^7.10.4" + "@babel/plugin-transform-async-to-generator@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.8.3.tgz#4308fad0d9409d71eafb9b1a6ee35f9d64b64086" @@ -479,6 +883,13 @@ "@babel/helper-plugin-utils" "^7.8.3" "@babel/helper-remap-async-to-generator" "^7.8.3" +"@babel/plugin-transform-block-scoped-functions@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.10.4.tgz#1afa595744f75e43a91af73b0d998ecfe4ebc2e8" + integrity sha512-WzXDarQXYYfjaV1szJvN3AD7rZgZzC1JtjJZ8dMHUyiK8mxPRahynp14zzNjU3VkPqPsO38CzxiWO1c9ARZ8JA== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-block-scoped-functions@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.8.3.tgz#437eec5b799b5852072084b3ae5ef66e8349e8a3" @@ -486,6 +897,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.3" +"@babel/plugin-transform-block-scoping@^7.10.4": + version "7.10.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.10.5.tgz#b81b8aafefbfe68f0f65f7ef397b9ece68a6037d" + integrity sha512-6Ycw3hjpQti0qssQcA6AMSFDHeNJ++R6dIMnpRqUjFeBBTmTDPa8zgF90OVfTvAo11mXZTlVUViY1g8ffrURLg== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-block-scoping@^7.7.4", "@babel/plugin-transform-block-scoping@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.8.3.tgz#97d35dab66857a437c166358b91d09050c868f3a" @@ -494,6 +912,20 @@ "@babel/helper-plugin-utils" "^7.8.3" lodash "^4.17.13" +"@babel/plugin-transform-classes@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.10.4.tgz#405136af2b3e218bc4a1926228bc917ab1a0adc7" + integrity sha512-2oZ9qLjt161dn1ZE0Ms66xBncQH4In8Sqw1YWgBUZuGVJJS5c0OFZXL6dP2MRHrkU/eKhWg8CzFJhRQl50rQxA== + dependencies: + "@babel/helper-annotate-as-pure" "^7.10.4" + "@babel/helper-define-map" "^7.10.4" + "@babel/helper-function-name" "^7.10.4" + "@babel/helper-optimise-call-expression" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-replace-supers" "^7.10.4" + "@babel/helper-split-export-declaration" "^7.10.4" + globals "^11.1.0" + "@babel/plugin-transform-classes@^7.9.5": version "7.9.5" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.9.5.tgz#800597ddb8aefc2c293ed27459c1fcc935a26c2c" @@ -508,6 +940,13 @@ "@babel/helper-split-export-declaration" "^7.8.3" globals "^11.1.0" +"@babel/plugin-transform-computed-properties@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.10.4.tgz#9ded83a816e82ded28d52d4b4ecbdd810cdfc0eb" + integrity sha512-JFwVDXcP/hM/TbyzGq3l/XWGut7p46Z3QvqFMXTfk6/09m7xZHJUN9xHfsv7vqqD4YnfI5ueYdSJtXqqBLyjBw== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-computed-properties@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.8.3.tgz#96d0d28b7f7ce4eb5b120bb2e0e943343c86f81b" @@ -515,6 +954,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.3" +"@babel/plugin-transform-destructuring@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.10.4.tgz#70ddd2b3d1bea83d01509e9bb25ddb3a74fc85e5" + integrity sha512-+WmfvyfsyF603iPa6825mq6Qrb7uLjTOsa3XOFzlYcYDHSS4QmpOWOL0NNBY5qMbvrcf3tq0Cw+v4lxswOBpgA== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-destructuring@^7.9.5": version "7.9.5" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.9.5.tgz#72c97cf5f38604aea3abf3b935b0e17b1db76a50" @@ -522,6 +968,14 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.3" +"@babel/plugin-transform-dotall-regex@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.10.4.tgz#469c2062105c1eb6a040eaf4fac4b488078395ee" + integrity sha512-ZEAVvUTCMlMFAbASYSVQoxIbHm2OkG2MseW6bV2JjIygOjdVv8tuxrCTzj1+Rynh7ODb8GivUy7dzEXzEhuPaA== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-dotall-regex@^7.4.4", "@babel/plugin-transform-dotall-regex@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.8.3.tgz#c3c6ec5ee6125c6993c5cbca20dc8621a9ea7a6e" @@ -530,6 +984,13 @@ "@babel/helper-create-regexp-features-plugin" "^7.8.3" "@babel/helper-plugin-utils" "^7.8.3" +"@babel/plugin-transform-duplicate-keys@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.10.4.tgz#697e50c9fee14380fe843d1f306b295617431e47" + integrity sha512-GL0/fJnmgMclHiBTTWXNlYjYsA7rDrtsazHG6mglaGSTh0KsrW04qml+Bbz9FL0LcJIRwBWL5ZqlNHKTkU3xAA== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-duplicate-keys@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.8.3.tgz#8d12df309aa537f272899c565ea1768e286e21f1" @@ -537,6 +998,14 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.3" +"@babel/plugin-transform-exponentiation-operator@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.10.4.tgz#5ae338c57f8cf4001bdb35607ae66b92d665af2e" + integrity sha512-S5HgLVgkBcRdyQAHbKj+7KyuWx8C6t5oETmUuwz1pt3WTWJhsUV0WIIXuVvfXMxl/QQyHKlSCNNtaIamG8fysw== + dependencies: + "@babel/helper-builder-binary-assignment-operator-visitor" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-exponentiation-operator@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.8.3.tgz#581a6d7f56970e06bf51560cd64f5e947b70d7b7" @@ -553,6 +1022,13 @@ "@babel/helper-plugin-utils" "^7.8.3" "@babel/plugin-syntax-flow" "^7.8.3" +"@babel/plugin-transform-for-of@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.10.4.tgz#c08892e8819d3a5db29031b115af511dbbfebae9" + integrity sha512-ItdQfAzu9AlEqmusA/65TqJ79eRcgGmpPPFvBnGILXZH975G0LNjP1yjHvGgfuCxqrPPueXOPe+FsvxmxKiHHQ== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-for-of@^7.9.0": version "7.9.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.9.0.tgz#0f260e27d3e29cd1bb3128da5e76c761aa6c108e" @@ -560,6 +1036,14 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.3" +"@babel/plugin-transform-function-name@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.10.4.tgz#6a467880e0fc9638514ba369111811ddbe2644b7" + integrity sha512-OcDCq2y5+E0dVD5MagT5X+yTRbcvFjDI2ZVAottGH6tzqjx/LKpgkUepu3hp/u4tZBzxxpNGwLsAvGBvQ2mJzg== + dependencies: + "@babel/helper-function-name" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-function-name@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.8.3.tgz#279373cb27322aaad67c2683e776dfc47196ed8b" @@ -568,6 +1052,13 @@ "@babel/helper-function-name" "^7.8.3" "@babel/helper-plugin-utils" "^7.8.3" +"@babel/plugin-transform-literals@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.10.4.tgz#9f42ba0841100a135f22712d0e391c462f571f3c" + integrity sha512-Xd/dFSTEVuUWnyZiMu76/InZxLTYilOSr1UlHV+p115Z/Le2Fi1KXkJUYz0b42DfndostYlPub3m8ZTQlMaiqQ== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-literals@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.8.3.tgz#aef239823d91994ec7b68e55193525d76dbd5dc1" @@ -575,6 +1066,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.3" +"@babel/plugin-transform-member-expression-literals@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.10.4.tgz#b1ec44fcf195afcb8db2c62cd8e551c881baf8b7" + integrity sha512-0bFOvPyAoTBhtcJLr9VcwZqKmSjFml1iVxvPL0ReomGU53CX53HsM4h2SzckNdkQcHox1bpAqzxBI1Y09LlBSw== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-member-expression-literals@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.8.3.tgz#963fed4b620ac7cbf6029c755424029fa3a40410" @@ -582,6 +1080,15 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.3" +"@babel/plugin-transform-modules-amd@^7.10.1", "@babel/plugin-transform-modules-amd@^7.10.4": + version "7.10.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.10.5.tgz#1b9cddaf05d9e88b3aad339cb3e445c4f020a9b1" + integrity sha512-elm5uruNio7CTLFItVC/rIzKLfQ17+fX7EVz5W0TMgIHFo1zY0Ozzx+lgwhL4plzl8OzVn6Qasx5DeEFyoNiRw== + dependencies: + "@babel/helper-module-transforms" "^7.10.5" + "@babel/helper-plugin-utils" "^7.10.4" + babel-plugin-dynamic-import-node "^2.3.3" + "@babel/plugin-transform-modules-amd@^7.7.5", "@babel/plugin-transform-modules-amd@^7.9.0", "@babel/plugin-transform-modules-amd@^7.9.6": version "7.9.6" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.9.6.tgz#8539ec42c153d12ea3836e0e3ac30d5aae7b258e" @@ -591,6 +1098,16 @@ "@babel/helper-plugin-utils" "^7.8.3" babel-plugin-dynamic-import-node "^2.3.3" +"@babel/plugin-transform-modules-commonjs@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.10.4.tgz#66667c3eeda1ebf7896d41f1f16b17105a2fbca0" + integrity sha512-Xj7Uq5o80HDLlW64rVfDBhao6OX89HKUmb+9vWYaLXBZOma4gA6tw4Ni1O5qVDoZWUV0fxMYA0aYzOawz0l+1w== + dependencies: + "@babel/helper-module-transforms" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-simple-access" "^7.10.4" + babel-plugin-dynamic-import-node "^2.3.3" + "@babel/plugin-transform-modules-commonjs@^7.9.6": version "7.9.6" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.9.6.tgz#64b7474a4279ee588cacd1906695ca721687c277" @@ -601,6 +1118,16 @@ "@babel/helper-simple-access" "^7.8.3" babel-plugin-dynamic-import-node "^2.3.3" +"@babel/plugin-transform-modules-systemjs@^7.10.4": + version "7.10.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.10.5.tgz#6270099c854066681bae9e05f87e1b9cadbe8c85" + integrity sha512-f4RLO/OL14/FP1AEbcsWMzpbUz6tssRaeQg11RH1BP/XnPpRoVwgeYViMFacnkaw4k4wjRSjn3ip1Uw9TaXuMw== + dependencies: + "@babel/helper-hoist-variables" "^7.10.4" + "@babel/helper-module-transforms" "^7.10.5" + "@babel/helper-plugin-utils" "^7.10.4" + babel-plugin-dynamic-import-node "^2.3.3" + "@babel/plugin-transform-modules-systemjs@^7.9.6": version "7.9.6" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.9.6.tgz#207f1461c78a231d5337a92140e52422510d81a4" @@ -611,6 +1138,14 @@ "@babel/helper-plugin-utils" "^7.8.3" babel-plugin-dynamic-import-node "^2.3.3" +"@babel/plugin-transform-modules-umd@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.10.4.tgz#9a8481fe81b824654b3a0b65da3df89f3d21839e" + integrity sha512-mohW5q3uAEt8T45YT7Qc5ws6mWgJAaL/8BfWD9Dodo1A3RKWli8wTS+WiQ/knF+tXlPirW/1/MqzzGfCExKECA== + dependencies: + "@babel/helper-module-transforms" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-modules-umd@^7.9.0": version "7.9.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.9.0.tgz#e909acae276fec280f9b821a5f38e1f08b480697" @@ -619,6 +1154,13 @@ "@babel/helper-module-transforms" "^7.9.0" "@babel/helper-plugin-utils" "^7.8.3" +"@babel/plugin-transform-named-capturing-groups-regex@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.10.4.tgz#78b4d978810b6f3bcf03f9e318f2fc0ed41aecb6" + integrity sha512-V6LuOnD31kTkxQPhKiVYzYC/Jgdq53irJC/xBSmqcNcqFGV+PER4l6rU5SH2Vl7bH9mLDHcc0+l9HUOe4RNGKA== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.10.4" + "@babel/plugin-transform-named-capturing-groups-regex@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.8.3.tgz#a2a72bffa202ac0e2d0506afd0939c5ecbc48c6c" @@ -626,6 +1168,13 @@ dependencies: "@babel/helper-create-regexp-features-plugin" "^7.8.3" +"@babel/plugin-transform-new-target@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.10.4.tgz#9097d753cb7b024cb7381a3b2e52e9513a9c6888" + integrity sha512-YXwWUDAH/J6dlfwqlWsztI2Puz1NtUAubXhOPLQ5gjR/qmQ5U96DY4FQO8At33JN4XPBhrjB8I4eMmLROjjLjw== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-new-target@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.8.3.tgz#60cc2ae66d85c95ab540eb34babb6434d4c70c43" @@ -640,6 +1189,14 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.3" +"@babel/plugin-transform-object-super@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.10.4.tgz#d7146c4d139433e7a6526f888c667e314a093894" + integrity sha512-5iTw0JkdRdJvr7sY0vHqTpnruUpTea32JHmq/atIWqsnNussbRzjEDyWep8UNztt1B5IusBYg8Irb0bLbiEBCQ== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-replace-supers" "^7.10.4" + "@babel/plugin-transform-object-super@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.8.3.tgz#ebb6a1e7a86ffa96858bd6ac0102d65944261725" @@ -648,6 +1205,14 @@ "@babel/helper-plugin-utils" "^7.8.3" "@babel/helper-replace-supers" "^7.8.3" +"@babel/plugin-transform-parameters@^7.10.4": + version "7.10.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.10.5.tgz#59d339d58d0b1950435f4043e74e2510005e2c4a" + integrity sha512-xPHwUj5RdFV8l1wuYiu5S9fqWGM2DrYc24TMvUiRrPVm+SM3XeqU9BcokQX/kEUe+p2RBwy+yoiR1w/Blq6ubw== + dependencies: + "@babel/helper-get-function-arity" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-parameters@^7.9.5": version "7.9.5" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.9.5.tgz#173b265746f5e15b2afe527eeda65b73623a0795" @@ -656,6 +1221,13 @@ "@babel/helper-get-function-arity" "^7.8.3" "@babel/helper-plugin-utils" "^7.8.3" +"@babel/plugin-transform-property-literals@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.10.4.tgz#f6fe54b6590352298785b83edd815d214c42e3c0" + integrity sha512-ofsAcKiUxQ8TY4sScgsGeR2vJIsfrzqvFb9GvJ5UdXDzl+MyYCaBj/FGzXuv7qE0aJcjWMILny1epqelnFlz8g== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-property-literals@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.8.3.tgz#33194300d8539c1ed28c62ad5087ba3807b98263" @@ -680,6 +1252,13 @@ "@babel/helper-plugin-utils" "^7.8.3" "@babel/plugin-syntax-jsx" "^7.8.3" +"@babel/plugin-transform-regenerator@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.10.4.tgz#2015e59d839074e76838de2159db421966fd8b63" + integrity sha512-3thAHwtor39A7C04XucbMg17RcZ3Qppfxr22wYzZNcVIkPHfpM9J0SO8zuCV6SZa265kxBJSrfKTvDCYqBFXGw== + dependencies: + regenerator-transform "^0.14.2" + "@babel/plugin-transform-regenerator@^7.8.7": version "7.8.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.8.7.tgz#5e46a0dca2bee1ad8285eb0527e6abc9c37672f8" @@ -687,6 +1266,13 @@ dependencies: regenerator-transform "^0.14.2" +"@babel/plugin-transform-reserved-words@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.10.4.tgz#8f2682bcdcef9ed327e1b0861585d7013f8a54dd" + integrity sha512-hGsw1O6Rew1fkFbDImZIEqA8GoidwTAilwCyWqLBM9f+e/u/sQMQu7uX6dyokfOayRuuVfKOW4O7HvaBWM+JlQ== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-reserved-words@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.8.3.tgz#9a0635ac4e665d29b162837dd3cc50745dfdf1f5" @@ -694,6 +1280,16 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.3" +"@babel/plugin-transform-runtime@^7.10.1": + version "7.10.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.10.5.tgz#3b39b7b24830e0c2d8ff7a4489fe5cf99fbace86" + integrity sha512-tV4V/FjElJ9lQtyjr5xD2IFFbgY46r7EeVu5a8CpEKT5laheHKSlFeHjpkPppW3PqzGLAuv5k2qZX5LgVZIX5w== + dependencies: + "@babel/helper-module-imports" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + resolve "^1.8.1" + semver "^5.5.1" + "@babel/plugin-transform-runtime@^7.9.0": version "7.9.6" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.9.6.tgz#3ba804438ad0d880a17bca5eaa0cdf1edeedb2fd" @@ -704,6 +1300,13 @@ resolve "^1.8.1" semver "^5.5.1" +"@babel/plugin-transform-shorthand-properties@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.10.4.tgz#9fd25ec5cdd555bb7f473e5e6ee1c971eede4dd6" + integrity sha512-AC2K/t7o07KeTIxMoHneyX90v3zkm5cjHJEokrPEAGEy3UCp8sLKfnfOIGdZ194fyN4wfX/zZUWT9trJZ0qc+Q== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-shorthand-properties@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.8.3.tgz#28545216e023a832d4d3a1185ed492bcfeac08c8" @@ -711,6 +1314,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.3" +"@babel/plugin-transform-spread@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.10.4.tgz#4e2c85ea0d6abaee1b24dcfbbae426fe8d674cff" + integrity sha512-1e/51G/Ni+7uH5gktbWv+eCED9pP8ZpRhZB3jOaI3mmzfvJTWHkuyYTv0Z5PYtyM+Tr2Ccr9kUdQxn60fI5WuQ== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-spread@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.8.3.tgz#9c8ffe8170fdfb88b114ecb920b82fb6e95fe5e8" @@ -718,6 +1328,14 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.3" +"@babel/plugin-transform-sticky-regex@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.10.4.tgz#8f3889ee8657581130a29d9cc91d7c73b7c4a28d" + integrity sha512-Ddy3QZfIbEV0VYcVtFDCjeE4xwVTJWTmUtorAJkn6u/92Z/nWJNV+mILyqHKrUxXYKA2EoCilgoPePymKL4DvQ== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-regex" "^7.10.4" + "@babel/plugin-transform-sticky-regex@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.8.3.tgz#be7a1290f81dae767475452199e1f76d6175b100" @@ -726,6 +1344,14 @@ "@babel/helper-plugin-utils" "^7.8.3" "@babel/helper-regex" "^7.8.3" +"@babel/plugin-transform-template-literals@^7.10.4": + version "7.10.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.10.5.tgz#78bc5d626a6642db3312d9d0f001f5e7639fde8c" + integrity sha512-V/lnPGIb+KT12OQikDvgSuesRX14ck5FfJXt6+tXhdkJ+Vsd0lDCVtF6jcB4rNClYFzaB2jusZ+lNISDk2mMMw== + dependencies: + "@babel/helper-annotate-as-pure" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-template-literals@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.8.3.tgz#7bfa4732b455ea6a43130adc0ba767ec0e402a80" @@ -734,6 +1360,13 @@ "@babel/helper-annotate-as-pure" "^7.8.3" "@babel/helper-plugin-utils" "^7.8.3" +"@babel/plugin-transform-typeof-symbol@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.10.4.tgz#9509f1a7eec31c4edbffe137c16cc33ff0bc5bfc" + integrity sha512-QqNgYwuuW0y0H+kUE/GWSR45t/ccRhe14Fs/4ZRouNNQsyd4o3PG4OtHiIrepbM2WKUBDAXKCAK/Lk4VhzTaGA== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-typeof-symbol@^7.8.4": version "7.8.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.8.4.tgz#ede4062315ce0aaf8a657a920858f1a2f35fc412" @@ -741,6 +1374,15 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.3" +"@babel/plugin-transform-typescript@^7.10.1": + version "7.10.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.10.5.tgz#edf353944e979f40d8ff9fe4e9975d0a465037c5" + integrity sha512-YCyYsFrrRMZ3qR7wRwtSSJovPG5vGyG4ZdcSAivGwTfoasMp3VOB/AKhohu3dFtmB4cCDcsndCSxGtrdliCsZQ== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.10.5" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-typescript" "^7.10.4" + "@babel/plugin-transform-typescript@^7.9.0": version "7.9.6" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.9.6.tgz#2248971416a506fc78278fc0c0ea3179224af1e9" @@ -776,6 +1418,21 @@ "@babel/helper-plugin-utils" "^7.8.3" "@babel/plugin-syntax-typescript" "^7.8.3" +"@babel/plugin-transform-unicode-escapes@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.10.4.tgz#feae523391c7651ddac115dae0a9d06857892007" + integrity sha512-y5XJ9waMti2J+e7ij20e+aH+fho7Wb7W8rNuu72aKRwCHFqQdhkdU2lo3uZ9tQuboEJcUFayXdARhcxLQ3+6Fg== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-transform-unicode-regex@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.10.4.tgz#e56d71f9282fac6db09c82742055576d5e6d80a8" + integrity sha512-wNfsc4s8N2qnIwpO/WP2ZiSyjfpTamT2C9V9FDH/Ljub9zw6P3SjkXcFmc0RQUt96k2fmIvtla2MMjgTwIAC+A== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-unicode-regex@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.8.3.tgz#0cef36e3ba73e5c57273effb182f46b91a1ecaad" @@ -784,6 +1441,14 @@ "@babel/helper-create-regexp-features-plugin" "^7.8.3" "@babel/helper-plugin-utils" "^7.8.3" +"@babel/polyfill@^7.10.1": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/polyfill/-/polyfill-7.10.4.tgz#915e5bfe61490ac0199008e35ca9d7d151a8e45a" + integrity sha512-8BYcnVqQ5kMD2HXoHInBH7H1b/uP3KdnwCYXOqFnXqguOyuu443WXusbIUbWEfY3Z0Txk0M1uG/8YuAMhNl6zg== + dependencies: + core-js "^2.6.5" + regenerator-runtime "^0.13.4" + "@babel/polyfill@^7.8.3", "@babel/polyfill@^7.8.7": version "7.8.7" resolved "https://registry.yarnpkg.com/@babel/polyfill/-/polyfill-7.8.7.tgz#151ec24c7135481336168c3bd8b8bf0cf91c032f" @@ -858,6 +1523,76 @@ levenary "^1.1.1" semver "^5.5.0" +"@babel/preset-env@^7.10.2": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.10.4.tgz#fbf57f9a803afd97f4f32e4f798bb62e4b2bef5f" + integrity sha512-tcmuQ6vupfMZPrLrc38d0sF2OjLT3/bZ0dry5HchNCQbrokoQi4reXqclvkkAT5b+gWc23meVWpve5P/7+w/zw== + dependencies: + "@babel/compat-data" "^7.10.4" + "@babel/helper-compilation-targets" "^7.10.4" + "@babel/helper-module-imports" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-proposal-async-generator-functions" "^7.10.4" + "@babel/plugin-proposal-class-properties" "^7.10.4" + "@babel/plugin-proposal-dynamic-import" "^7.10.4" + "@babel/plugin-proposal-json-strings" "^7.10.4" + "@babel/plugin-proposal-nullish-coalescing-operator" "^7.10.4" + "@babel/plugin-proposal-numeric-separator" "^7.10.4" + "@babel/plugin-proposal-object-rest-spread" "^7.10.4" + "@babel/plugin-proposal-optional-catch-binding" "^7.10.4" + "@babel/plugin-proposal-optional-chaining" "^7.10.4" + "@babel/plugin-proposal-private-methods" "^7.10.4" + "@babel/plugin-proposal-unicode-property-regex" "^7.10.4" + "@babel/plugin-syntax-async-generators" "^7.8.0" + "@babel/plugin-syntax-class-properties" "^7.10.4" + "@babel/plugin-syntax-dynamic-import" "^7.8.0" + "@babel/plugin-syntax-json-strings" "^7.8.0" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.0" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" + "@babel/plugin-syntax-object-rest-spread" "^7.8.0" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.0" + "@babel/plugin-syntax-optional-chaining" "^7.8.0" + "@babel/plugin-syntax-top-level-await" "^7.10.4" + "@babel/plugin-transform-arrow-functions" "^7.10.4" + "@babel/plugin-transform-async-to-generator" "^7.10.4" + "@babel/plugin-transform-block-scoped-functions" "^7.10.4" + "@babel/plugin-transform-block-scoping" "^7.10.4" + "@babel/plugin-transform-classes" "^7.10.4" + "@babel/plugin-transform-computed-properties" "^7.10.4" + "@babel/plugin-transform-destructuring" "^7.10.4" + "@babel/plugin-transform-dotall-regex" "^7.10.4" + "@babel/plugin-transform-duplicate-keys" "^7.10.4" + "@babel/plugin-transform-exponentiation-operator" "^7.10.4" + "@babel/plugin-transform-for-of" "^7.10.4" + "@babel/plugin-transform-function-name" "^7.10.4" + "@babel/plugin-transform-literals" "^7.10.4" + "@babel/plugin-transform-member-expression-literals" "^7.10.4" + "@babel/plugin-transform-modules-amd" "^7.10.4" + "@babel/plugin-transform-modules-commonjs" "^7.10.4" + "@babel/plugin-transform-modules-systemjs" "^7.10.4" + "@babel/plugin-transform-modules-umd" "^7.10.4" + "@babel/plugin-transform-named-capturing-groups-regex" "^7.10.4" + "@babel/plugin-transform-new-target" "^7.10.4" + "@babel/plugin-transform-object-super" "^7.10.4" + "@babel/plugin-transform-parameters" "^7.10.4" + "@babel/plugin-transform-property-literals" "^7.10.4" + "@babel/plugin-transform-regenerator" "^7.10.4" + "@babel/plugin-transform-reserved-words" "^7.10.4" + "@babel/plugin-transform-shorthand-properties" "^7.10.4" + "@babel/plugin-transform-spread" "^7.10.4" + "@babel/plugin-transform-sticky-regex" "^7.10.4" + "@babel/plugin-transform-template-literals" "^7.10.4" + "@babel/plugin-transform-typeof-symbol" "^7.10.4" + "@babel/plugin-transform-unicode-escapes" "^7.10.4" + "@babel/plugin-transform-unicode-regex" "^7.10.4" + "@babel/preset-modules" "^0.1.3" + "@babel/types" "^7.10.4" + browserslist "^4.12.0" + core-js-compat "^3.6.2" + invariant "^2.2.2" + levenary "^1.1.1" + semver "^5.5.0" + "@babel/preset-flow@^7.0.0": version "7.9.0" resolved "https://registry.yarnpkg.com/@babel/preset-flow/-/preset-flow-7.9.0.tgz#fee847c3e090b0b2d9227c1949e4da1d1379280d" @@ -903,6 +1638,22 @@ dependencies: regenerator-runtime "^0.13.4" +"@babel/runtime@^7.10.2": + version "7.10.5" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.10.5.tgz#303d8bd440ecd5a491eae6117fd3367698674c5c" + integrity sha512-otddXKhdNn7d0ptoFRHtMLa8LqDxLYwTjB4nYgM1yy5N6gU/MUf8zqyyLltCH3yAVitBzmwK4us+DD0l/MauAg== + dependencies: + regenerator-runtime "^0.13.4" + +"@babel/template@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.10.4.tgz#3251996c4200ebc71d1a8fc405fba940f36ba278" + integrity sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/parser" "^7.10.4" + "@babel/types" "^7.10.4" + "@babel/template@^7.4.0", "@babel/template@^7.8.3", "@babel/template@^7.8.6": version "7.8.6" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.8.6.tgz#86b22af15f828dfb086474f964dcc3e39c43ce2b" @@ -927,6 +1678,21 @@ globals "^11.1.0" lodash "^4.17.13" +"@babel/traverse@^7.10.4", "@babel/traverse@^7.10.5": + version "7.10.5" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.10.5.tgz#77ce464f5b258be265af618d8fddf0536f20b564" + integrity sha512-yc/fyv2gUjPqzTz0WHeRJH2pv7jA9kA7mBX2tXl/x5iOE81uaVPuGPtaYk7wmkx4b67mQ7NqI8rmT2pF47KYKQ== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/generator" "^7.10.5" + "@babel/helper-function-name" "^7.10.4" + "@babel/helper-split-export-declaration" "^7.10.4" + "@babel/parser" "^7.10.5" + "@babel/types" "^7.10.5" + debug "^4.1.0" + globals "^11.1.0" + lodash "^4.17.19" + "@babel/types@^7.1.6", "@babel/types@^7.3.2", "@babel/types@^7.3.4", "@babel/types@^7.4.0", "@babel/types@^7.4.4", "@babel/types@^7.6.0", "@babel/types@^7.7.0", "@babel/types@^7.7.2", "@babel/types@^7.8.3", "@babel/types@^7.8.6", "@babel/types@^7.9.0", "@babel/types@^7.9.5", "@babel/types@^7.9.6": version "7.9.6" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.9.6.tgz#2c5502b427251e9de1bd2dff95add646d95cc9f7" @@ -936,6 +1702,15 @@ lodash "^4.17.13" to-fast-properties "^2.0.0" +"@babel/types@^7.10.4", "@babel/types@^7.10.5": + version "7.10.5" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.10.5.tgz#d88ae7e2fde86bfbfe851d4d81afa70a997b5d15" + integrity sha512-ixV66KWfCI6GKoA/2H9v6bQdbfXEwwpOdQ8cRvb4F+eyvhlaHxWFMQB4+3d9QFJXZsiiiqVrewNV0DFEQpyT4Q== + dependencies: + "@babel/helper-validator-identifier" "^7.10.4" + lodash "^4.17.19" + to-fast-properties "^2.0.0" + "@base2/pretty-print-object@1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@base2/pretty-print-object/-/pretty-print-object-1.0.0.tgz#860ce718b0b73f4009e153541faff2cb6b85d047" @@ -3917,6 +4692,13 @@ babel-plugin-ember-modules-api-polyfill@^2.12.0, babel-plugin-ember-modules-api- dependencies: ember-rfc176-data "^0.3.13" +babel-plugin-ember-modules-api-polyfill@^2.13.4: + version "2.13.4" + resolved "https://registry.yarnpkg.com/babel-plugin-ember-modules-api-polyfill/-/babel-plugin-ember-modules-api-polyfill-2.13.4.tgz#cf62bc9bfd808c48d810d5194f4329e9453bd603" + integrity sha512-uxQPkEQAzCYdwhZk16O9m1R4xtCRNy4oEUTBrccOPfzlIahRZJic/JeP/ZEL0BC6Mfq6r55eOg6gMF/zdFoCvA== + dependencies: + ember-rfc176-data "^0.3.13" + babel-plugin-emotion@^10.0.20, babel-plugin-emotion@^10.0.27: version "10.0.33" resolved "https://registry.yarnpkg.com/babel-plugin-emotion/-/babel-plugin-emotion-10.0.33.tgz#ce1155dcd1783bbb9286051efee53f4e2be63e03" @@ -4842,6 +5624,24 @@ broccoli-babel-transpiler@^7.3.0, broccoli-babel-transpiler@^7.4.0: rsvp "^4.8.4" workerpool "^3.1.1" +broccoli-babel-transpiler@^7.5.0: + version "7.6.0" + resolved "https://registry.yarnpkg.com/broccoli-babel-transpiler/-/broccoli-babel-transpiler-7.6.0.tgz#819e6d228b8aa3b4f3b6dfc4b546c13dd304b462" + integrity sha512-VjqIl4qe3zEkGjsPA2jgw/jakjfLjw2IpQ62d4f1kArZyMW/cQ6o54VD0Cd6cai4J08nb4WPLu4cr4Z62vr5uw== + dependencies: + "@babel/core" "^7.10.2" + "@babel/polyfill" "^7.10.1" + broccoli-funnel "^2.0.2" + broccoli-merge-trees "^3.0.2" + broccoli-persistent-filter "^2.2.1" + clone "^2.1.2" + hash-for-dep "^1.4.7" + heimdalljs "^0.2.1" + heimdalljs-logger "^0.1.9" + json-stable-stringify "^1.0.1" + rsvp "^4.8.4" + workerpool "^3.1.1" + broccoli-builder@^0.18.14: version "0.18.14" resolved "https://registry.yarnpkg.com/broccoli-builder/-/broccoli-builder-0.18.14.tgz#4b79e2f844de11a4e1b816c3f49c6df4776c312d" @@ -5684,6 +6484,13 @@ cachedir@2.2.0: resolved "https://registry.yarnpkg.com/cachedir/-/cachedir-2.2.0.tgz#19afa4305e05d79e417566882e0c8f960f62ff0e" integrity sha512-VvxA0xhNqIIfg0V9AmJkDg91DaJwryutH5rVEZAhcNi4iJFj9f+QxmAjgK1LT9I8OgToX27fypX6/MeCXVbBjQ== +calculate-cache-key-for-tree@^1.1.0: + version "1.2.3" + resolved "https://registry.yarnpkg.com/calculate-cache-key-for-tree/-/calculate-cache-key-for-tree-1.2.3.tgz#5a5e4fcfa2d374a63e47fe967593f179e8282825" + integrity sha512-PPQorvdNw8K8k7UftCeradwOmKDSDJs8wcqYTtJPEt3fHbZyK8QsorybJA+lOmk0dgE61vX6R+5Kd3W9h4EMGg== + dependencies: + json-stable-stringify "^1.0.1" + calculate-cache-key-for-tree@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/calculate-cache-key-for-tree/-/calculate-cache-key-for-tree-2.0.0.tgz#7ac57f149a4188eacb0a45b210689215d3fef8d6" @@ -7522,6 +8329,38 @@ ember-cli-babel-plugin-helpers@^1.0.0, ember-cli-babel-plugin-helpers@^1.1.0: resolved "https://registry.yarnpkg.com/ember-cli-babel-plugin-helpers/-/ember-cli-babel-plugin-helpers-1.1.0.tgz#de3baedd093163b6c2461f95964888c1676325ac" integrity sha512-Zr4my8Xn+CzO0gIuFNXji0eTRml5AxZUTDQz/wsNJ5AJAtyFWCY4QtKdoELNNbiCVGt1lq5yLiwTm4scGKu6xA== +ember-cli-babel@7: + version "7.21.0" + resolved "https://registry.yarnpkg.com/ember-cli-babel/-/ember-cli-babel-7.21.0.tgz#c79e888876aee87dfc3260aee7cb580b74264bbc" + integrity sha512-jHVi9melAibo0DrAG3GAxid+29xEyjBoU53652B4qcu3Xp58feZGTH/JGXovH7TjvbeNn65zgNyoV3bk1onULw== + dependencies: + "@babel/core" "^7.10.2" + "@babel/helper-compilation-targets" "^7.10.2" + "@babel/plugin-proposal-class-properties" "^7.10.1" + "@babel/plugin-proposal-decorators" "^7.10.1" + "@babel/plugin-transform-modules-amd" "^7.10.1" + "@babel/plugin-transform-runtime" "^7.10.1" + "@babel/plugin-transform-typescript" "^7.10.1" + "@babel/polyfill" "^7.10.1" + "@babel/preset-env" "^7.10.2" + "@babel/runtime" "^7.10.2" + amd-name-resolver "^1.2.1" + babel-plugin-debug-macros "^0.3.3" + babel-plugin-ember-data-packages-polyfill "^0.1.2" + babel-plugin-ember-modules-api-polyfill "^2.13.4" + babel-plugin-module-resolver "^3.1.1" + broccoli-babel-transpiler "^7.5.0" + broccoli-debug "^0.6.4" + broccoli-funnel "^2.0.1" + broccoli-source "^1.1.0" + clone "^2.1.2" + ember-cli-babel-plugin-helpers "^1.1.0" + ember-cli-version-checker "^4.1.0" + ensure-posix-path "^1.0.2" + fixturify-project "^1.10.0" + rimraf "^3.0.1" + semver "^5.5.0" + ember-cli-babel@7.19.0: version "7.19.0" resolved "https://registry.yarnpkg.com/ember-cli-babel/-/ember-cli-babel-7.19.0.tgz#e6eddea18a867231fcf90a80689e92b98be9a63b" @@ -8053,7 +8892,7 @@ ember-cli@~3.16.1, ember-cli@~3.16.2: watch-detector "^1.0.0" yam "^1.0.0" -ember-compatibility-helpers@^1.1.1, ember-compatibility-helpers@^1.1.2, ember-compatibility-helpers@^1.2.0: +ember-compatibility-helpers@^1.1.1, ember-compatibility-helpers@^1.1.2, ember-compatibility-helpers@^1.2.0, ember-compatibility-helpers@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/ember-compatibility-helpers/-/ember-compatibility-helpers-1.2.1.tgz#87c92c4303f990ff455c28ca39fb3ee11441aa16" integrity sha512-6wzYvnhg1ihQUT5yGqnLtleq3Nv5KNv79WhrEuNU9SwR4uIxCO+KpyC7r3d5VI0EM7/Nmv9Nd0yTkzmTMdVG1A== @@ -8062,13 +8901,27 @@ ember-compatibility-helpers@^1.1.1, ember-compatibility-helpers@^1.1.2, ember-co ember-cli-version-checker "^2.1.1" semver "^5.4.1" -ember-copy@^1.0.0: +ember-copy@1.0.0, ember-copy@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/ember-copy/-/ember-copy-1.0.0.tgz#426554ba6cf65920f31d24d0a3ca2cb1be16e4aa" integrity sha512-aiZNAvOmdemHdvZNn0b5b/0d9g3JFpcOsrDgfhYEbfd7SzE0b69YiaVK2y3wjqfjuuiA54vOllGN4pjSzECNSw== dependencies: ember-cli-babel "^6.6.0" +ember-data-model-fragments@5.0.0-beta.0: + version "5.0.0-beta.0" + resolved "https://registry.yarnpkg.com/ember-data-model-fragments/-/ember-data-model-fragments-5.0.0-beta.0.tgz#da90799970317ca852f96b2ea1548ca70094a5bb" + integrity sha512-vjApz3ZWSiLyUUU2IRi/ArKVBJe+iW+BvKkiQ6an/e7ln4Jt06P9xXm1Fu3hfqkQOQlfn7QkUnlzm4XxRtlMfg== + dependencies: + broccoli-file-creator "^2.1.1" + broccoli-merge-trees "^3.0.0" + calculate-cache-key-for-tree "^1.1.0" + ember-cli-babel "7" + ember-compatibility-helpers "^1.2.1" + ember-copy "1.0.0" + git-repo-info "^2.0.0" + npm-git-info "^1.0.3" + ember-data@^3.16.0: version "3.18.0" resolved "https://registry.yarnpkg.com/ember-data/-/ember-data-3.18.0.tgz#e7c27e311b62f986e55075dc61ca581e7c74a4d3" @@ -10127,7 +10980,7 @@ git-repo-info@^1.4.1: resolved "https://registry.yarnpkg.com/git-repo-info/-/git-repo-info-1.4.1.tgz#2a072823254aaf62fcf0766007d7b6651bd41943" integrity sha1-KgcoIyVKr2L88HZgB9e2ZRvUGUM= -git-repo-info@^2.1.1: +git-repo-info@^2.0.0, git-repo-info@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/git-repo-info/-/git-repo-info-2.1.1.tgz#220ffed8cbae74ef8a80e3052f2ccb5179aed058" integrity sha512-8aCohiDo4jwjOwma4FmYFd3i97urZulL8XL24nIPxuE+GZnfsAyy/g2Shqx6OjUiFKUXZM+Yy+KHnOmmA3FVcg== @@ -12758,6 +13611,11 @@ lodash@4.17.15, lodash@^4.0.0, lodash@^4.0.1, lodash@^4.15.0, lodash@^4.17.10, l resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== +lodash@^4.17.19: + version "4.17.19" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b" + integrity sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ== + log-symbols@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a" From bfe5926293e24aa1c73353c58915073ee50a2590 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Wed, 22 Jul 2020 12:44:41 -0400 Subject: [PATCH 19/70] refactor: change name to fragment-scope --- addons/api/addon/models/base.js | 4 ++-- .../models/{fragment-parent-scope.js => fragment-scope.js} | 2 +- addons/api/app/models/fragment-parent-scope.js | 1 - addons/api/app/models/fragment-scope.js | 1 + 4 files changed, 4 insertions(+), 4 deletions(-) rename addons/api/addon/models/{fragment-parent-scope.js => fragment-scope.js} (92%) delete mode 100644 addons/api/app/models/fragment-parent-scope.js create mode 100644 addons/api/app/models/fragment-scope.js diff --git a/addons/api/addon/models/base.js b/addons/api/addon/models/base.js index 640e54c81..635d3b831 100644 --- a/addons/api/addon/models/base.js +++ b/addons/api/addon/models/base.js @@ -31,9 +31,9 @@ export default class BaseModel extends Model { /** * Scope of this resource, if any, represented as a JSON fragment. - * @type {FragmentParentScope} + * @type {FragmentScope} */ - @fragment('fragment-parent-scope') scope; + @fragment('fragment-scope') scope; // =attributes diff --git a/addons/api/addon/models/fragment-parent-scope.js b/addons/api/addon/models/fragment-scope.js similarity index 92% rename from addons/api/addon/models/fragment-parent-scope.js rename to addons/api/addon/models/fragment-scope.js index 2f4831a0e..1a402c383 100644 --- a/addons/api/addon/models/fragment-parent-scope.js +++ b/addons/api/addon/models/fragment-scope.js @@ -3,7 +3,7 @@ import { computed } from '@ember/object'; import { attr } from '@ember-data/model'; import { scopeTypes } from './scope'; -export default class FragmentParentScope extends Fragment { +export default class FragmentScope extends Fragment { // =attributes diff --git a/addons/api/app/models/fragment-parent-scope.js b/addons/api/app/models/fragment-parent-scope.js deleted file mode 100644 index fdfccb452..000000000 --- a/addons/api/app/models/fragment-parent-scope.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from 'api/models/fragment-parent-scope'; diff --git a/addons/api/app/models/fragment-scope.js b/addons/api/app/models/fragment-scope.js new file mode 100644 index 000000000..277f701cf --- /dev/null +++ b/addons/api/app/models/fragment-scope.js @@ -0,0 +1 @@ +export { default } from 'api/models/fragment-scope'; From 81011b92e3a78da466ae604835185734634aba43 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Wed, 22 Jul 2020 13:12:05 -0400 Subject: [PATCH 20/70] refactor: scopes prefix includes a /scopes/ part before ID --- addons/api/addon/adapters/application.js | 2 +- .../api/tests/unit/adapters/application-test.js | 16 ++++++++-------- addons/api/tests/unit/models/base-test.js | 14 +++++++------- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/addons/api/addon/adapters/application.js b/addons/api/addon/adapters/application.js index 4d3a86b5e..214b80d3c 100644 --- a/addons/api/addon/adapters/application.js +++ b/addons/api/addon/adapters/application.js @@ -74,7 +74,7 @@ export default class ApplicationAdapter extends RESTAdapter.extend( 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 = `/${scopePath}`; + scopePath = `/scopes/${scopePath}`; } return `${prefix}${scopePath}`; } diff --git a/addons/api/tests/unit/adapters/application-test.js b/addons/api/tests/unit/adapters/application-test.js index b3c59adf3..4ea1b2ec5 100644 --- a/addons/api/tests/unit/adapters/application-test.js +++ b/addons/api/tests/unit/adapters/application-test.js @@ -32,7 +32,7 @@ module('Unit | Adapter | application', function (hooks) { adapterOptions: {scopeID} }); assert.ok(config.api.namespace); - assert.equal(prefix, `v1/${scopeID}`); + assert.equal(prefix, `v1/scopes/${scopeID}`); }); test('it generates correct default URL suffixes', function (assert) { @@ -65,23 +65,23 @@ module('Unit | Adapter | application', function (hooks) { const adapter = this.owner.lookup('adapter:application'); // test URL generation for each request type... const findRecordURL = adapter.buildURL('user', '1', mockSnapshot, 'findRecord'); - assert.equal(findRecordURL, '/v1/o_1/users/1:my-custom-method'); + assert.equal(findRecordURL, '/v1/scopes/o_1/users/1:my-custom-method'); const findAllURL = adapter.buildURL('user', null, mockSnapshot, 'findAll'); - assert.equal(findAllURL, '/v1/o_1/users:my-custom-method'); + assert.equal(findAllURL, '/v1/scopes/o_1/users:my-custom-method'); const findBelongsToURL = adapter.buildURL('user', '2', mockSnapshot, 'findBelongsTo'); - assert.equal(findBelongsToURL, '/v1/o_1/users/2:my-custom-method'); + assert.equal(findBelongsToURL, '/v1/scopes/o_1/users/2:my-custom-method'); const createRecordURL = adapter.buildURL('user', null, mockSnapshot, 'createRecord'); - assert.equal(createRecordURL, '/v1/o_1/users:my-custom-method'); + assert.equal(createRecordURL, '/v1/scopes/o_1/users:my-custom-method'); const updateRecordURL = adapter.buildURL('user', '3', mockSnapshot, 'updateRecord'); - assert.equal(updateRecordURL, '/v1/o_1/users/3:my-custom-method'); + assert.equal(updateRecordURL, '/v1/scopes/o_1/users/3:my-custom-method'); const deleteRecordURL = adapter.buildURL('user', '4', mockSnapshot, 'deleteRecord'); - assert.equal(deleteRecordURL, '/v1/o_1/users/4:my-custom-method'); + assert.equal(deleteRecordURL, '/v1/scopes/o_1/users/4:my-custom-method'); }); test('it can request records through the store from a specified scope', async function (assert) { assert.expect(1); const store = this.owner.lookup('service:store'); - this.server.get('/v1/p_456/groups', () => { + this.server.get('/v1/scopes/p_456/groups', () => { assert.ok(true, 'Scoped resource URL was requested.'); return {items: []}; }); diff --git a/addons/api/tests/unit/models/base-test.js b/addons/api/tests/unit/models/base-test.js index 99d74a81f..40be071fe 100644 --- a/addons/api/tests/unit/models/base-test.js +++ b/addons/api/tests/unit/models/base-test.js @@ -97,17 +97,17 @@ module('Unit | Model | base', function (hooks) { assert.expect(3); const store = this.owner.lookup('service:store'); const model = store.createRecord('user', { scopeID: 'o_123' }); - this.server.post('/v1/o_123/users', () => { + this.server.post('/v1/scopes/o_123/users', () => { assert.ok(true, 'Correctly scoped create record URL was requested.') return {}; }); await model.save(); - this.server.patch('/v1/o_123/users', () => { + this.server.patch('/v1/scopes/o_123/users', () => { assert.ok(true, 'Correctly scoped update record URL was requested.') return {}; }); await model.save(); - this.server.delete('/v1/o_123/users', () => { + this.server.delete('/v1/scopes/o_123/users', () => { assert.ok(true, 'Correctly scoped delete record URL was requested.') return {}; }); @@ -120,17 +120,17 @@ module('Unit | Model | base', function (hooks) { const customScopeID = 'global'; const scope = {scope_id: 'o_123'}; const model = store.createRecord('user', { scope }); - this.server.post('/v1/global/users', () => { + this.server.post('/v1/scopes/global/users', () => { assert.ok(true, 'Correctly scoped create record URL was requested.') return {}; }); await model.save({adapterOptions: {scopeID: customScopeID}}); - this.server.patch('/v1/global/users', () => { + this.server.patch('/v1/scopes/global/users', () => { assert.ok(true, 'Correctly scoped update record URL was requested.') return {}; }); await model.save({adapterOptions: {scopeID: customScopeID}}); - this.server.delete('/v1/global/users', () => { + this.server.delete('/v1/scopes/global/users', () => { assert.ok(true, 'Correctly scoped delete record URL was requested.') return {}; }); @@ -143,7 +143,7 @@ module('Unit | Model | base', function (hooks) { const method = 'my-custom-method'; const scope = {scope_id: 'o_123'}; const model = store.createRecord('user', { scope }); - this.server.post(`/v1/o_123/users:${method}`, () => { + this.server.post(`/v1/scopes/o_123/users:${method}`, () => { assert.ok(true, 'Correctly scoped create record URL was requested.') return {}; }); From df3cd1e72c1d2e47336fb593e7191a85010f0002 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Wed, 22 Jul 2020 13:32:06 -0400 Subject: [PATCH 21/70] refactor: rename parent_scope_id to scope_id --- addons/api/addon/serializers/scope.js | 4 ++-- addons/api/tests/unit/serializers/scope-test.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/addons/api/addon/serializers/scope.js b/addons/api/addon/serializers/scope.js index da578ea0f..5767a8015 100644 --- a/addons/api/addon/serializers/scope.js +++ b/addons/api/addon/serializers/scope.js @@ -13,7 +13,7 @@ export default class ScopeSerializer extends ApplicationSerializer { /** * While most resources do not serialize scope into a request body (scope * is expressed in the URL), scopes themselves _do serialize scope_ as a - * string field `parent_scope_id`. + * string field `scope_id`. * Otherwise delegate to default attribute serializer. * * @override @@ -27,7 +27,7 @@ export default class ScopeSerializer extends ApplicationSerializer { const value = super.serializeAttribute(...arguments); const parentScope = snapshot.attr('scope'); if (key === 'scope' && this._canSerialize(key) && parentScope) { - json.parent_scope_id = parentScope.attr('scope_id'); + json.scope_id = parentScope.attr('scope_id'); delete json[key]; } return value; diff --git a/addons/api/tests/unit/serializers/scope-test.js b/addons/api/tests/unit/serializers/scope-test.js index 1f1823da1..3993238fe 100644 --- a/addons/api/tests/unit/serializers/scope-test.js +++ b/addons/api/tests/unit/serializers/scope-test.js @@ -26,7 +26,7 @@ module('Unit | Serializer | scope', function(hooks) { type: 'org', name: 'User', description: 'Description', - parent_scope_id: 'global' + scope_id: 'global' }); }); }); From 857c26272d474cef108dae1f87fcfe9251fea9cb Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Wed, 22 Jul 2020 17:16:20 -0400 Subject: [PATCH 22/70] chore: add initial scopes routes with scope optional scope loading --- ui/core/app/router.js | 7 +- ui/core/app/routes/scopes.js | 4 + ui/core/app/routes/scopes/scope.js | 26 ++++++ ui/core/app/templates/scopes.hbs | 1 + ui/core/app/templates/scopes/scope.hbs | 1 + ui/core/mirage/config.js | 86 +++++++++---------- ui/core/mirage/factories/scope.js | 25 ++++++ ui/core/mirage/generated/factories/scope.js | 15 ++++ ui/core/mirage/scenarios/default.js | 3 +- ui/core/mirage/serializers/scope.js | 5 ++ ui/core/package.json | 1 + ui/core/tests/unit/routes/scopes-test.js | 11 +++ .../tests/unit/routes/scopes/scope-test.js | 11 +++ 13 files changed, 150 insertions(+), 46 deletions(-) create mode 100644 ui/core/app/routes/scopes.js create mode 100644 ui/core/app/routes/scopes/scope.js create mode 100644 ui/core/app/templates/scopes.hbs create mode 100644 ui/core/app/templates/scopes/scope.hbs create mode 100644 ui/core/mirage/factories/scope.js create mode 100644 ui/core/mirage/generated/factories/scope.js create mode 100644 ui/core/mirage/serializers/scope.js create mode 100644 ui/core/tests/unit/routes/scopes-test.js create mode 100644 ui/core/tests/unit/routes/scopes/scope-test.js diff --git a/ui/core/app/router.js b/ui/core/app/router.js index 61566937f..6dbe00b3b 100644 --- a/ui/core/app/router.js +++ b/ui/core/app/router.js @@ -7,6 +7,11 @@ export default class Router extends EmberRouter { } Router.map(function () { + + this.route('scopes', function() { + this.route('scope', { path: ':scope_id' }); + }); + this.route('orgs', function () { this.route('org', { path: ':org_id' }, function () { this.route('projects', function () { @@ -23,7 +28,7 @@ Router.map(function () { this.route('user', { path: ':user_id' }, function() {}); this.route('new'); }); - + this.route('roles', function() { this.route('role', { path: ':role_id' }, function() {}); this.route('new'); diff --git a/ui/core/app/routes/scopes.js b/ui/core/app/routes/scopes.js new file mode 100644 index 000000000..5fff74b2e --- /dev/null +++ b/ui/core/app/routes/scopes.js @@ -0,0 +1,4 @@ +import Route from '@ember/routing/route'; + +export default class ScopesRoute extends Route { +} diff --git a/ui/core/app/routes/scopes/scope.js b/ui/core/app/routes/scopes/scope.js new file mode 100644 index 000000000..68201166e --- /dev/null +++ b/ui/core/app/routes/scopes/scope.js @@ -0,0 +1,26 @@ +import Route from '@ember/routing/route'; + +export default class ScopesScopeRoute extends Route { + + /** + * Attempts to load the specified scope from the API. This is allowed + * to fail, since in some cases the user may not have permission to read a + * scope directly, but may have permission to read its resources. + * If the scope fails to load, we still proceed using a temporary scope object + * consisting of only the specified ID. + * @param {object} params + * @param {string} params.scope_id + * @return {Promise{ScopeModel}} + */ + async model({ scope_id: id }) { + try { + return await this.store.findRecord('scope', id); + } catch { + const maybeExisting = this.store.peekRecord('scope', id); + // TODO it's unclear if this is reliable... + // we may need to revert to a POJO. + return maybeExisting || this.store.createRecord('scope', { id }); + } + } + +} diff --git a/ui/core/app/templates/scopes.hbs b/ui/core/app/templates/scopes.hbs new file mode 100644 index 000000000..e2147cab0 --- /dev/null +++ b/ui/core/app/templates/scopes.hbs @@ -0,0 +1 @@ +{{outlet}} \ No newline at end of file diff --git a/ui/core/app/templates/scopes/scope.hbs b/ui/core/app/templates/scopes/scope.hbs new file mode 100644 index 000000000..e2147cab0 --- /dev/null +++ b/ui/core/app/templates/scopes/scope.hbs @@ -0,0 +1 @@ +{{outlet}} \ No newline at end of file diff --git a/ui/core/mirage/config.js b/ui/core/mirage/config.js index 781a607d5..421f87a8f 100644 --- a/ui/core/mirage/config.js +++ b/ui/core/mirage/config.js @@ -1,5 +1,5 @@ import config from '../config/environment'; -// import { Response } from 'miragejs'; +import { Response } from 'miragejs'; export default function() { @@ -16,66 +16,66 @@ export default function() { this.passthrough(); // Scope resources - // org - this.get('/orgs'); - this.post('/orgs'); - this.get('/orgs/:id'); - this.patch('/orgs/:id'); - this.del('/orgs/:id'); - - // project - - this.get('/orgs/:org_id/projects'); - this.post('/orgs/:org_id/projects'); - this.get('/orgs/:org_id/projects/:id'); - this.patch('/orgs/:org_id/projects/:id'); - this.del('/orgs/:org_id/projects/:id'); + this.post('/scopes'); + // To simulate a possible real-world case, org scopes are not returned, + // but project scopes are. + // TODO this should be expanded to support other scenarios. + this.get('/scopes/:id', ({ scopes }, { params: { id: scopeID } }) => { + const scope = scopes.find(scopeID); + if (scope.type !== 'project') { + return new Response(404, {}, {}); + } else { + return scope; + } + }); + this.patch('/scopes/:id'); + this.del('/scopes/:id'); // Auth & IAM resources - this.get('/orgs/:org_id/auth-methods'); - this.post('/orgs/:org_id/auth-methods'); - this.get('/orgs/:org_id/auth-methods/:id'); - this.patch('/orgs/:org_id/auth-methods/:id'); - this.del('/orgs/:org_id/auth-methods/:id'); + this.get('/scopes/:scope_id/auth-methods'); + this.post('/scopes/:scope_id/auth-methods'); + this.get('/scopes/:scope_id/auth-methods/:id'); + this.patch('/scopes/:scope_id/auth-methods/:id'); + this.del('/scopes/:scope_id/auth-methods/:id'); // IAM : Users - this.get('/orgs/:org_id/users'); - this.post('/orgs/:org_id/users'); - this.get('/orgs/:org_id/users/:id'); - this.patch('/orgs/:org_id/users/:id'); - this.del('/orgs/:org_id/users/:id'); - + this.get('/scopes/:scope_id/users'); + this.post('/scopes/:scope_id/users'); + this.get('/scopes/:scope_id/users/:id'); + this.patch('/scopes/:scope_id/users/:id'); + this.del('/scopes/:scope_id/users/:id'); + // IAM: Roles - this.get('/orgs/:org_id/roles'); - this.post('/orgs/:org_id/roles'); - this.get('/orgs/:org_id/roles/:id'); - this.patch('/orgs/:org_id/roles/:id'); - this.del('/orgs/:org_id/roles/:id'); + this.get('/scopes/:scope_id/roles'); + this.post('/scopes/:scope_id/roles'); + this.get('/scopes/:scope_id/roles/:id'); + this.patch('/scopes/:scope_id/roles/:id'); + this.del('/scopes/:scope_id/roles/:id'); // group - this.get('/orgs/:org_id/groups'); - this.post('/orgs/:org_id/groups'); - this.get('/orgs/:org_id/groups/:id'); - this.patch('/orgs/:org_id/groups/:id'); - this.del('/orgs/:org_id/groups/:id'); + this.get('/scopes/:scope_id/groups'); + this.post('/scopes/:scope_id/groups'); + this.get('/scopes/:scope_id/groups/:id'); + this.patch('/scopes/:scope_id/groups/:id'); + this.del('/scopes/:scope_id/groups/:id'); // Other resources // host-catalog - this.get('/orgs/:org_id/projects/:project_id/host-catalogs'); - this.post('/orgs/:org_id/projects/:project_id/host-catalogs'); - this.get('/orgs/:org_id/projects/:project_id/host-catalogs/:id'); - this.patch('/orgs/:org_id/projects/:project_id/host-catalogs/:id'); - this.del('/orgs/:org_id/projects/:project_id/host-catalogs/:id'); + this.get('/scopes/:scope_id/host-catalogs'); + this.post('/scopes/:scope_id/host-catalogs'); + this.get('/scopes/:scope_id/host-catalogs/:id'); + this.patch('/scopes/:scope_id/host-catalogs/:id'); + this.del('/scopes/:scope_id/host-catalogs/:id'); // Uncomment the following line and the Response import above // Then change the response code to simulate error responses. - // this.get('/orgs/:org_id/projects', () => new Response(505)); + // this.get('/scopes/:scope_id/projects', () => new Response(505)); // Update error payload to simulate specific error responses. - // this.get('/orgs/:org_id/projects', () => new Response(505, {}, { + // this.get('/scopes/:scope_id/projects', () => new Response(505, {}, { // errors: [{ // status: 505, // message: 'HTTP version not supported.', diff --git a/ui/core/mirage/factories/scope.js b/ui/core/mirage/factories/scope.js new file mode 100644 index 000000000..703e80ad4 --- /dev/null +++ b/ui/core/mirage/factories/scope.js @@ -0,0 +1,25 @@ +import factory from '../generated/factories/scope'; +import { trait } from 'ember-cli-mirage'; + +export default factory.extend({ + + type: 'global', + + withChildren: trait({ + afterCreate(record, server) { + if (record.type === 'global') { + server.createList('scope', 1, { + type: 'org', + scope: { id: record.id, type: record.type } + }, 'withChildren'); + } + if (record.type === 'org') { + server.createList('scope', 5, { + type: 'project', + scope: { id: record.id, type: record.type } + }); + } + } + }) + +}); diff --git a/ui/core/mirage/generated/factories/scope.js b/ui/core/mirage/generated/factories/scope.js new file mode 100644 index 000000000..cc574d3cd --- /dev/null +++ b/ui/core/mirage/generated/factories/scope.js @@ -0,0 +1,15 @@ +import { Factory } from 'ember-cli-mirage'; +import { random, date } from 'faker'; + +/** + * GeneratedScopeModelFactory + */ +export default Factory.extend({ + + name: () => random.words(), + description: () => random.words(), + created_time: () => date.recent(), + updated_time: () => date.recent(), + disabled: () => random.boolean(), + +}); diff --git a/ui/core/mirage/scenarios/default.js b/ui/core/mirage/scenarios/default.js index d4e4dc2b7..f143f6ec6 100644 --- a/ui/core/mirage/scenarios/default.js +++ b/ui/core/mirage/scenarios/default.js @@ -2,8 +2,7 @@ export default function(server) { // Scope resources - server.createList('org', 3); - server.createList('project', 6); + server.create('scope', null, 'withChildren'); // Auth diff --git a/ui/core/mirage/serializers/scope.js b/ui/core/mirage/serializers/scope.js new file mode 100644 index 000000000..bfb33f398 --- /dev/null +++ b/ui/core/mirage/serializers/scope.js @@ -0,0 +1,5 @@ +import ApplicationSerializer from './application'; + +export default ApplicationSerializer.extend({ + modelName: 'scope' +}); diff --git a/ui/core/package.json b/ui/core/package.json index 02bcc7ea3..f6ae93043 100644 --- a/ui/core/package.json +++ b/ui/core/package.json @@ -54,6 +54,7 @@ "ember-cli-template-lint": "^1.0.0-beta.3", "ember-cli-uglify": "^3.0.0", "ember-data": "~3.16.0", + "ember-data-model-fragments": "5.0.0-beta.0", "ember-exam": "^5.0.1", "ember-export-application-global": "^2.0.1", "ember-fetch": "^7.0.0", diff --git a/ui/core/tests/unit/routes/scopes-test.js b/ui/core/tests/unit/routes/scopes-test.js new file mode 100644 index 000000000..06a5af092 --- /dev/null +++ b/ui/core/tests/unit/routes/scopes-test.js @@ -0,0 +1,11 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Route | scopes', function(hooks) { + setupTest(hooks); + + test('it exists', function(assert) { + let route = this.owner.lookup('route:scopes'); + assert.ok(route); + }); +}); diff --git a/ui/core/tests/unit/routes/scopes/scope-test.js b/ui/core/tests/unit/routes/scopes/scope-test.js new file mode 100644 index 000000000..62c97d9f0 --- /dev/null +++ b/ui/core/tests/unit/routes/scopes/scope-test.js @@ -0,0 +1,11 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Route | scopes/scope', function(hooks) { + setupTest(hooks); + + test('it exists', function(assert) { + let route = this.owner.lookup('route:scopes/scope'); + assert.ok(route); + }); +}); From c743cc17168a0d86fc01370c431c81e6d5de2c15 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Thu, 23 Jul 2020 09:37:06 -0400 Subject: [PATCH 23/70] chore: add projects routes --- .../app/controllers/scopes/scope/projects.js | 19 +++++ .../controllers/scopes/scope/projects/new.js | 19 +++++ .../scopes/scope/projects/project.js | 14 ++++ ui/core/app/router.js | 7 +- ui/core/app/routes/scopes/scope.js | 12 +-- ui/core/app/routes/scopes/scope/projects.js | 84 +++++++++++++++++++ .../app/routes/scopes/scope/projects/index.js | 4 + .../app/routes/scopes/scope/projects/new.js | 19 +++++ .../routes/scopes/scope/projects/project.js | 17 ++++ .../scopes/scope/projects/project/index.js | 4 + ui/core/app/templates/application.hbs | 3 + ui/core/app/templates/scopes/scope.hbs | 38 ++++++++- .../app/templates/scopes/scope/projects.hbs | 3 + .../templates/scopes/scope/projects/index.hbs | 56 +++++++++++++ .../templates/scopes/scope/projects/new.hbs | 21 +++++ .../scopes/scope/projects/project.hbs | 3 + .../scopes/scope/projects/project/index.hbs | 35 ++++++++ ui/core/mirage/config.js | 7 ++ .../controllers/scopes/scope/projects-test.js | 12 +++ .../scopes/scope/projects/new-test.js | 12 +++ .../scopes/scope/projects/project-test.js | 12 +++ .../unit/routes/scopes/scope/projects-test.js | 11 +++ .../scopes/scope/projects/index-test.js | 11 +++ .../routes/scopes/scope/projects/new-test.js | 11 +++ .../scopes/scope/projects/project-test.js | 11 +++ .../scope/projects/project/index-test.js | 11 +++ ui/core/translations/en-us.yaml | 3 +- 27 files changed, 451 insertions(+), 8 deletions(-) create mode 100644 ui/core/app/controllers/scopes/scope/projects.js create mode 100644 ui/core/app/controllers/scopes/scope/projects/new.js create mode 100644 ui/core/app/controllers/scopes/scope/projects/project.js create mode 100644 ui/core/app/routes/scopes/scope/projects.js create mode 100644 ui/core/app/routes/scopes/scope/projects/index.js create mode 100644 ui/core/app/routes/scopes/scope/projects/new.js create mode 100644 ui/core/app/routes/scopes/scope/projects/project.js create mode 100644 ui/core/app/routes/scopes/scope/projects/project/index.js create mode 100644 ui/core/app/templates/scopes/scope/projects.hbs create mode 100644 ui/core/app/templates/scopes/scope/projects/index.hbs create mode 100644 ui/core/app/templates/scopes/scope/projects/new.hbs create mode 100644 ui/core/app/templates/scopes/scope/projects/project.hbs create mode 100644 ui/core/app/templates/scopes/scope/projects/project/index.hbs create mode 100644 ui/core/tests/unit/controllers/scopes/scope/projects-test.js create mode 100644 ui/core/tests/unit/controllers/scopes/scope/projects/new-test.js create mode 100644 ui/core/tests/unit/controllers/scopes/scope/projects/project-test.js create mode 100644 ui/core/tests/unit/routes/scopes/scope/projects-test.js create mode 100644 ui/core/tests/unit/routes/scopes/scope/projects/index-test.js create mode 100644 ui/core/tests/unit/routes/scopes/scope/projects/new-test.js create mode 100644 ui/core/tests/unit/routes/scopes/scope/projects/project-test.js create mode 100644 ui/core/tests/unit/routes/scopes/scope/projects/project/index-test.js diff --git a/ui/core/app/controllers/scopes/scope/projects.js b/ui/core/app/controllers/scopes/scope/projects.js new file mode 100644 index 000000000..cdd9bda8a --- /dev/null +++ b/ui/core/app/controllers/scopes/scope/projects.js @@ -0,0 +1,19 @@ +import Controller from '@ember/controller'; +import { inject as service } from '@ember/service'; + +export default class ScopesScopeProjectsController extends Controller { + + // =services + + @service intl; + + // =attributes + + /** + * @type {string} + */ + get breadCrumb() { + return this.intl.t('resources.projects'); + } + +} diff --git a/ui/core/app/controllers/scopes/scope/projects/new.js b/ui/core/app/controllers/scopes/scope/projects/new.js new file mode 100644 index 000000000..50b30d187 --- /dev/null +++ b/ui/core/app/controllers/scopes/scope/projects/new.js @@ -0,0 +1,19 @@ +import Controller from '@ember/controller'; +import { inject as service } from '@ember/service'; + +export default class ScopesScopeProjectsNewController extends Controller { + + // =services + + @service intl; + + // =attributes + + /** + * @type {string} + */ + get breadCrumb() { + return this.intl.t('titles.new'); + } + +} diff --git a/ui/core/app/controllers/scopes/scope/projects/project.js b/ui/core/app/controllers/scopes/scope/projects/project.js new file mode 100644 index 000000000..8fc8bbb1c --- /dev/null +++ b/ui/core/app/controllers/scopes/scope/projects/project.js @@ -0,0 +1,14 @@ +import Controller from '@ember/controller'; + +export default class ScopesScopeProjectsProjectController extends Controller { + + // =attributes + + /** + * @type {string} + */ + get breadCrumb() { + return this.model.displayName; + } + +} diff --git a/ui/core/app/router.js b/ui/core/app/router.js index 6dbe00b3b..e07a9ee94 100644 --- a/ui/core/app/router.js +++ b/ui/core/app/router.js @@ -9,7 +9,12 @@ export default class Router extends EmberRouter { Router.map(function () { this.route('scopes', function() { - this.route('scope', { path: ':scope_id' }); + this.route('scope', { path: ':scope_id' }, function() { + this.route('projects', function() { + this.route('project', { path: ':project_id' }, function() {}); + this.route('new'); + }); + }); }); this.route('orgs', function () { diff --git a/ui/core/app/routes/scopes/scope.js b/ui/core/app/routes/scopes/scope.js index 68201166e..8cde487bd 100644 --- a/ui/core/app/routes/scopes/scope.js +++ b/ui/core/app/routes/scopes/scope.js @@ -2,10 +2,12 @@ import Route from '@ember/routing/route'; export default class ScopesScopeRoute extends Route { + // =methods + /** - * Attempts to load the specified scope from the API. This is allowed + * Attempt to load the specified scope from the API. This is allowed * to fail, since in some cases the user may not have permission to read a - * scope directly, but may have permission to read its resources. + * scope directly, but may have permission to read resources under it. * If the scope fails to load, we still proceed using a temporary scope object * consisting of only the specified ID. * @param {object} params @@ -16,10 +18,10 @@ export default class ScopesScopeRoute extends Route { try { return await this.store.findRecord('scope', id); } catch { - const maybeExisting = this.store.peekRecord('scope', id); - // TODO it's unclear if this is reliable... + const maybeExistingScope = this.store.peekRecord('scope', id); + // TODO it's unclear if creating a temporary unsaved scope is reliable... // we may need to revert to a POJO. - return maybeExisting || this.store.createRecord('scope', { id }); + return maybeExistingScope || this.store.createRecord('scope', { id }); } } diff --git a/ui/core/app/routes/scopes/scope/projects.js b/ui/core/app/routes/scopes/scope/projects.js new file mode 100644 index 000000000..f2a5fa77b --- /dev/null +++ b/ui/core/app/routes/scopes/scope/projects.js @@ -0,0 +1,84 @@ +import Route from '@ember/routing/route'; +import { inject as service } from '@ember/service'; +import { action } from '@ember/object'; + +export default class ScopesScopeProjectsRoute extends Route { + + // =services + + @service notify; + + // =methods + + /** + * Loads all scopes under the current scope. + * @return {Promise{[ScopeModel]}} + */ + async model() { + const { id: scope_id } = this.modelFor('scopes.scope'); + return this.store.query('scope', { scope_id }); + } + + /** + * If arriving here within a verifiable project scope, redirect to its + * parent, which is necessarily an org, in order to list projects. + * + * TODO: this conditional redirect based on the scope type is probably + * necessary for many routes directly under scope, so we may want + * to move this into a mixin. + * @param {ScopeModel} + */ + redirect() { + const { isProject, scopeID } = this.modelFor('scopes.scope'); + if (isProject) this.transitionTo('scopes.scope.projects', scopeID); + } + + // =actions + + /** + * Rollback changes on project. + * @param {ProjectModel} project + */ + @action + cancel(project) { + const { isNew } = project; + project.rollbackAttributes(); + if (isNew) this.transitionTo('scopes.scope.projects'); + } + + /** + * Handle save project. + * @param {ProjectModel} project + * @param {Event} e + */ + @action + async save(project) { + try { + await project.save(); + // TODO: replace with translated strings + this.notify.success('Save succeeded.'); + this.transitionTo('scopes.scope.projects.project', project); + } catch (error) { + // TODO: replace with translated strings + this.notify.error(error.message, { closeAfter: null }); + } + } + + /** + * Deletes the project and redirects to index. + * @param {ProjectModel} project + */ + @action + async delete(project) { + try { + await project.destroyRecord(); + this.transitionTo('orgs.org.projects'); + // TODO: replace with translated strings + this.notify.success('Deleted project succesfully.'); + } catch (error) { + // TODO: replace with translated strings + this.notify.error(error.message, { closeAfter: null }); + } + } + +} diff --git a/ui/core/app/routes/scopes/scope/projects/index.js b/ui/core/app/routes/scopes/scope/projects/index.js new file mode 100644 index 000000000..294371052 --- /dev/null +++ b/ui/core/app/routes/scopes/scope/projects/index.js @@ -0,0 +1,4 @@ +import Route from '@ember/routing/route'; + +export default class ScopesScopeProjectsIndexRoute extends Route { +} diff --git a/ui/core/app/routes/scopes/scope/projects/new.js b/ui/core/app/routes/scopes/scope/projects/new.js new file mode 100644 index 000000000..1781f5269 --- /dev/null +++ b/ui/core/app/routes/scopes/scope/projects/new.js @@ -0,0 +1,19 @@ +import Route from '@ember/routing/route'; + +export default class ScopesScopeProjectsNewRoute extends Route { + + // =methods + + /** + * Creates a new unsaved project scope belonging to the current org scope. + * @return {ScopeModel} + */ + model() { + const { id } = this.modelFor('scopes.scope'); + return this.store.createRecord('scope', { + type: 'project', + scopeID: id + }); + } + +} diff --git a/ui/core/app/routes/scopes/scope/projects/project.js b/ui/core/app/routes/scopes/scope/projects/project.js new file mode 100644 index 000000000..c2aee8ed3 --- /dev/null +++ b/ui/core/app/routes/scopes/scope/projects/project.js @@ -0,0 +1,17 @@ +import Route from '@ember/routing/route'; + +export default class ScopesScopeProjectsProjectRoute extends Route { + + // =methods + + /** + * Loads a scope by ID (ostensibly a project scope). + * @param {object} params + * @param {string} params.project_id + * @return {ScopeModel} + */ + model({ project_id }) { + return this.store.findRecord('scope', project_id); + } + +} diff --git a/ui/core/app/routes/scopes/scope/projects/project/index.js b/ui/core/app/routes/scopes/scope/projects/project/index.js new file mode 100644 index 000000000..f5511f2e7 --- /dev/null +++ b/ui/core/app/routes/scopes/scope/projects/project/index.js @@ -0,0 +1,4 @@ +import Route from '@ember/routing/route'; + +export default class ScopesScopeProjectsProjectIndexRoute extends Route { +} diff --git a/ui/core/app/templates/application.hbs b/ui/core/app/templates/application.hbs index 576e7875a..fbbecf1e7 100644 --- a/ui/core/app/templates/application.hbs +++ b/ui/core/app/templates/application.hbs @@ -1,3 +1,6 @@ + +{{title (app-name)}} + diff --git a/ui/core/app/templates/scopes/scope.hbs b/ui/core/app/templates/scopes/scope.hbs index e2147cab0..2d1a73607 100644 --- a/ui/core/app/templates/scopes/scope.hbs +++ b/ui/core/app/templates/scopes/scope.hbs @@ -1 +1,37 @@ -{{outlet}} \ No newline at end of file + + + + + + + {{t "resources.projects"}} + + + + {{outlet "sidebar"}} + + {{!-- + + + {{t "resources.users"}} + + + + {{t "resources.groups"}} + + + + {{t "resources.roles"}} + + + --}} + + + + +
+ {{outlet}} +
+
+ +
diff --git a/ui/core/app/templates/scopes/scope/projects.hbs b/ui/core/app/templates/scopes/scope/projects.hbs new file mode 100644 index 000000000..1f709c7f7 --- /dev/null +++ b/ui/core/app/templates/scopes/scope/projects.hbs @@ -0,0 +1,3 @@ +{{title (t "resources.projects")}} + +{{outlet}} diff --git a/ui/core/app/templates/scopes/scope/projects/index.hbs b/ui/core/app/templates/scopes/scope/projects/index.hbs new file mode 100644 index 000000000..c07e265ec --- /dev/null +++ b/ui/core/app/templates/scopes/scope/projects/index.hbs @@ -0,0 +1,56 @@ + + + + + + + +

{{t "resources.projects"}}

+
+ + + {{#if @model}} + + {{t 'titles.new'}} + + {{/if}} + + + + + {{#if @model}} + + + {{#each @model as |project|}} + + {{/each}} + + + {{else}} + + + + + {{t 'descriptions.projects-welcome'}} + + + + {{t 'titles.new'}} + + + + + {{/if}} + + + +
diff --git a/ui/core/app/templates/scopes/scope/projects/new.hbs b/ui/core/app/templates/scopes/scope/projects/new.hbs new file mode 100644 index 000000000..a8758e3c0 --- /dev/null +++ b/ui/core/app/templates/scopes/scope/projects/new.hbs @@ -0,0 +1,21 @@ +{{title (t 'titles.new')}} + + + + + + + + +

{{t "titles.new"}}

+
+ + + + + +
diff --git a/ui/core/app/templates/scopes/scope/projects/project.hbs b/ui/core/app/templates/scopes/scope/projects/project.hbs new file mode 100644 index 000000000..d66e83390 --- /dev/null +++ b/ui/core/app/templates/scopes/scope/projects/project.hbs @@ -0,0 +1,3 @@ +{{title @model.displayName}} + +{{outlet}} diff --git a/ui/core/app/templates/scopes/scope/projects/project/index.hbs b/ui/core/app/templates/scopes/scope/projects/project/index.hbs new file mode 100644 index 000000000..5dccae834 --- /dev/null +++ b/ui/core/app/templates/scopes/scope/projects/project/index.hbs @@ -0,0 +1,35 @@ + + + + + + + +

{{t "resources.project"}}

+
+ + + + + {{t 'titles.details'}} + + + + + + + + + {{t 'actions.delete'}} + + + +
diff --git a/ui/core/mirage/config.js b/ui/core/mirage/config.js index 421f87a8f..d27476462 100644 --- a/ui/core/mirage/config.js +++ b/ui/core/mirage/config.js @@ -17,6 +17,13 @@ export default function() { // Scope resources + this.get('/scopes', ({ scopes }, { queryParams: { scope_id } }) => { + // Default parent scope is global + if (!scope_id) scope_id = 'global'; + return scopes.where(scope => { + return scope.scope ? scope.scope.id === scope_id : false + }); + }); this.post('/scopes'); // To simulate a possible real-world case, org scopes are not returned, // but project scopes are. diff --git a/ui/core/tests/unit/controllers/scopes/scope/projects-test.js b/ui/core/tests/unit/controllers/scopes/scope/projects-test.js new file mode 100644 index 000000000..21125f233 --- /dev/null +++ b/ui/core/tests/unit/controllers/scopes/scope/projects-test.js @@ -0,0 +1,12 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Controller | scopes/scope/projects', function(hooks) { + setupTest(hooks); + + // Replace this with your real tests. + test('it exists', function(assert) { + let controller = this.owner.lookup('controller:scopes/scope/projects'); + assert.ok(controller); + }); +}); diff --git a/ui/core/tests/unit/controllers/scopes/scope/projects/new-test.js b/ui/core/tests/unit/controllers/scopes/scope/projects/new-test.js new file mode 100644 index 000000000..eeb857274 --- /dev/null +++ b/ui/core/tests/unit/controllers/scopes/scope/projects/new-test.js @@ -0,0 +1,12 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Controller | scopes/scope/projects/new', function(hooks) { + setupTest(hooks); + + // Replace this with your real tests. + test('it exists', function(assert) { + let controller = this.owner.lookup('controller:scopes/scope/projects/new'); + assert.ok(controller); + }); +}); diff --git a/ui/core/tests/unit/controllers/scopes/scope/projects/project-test.js b/ui/core/tests/unit/controllers/scopes/scope/projects/project-test.js new file mode 100644 index 000000000..26a255430 --- /dev/null +++ b/ui/core/tests/unit/controllers/scopes/scope/projects/project-test.js @@ -0,0 +1,12 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Controller | scopes/scope/projects/project', function(hooks) { + setupTest(hooks); + + // Replace this with your real tests. + test('it exists', function(assert) { + let controller = this.owner.lookup('controller:scopes/scope/projects/project'); + assert.ok(controller); + }); +}); diff --git a/ui/core/tests/unit/routes/scopes/scope/projects-test.js b/ui/core/tests/unit/routes/scopes/scope/projects-test.js new file mode 100644 index 000000000..4a8dde55f --- /dev/null +++ b/ui/core/tests/unit/routes/scopes/scope/projects-test.js @@ -0,0 +1,11 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Route | scopes/scope/projects', function(hooks) { + setupTest(hooks); + + test('it exists', function(assert) { + let route = this.owner.lookup('route:scopes/scope/projects'); + assert.ok(route); + }); +}); diff --git a/ui/core/tests/unit/routes/scopes/scope/projects/index-test.js b/ui/core/tests/unit/routes/scopes/scope/projects/index-test.js new file mode 100644 index 000000000..71c813a35 --- /dev/null +++ b/ui/core/tests/unit/routes/scopes/scope/projects/index-test.js @@ -0,0 +1,11 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Route | scopes/scope/projects/index', function(hooks) { + setupTest(hooks); + + test('it exists', function(assert) { + let route = this.owner.lookup('route:scopes/scope/projects/index'); + assert.ok(route); + }); +}); diff --git a/ui/core/tests/unit/routes/scopes/scope/projects/new-test.js b/ui/core/tests/unit/routes/scopes/scope/projects/new-test.js new file mode 100644 index 000000000..6d22e1fa7 --- /dev/null +++ b/ui/core/tests/unit/routes/scopes/scope/projects/new-test.js @@ -0,0 +1,11 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Route | scopes/scope/projects/new', function(hooks) { + setupTest(hooks); + + test('it exists', function(assert) { + let route = this.owner.lookup('route:scopes/scope/projects/new'); + assert.ok(route); + }); +}); diff --git a/ui/core/tests/unit/routes/scopes/scope/projects/project-test.js b/ui/core/tests/unit/routes/scopes/scope/projects/project-test.js new file mode 100644 index 000000000..54555a07c --- /dev/null +++ b/ui/core/tests/unit/routes/scopes/scope/projects/project-test.js @@ -0,0 +1,11 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Route | scopes/scope/projects/project', function(hooks) { + setupTest(hooks); + + test('it exists', function(assert) { + let route = this.owner.lookup('route:scopes/scope/projects/project'); + assert.ok(route); + }); +}); diff --git a/ui/core/tests/unit/routes/scopes/scope/projects/project/index-test.js b/ui/core/tests/unit/routes/scopes/scope/projects/project/index-test.js new file mode 100644 index 000000000..c81b2f5a7 --- /dev/null +++ b/ui/core/tests/unit/routes/scopes/scope/projects/project/index-test.js @@ -0,0 +1,11 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Route | scopes/scope/projects/project/index', function(hooks) { + setupTest(hooks); + + test('it exists', function(assert) { + let route = this.owner.lookup('route:scopes/scope/projects/project/index'); + assert.ok(route); + }); +}); diff --git a/ui/core/translations/en-us.yaml b/ui/core/translations/en-us.yaml index bda14642a..e770de25c 100644 --- a/ui/core/translations/en-us.yaml +++ b/ui/core/translations/en-us.yaml @@ -6,7 +6,7 @@ actions: help: Need help? resources: user: User - users: Users + users: Users role: Role roles: Roles group: Group @@ -18,6 +18,7 @@ resources: titles: iam: IAM new: New + details: Details general: General overview: Overview settings: Settings From b7996f6271774fa2a143b917ab87e2d33e26fe9e Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Thu, 23 Jul 2020 10:34:31 -0400 Subject: [PATCH 24/70] fix: normalize embedded scope IDs and ensure scope resources do not receive scope-prefixed URLs like other resources do --- addons/api/addon/adapters/application.js | 14 +++++++++---- addons/api/addon/serializers/application.js | 20 ++++++++++++++++++- .../tests/unit/adapters/application-test.js | 10 ++++++++++ .../unit/serializers/application-test.js | 15 +++++++------- 4 files changed, 47 insertions(+), 12 deletions(-) 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: {}, }, }); From b4e8b1be8b90573925285ae4df98aaa05b31d1ba Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Thu, 23 Jul 2020 11:31:46 -0400 Subject: [PATCH 25/70] chore: add projects CRUDL functionality --- ui/core/app/routes/scopes/scope/projects.js | 4 +++- ui/core/mirage/config.js | 11 ++++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/ui/core/app/routes/scopes/scope/projects.js b/ui/core/app/routes/scopes/scope/projects.js index f2a5fa77b..ebc5e97e5 100644 --- a/ui/core/app/routes/scopes/scope/projects.js +++ b/ui/core/app/routes/scopes/scope/projects.js @@ -55,6 +55,7 @@ export default class ScopesScopeProjectsRoute extends Route { async save(project) { try { await project.save(); + this.refresh(); // TODO: replace with translated strings this.notify.success('Save succeeded.'); this.transitionTo('scopes.scope.projects.project', project); @@ -72,7 +73,8 @@ export default class ScopesScopeProjectsRoute extends Route { async delete(project) { try { await project.destroyRecord(); - this.transitionTo('orgs.org.projects'); + this.refresh(); + this.transitionTo('scopes.scope.projects'); // TODO: replace with translated strings this.notify.success('Deleted project succesfully.'); } catch (error) { diff --git a/ui/core/mirage/config.js b/ui/core/mirage/config.js index d27476462..6e554c34a 100644 --- a/ui/core/mirage/config.js +++ b/ui/core/mirage/config.js @@ -24,7 +24,16 @@ export default function() { return scope.scope ? scope.scope.id === scope_id : false }); }); - this.post('/scopes'); + this.post('/scopes', function ({ scopes }, request) { + // Parent scope comes through the payload via `scope_id`, but this needs + // to be expanded to a scope object `scope: {id:..., type:...}` before + // saving, which is the gist of this function. + const attrs = this.normalizedRequestAttrs(); + const parentScopeAttrs = this.serialize(scopes.find(attrs.scopeId)); + delete attrs.scopeId; + attrs.scope = parentScopeAttrs; + return scopes.create(attrs); + }); // To simulate a possible real-world case, org scopes are not returned, // but project scopes are. // TODO this should be expanded to support other scenarios. From 279d552eca49fe21338d2eca5a44cdb7670aaa08 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Thu, 23 Jul 2020 14:50:48 -0400 Subject: [PATCH 26/70] test: update projects acceptance tests to work with scopes --- ui/core/tests/acceptance/projects-test.js | 88 +++++++++++++---------- ui/core/tests/helpers/.gitkeep | 0 2 files changed, 50 insertions(+), 38 deletions(-) delete mode 100644 ui/core/tests/helpers/.gitkeep diff --git a/ui/core/tests/acceptance/projects-test.js b/ui/core/tests/acceptance/projects-test.js index 0e7877c97..580a4a443 100644 --- a/ui/core/tests/acceptance/projects-test.js +++ b/ui/core/tests/acceptance/projects-test.js @@ -9,41 +9,57 @@ module('Acceptance | projects', function (hooks) { setupApplicationTest(hooks); setupMirage(hooks); - hooks.beforeEach(function () { - this.server.create('org'); - }); + let org; + let orgID; + let projectsURL; + let newProjectURL; + let existingProject; + let existingProjectURL; + let getProjectScopesCount; + let getFirstProjectScope; + let initialProjectScopesCount; + hooks.beforeEach(function () { + org = this.server.create('scope', {type: 'org'}, 'withChildren'); + getProjectScopesCount = () => this.server.schema.scopes.where({type: 'project'}).length; + getFirstProjectScope = () => this.server.schema.scopes.where({type: 'project'}).models[0]; + initialProjectScopesCount = getProjectScopesCount(); + orgID = org.id; + projectsURL = `/scopes/${orgID}/projects`; + newProjectURL = `/scopes/${orgID}/projects/new`; + existingProject = getFirstProjectScope(); + existingProjectURL = `/scopes/${orgID}/projects/${existingProject.id}`; + }); test('visiting projects', async function (assert) { assert.expect(1); this.server.createList('project', 1); - await visit('/orgs/1/projects'); + await visit(projectsURL); await a11yAudit(); - assert.equal(currentURL(), '/orgs/1/projects'); + assert.equal(currentURL(), projectsURL); }); test('can create new projects', async function (assert) { - assert.expect(3); - assert.equal(this.server.db.projects.length, 0); - await visit('/orgs/1/projects/new'); + assert.expect(1); + await visit(newProjectURL); + await a11yAudit(); await fillIn('[name="name"]', 'random string'); await click('[type="submit"]'); - assert.equal(currentURL(), '/orgs/1/projects/1'); - assert.equal(this.server.db.projects.length, 1); + assert.equal(getProjectScopesCount(), initialProjectScopesCount + 1); }); test('can cancel create new projects', async function (assert) { assert.expect(3); - assert.equal(this.server.db.projects.length, 0); - await visit('/orgs/1/projects/new'); + assert.equal(getProjectScopesCount(), initialProjectScopesCount); + await visit(newProjectURL); await fillIn('[name="name"]', 'random string'); await click('.rose-form-actions [type="button"]'); - assert.equal(currentURL(), '/orgs/1/projects'); - assert.equal(this.server.db.projects.length, 0); + assert.equal(currentURL(), projectsURL); + assert.equal(getProjectScopesCount(), initialProjectScopesCount); }); test('saving a new project with invalid fields displays error messages', async function (assert) { assert.expect(2); - this.server.post('/orgs/:org_id/projects', () => { + this.server.post('/scopes', () => { return new Response( 400, {}, @@ -62,8 +78,7 @@ module('Acceptance | projects', function (hooks) { } ); }); - await visit('/orgs/1/projects/new'); - await fillIn('[name="name"]', 'random string'); + await visit(newProjectURL); await click('[type="submit"]'); assert.ok( find('[role="alert"]').textContent.trim(), @@ -78,37 +93,35 @@ module('Acceptance | projects', function (hooks) { }); test('can save changes to existing project', async function (assert) { - assert.expect(2); - this.server.createList('project', 1, {name: 'MyProject'}); - await visit('/orgs/1/projects/1'); + assert.expect(3); + assert.notEqual(existingProject.name, 'random string'); + await visit(existingProjectURL); await fillIn('[name="name"]', 'random string'); await click('.rose-form-actions [type="submit"]'); - assert.equal(currentURL(), '/orgs/1/projects/1'); - assert.equal(this.server.db.projects[0].name, 'random string'); + assert.equal(currentURL(), existingProjectURL); + assert.equal(getFirstProjectScope().name, 'random string'); }); test('can cancel changes to existing project', async function (assert) { - assert.expect(1); - this.server.createList('project', 1, {name: 'MyProject'}); - await visit('/orgs/1/projects/1'); + assert.expect(2); + await visit(existingProjectURL); await fillIn('[name="name"]', 'random string'); await click('.rose-form-actions [type="button"]'); - assert.equal(find('[name="name"]').value, 'MyProject'); + assert.notEqual(existingProject.name, 'random string'); + assert.equal(find('[name="name"]').value, existingProject.name); }); test('can delete project', async function(assert) { - assert.expect(2); - this.server.createList('project', 1); - assert.equal(this.server.db.projects.length, 1); - await visit('/orgs/1/projects/1'); + assert.expect(1); + await visit(existingProjectURL); await click('.rose-button-warning'); - assert.equal(this.server.db.projects.length, 0); + assert.equal(getProjectScopesCount(), initialProjectScopesCount - 1); }); test('saving an existing project with invalid fields displays error messages', async function (assert) { assert.expect(2); this.server.createList('project', 1); - this.server.patch('/orgs/:org_id/projects/:id', () => { + this.server.patch('/scopes/:id', () => { return new Response( 400, {}, @@ -127,7 +140,7 @@ module('Acceptance | projects', function (hooks) { } ); }); - await visit('/orgs/1/projects/1'); + await visit(existingProjectURL); await fillIn('[name="name"]', 'random string'); await click('[type="submit"]'); assert.ok( @@ -144,8 +157,7 @@ module('Acceptance | projects', function (hooks) { test('errors are displayed when save project fails', async function (assert) { assert.expect(1); - this.server.createList('project', 1); - this.server.patch('/orgs/:org_id/projects/:id', () => { + this.server.patch('/scopes/:id', () => { return new Response( 490, {}, @@ -156,7 +168,7 @@ module('Acceptance | projects', function (hooks) { } ); }); - await visit('/orgs/1/projects/1'); + await visit(existingProjectURL); await fillIn('[name="name"]', 'random string'); await click('[type="submit"]'); assert.ok( @@ -169,7 +181,7 @@ module('Acceptance | projects', function (hooks) { test('errors are displayed when delete project fails', async function (assert) { assert.expect(1); this.server.createList('project', 1); - this.server.del('/orgs/:org_id/projects/:id', () => { + this.server.del('/scopes/:id', () => { return new Response( 490, {}, @@ -180,7 +192,7 @@ module('Acceptance | projects', function (hooks) { } ); }); - await visit('/orgs/1/projects/1'); + await visit(existingProjectURL); await click('.rose-button-warning'); assert.ok( find('[role="alert"]').textContent.trim(), diff --git a/ui/core/tests/helpers/.gitkeep b/ui/core/tests/helpers/.gitkeep deleted file mode 100644 index e69de29bb..000000000 From 1b7a8902df7be46e0d9f62b37fa840d4ba5f4381 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Thu, 23 Jul 2020 16:11:26 -0400 Subject: [PATCH 27/70] test: add projects path redirection test --- ui/core/tests/acceptance/projects-test.js | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/ui/core/tests/acceptance/projects-test.js b/ui/core/tests/acceptance/projects-test.js index 580a4a443..57ba7c17e 100644 --- a/ui/core/tests/acceptance/projects-test.js +++ b/ui/core/tests/acceptance/projects-test.js @@ -30,18 +30,26 @@ module('Acceptance | projects', function (hooks) { existingProject = getFirstProjectScope(); existingProjectURL = `/scopes/${orgID}/projects/${existingProject.id}`; }); + test('visiting projects', async function (assert) { assert.expect(1); - this.server.createList('project', 1); await visit(projectsURL); await a11yAudit(); assert.equal(currentURL(), projectsURL); }); + test('visiting projects within a project scope redirects to the parent org scope', async function (assert) { + assert.expect(2); + const wrongProjectsURL = `/scopes/${existingProject.id}/projects`; + assert.notEqual(wrongProjectsURL, projectsURL); + await visit(wrongProjectsURL); + await a11yAudit(); + assert.equal(currentURL(), projectsURL, 'Wrong projects path was redirected to correct path.'); + }); + test('can create new projects', async function (assert) { assert.expect(1); await visit(newProjectURL); - await a11yAudit(); await fillIn('[name="name"]', 'random string'); await click('[type="submit"]'); assert.equal(getProjectScopesCount(), initialProjectScopesCount + 1); @@ -120,7 +128,6 @@ module('Acceptance | projects', function (hooks) { test('saving an existing project with invalid fields displays error messages', async function (assert) { assert.expect(2); - this.server.createList('project', 1); this.server.patch('/scopes/:id', () => { return new Response( 400, @@ -180,7 +187,6 @@ module('Acceptance | projects', function (hooks) { test('errors are displayed when delete project fails', async function (assert) { assert.expect(1); - this.server.createList('project', 1); this.server.del('/scopes/:id', () => { return new Response( 490, From d08d52e0900688b5626044cc4d1233f9e0d5ef0a Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Thu, 23 Jul 2020 19:01:31 -0400 Subject: [PATCH 28/70] refactor: send scope_id query param on scope create, rather than serializing into the payload --- addons/api/addon/adapters/scope.js | 27 ++++++++++++++ addons/api/addon/serializers/scope.js | 36 ------------------- addons/api/app/adapters/scope.js | 1 + addons/api/app/serializers/scope.js | 1 - addons/api/tests/unit/adapters/scope-test.js | 23 ++++++++++++ .../api/tests/unit/serializers/scope-test.js | 32 ----------------- ui/core/mirage/config.js | 3 +- 7 files changed, 52 insertions(+), 71 deletions(-) create mode 100644 addons/api/addon/adapters/scope.js delete mode 100644 addons/api/addon/serializers/scope.js create mode 100644 addons/api/app/adapters/scope.js delete mode 100644 addons/api/app/serializers/scope.js create mode 100644 addons/api/tests/unit/adapters/scope-test.js delete mode 100644 addons/api/tests/unit/serializers/scope-test.js diff --git a/addons/api/addon/adapters/scope.js b/addons/api/addon/adapters/scope.js new file mode 100644 index 000000000..6d4549844 --- /dev/null +++ b/addons/api/addon/adapters/scope.js @@ -0,0 +1,27 @@ +import ApplicationAdapter from './application'; +import { serializeIntoHash } from '@ember-data/adapter/-private'; + +export default class ScopeAdapter extends ApplicationAdapter { + + /** + * Identical to default method except that we pass a query parameter + * indicating the parent scope. + * @override + * @param {Store} store + * @param {Model} type + * @param {Snapshot} snapshot + * @return {Promise} promise + */ + createRecord(store, type, snapshot) { + const parentScopeID = snapshot.attr('scope').attr('scope_id'); + let url = + this.buildURL(type.modelName, null, snapshot, 'createRecord'); + // TODO this should be generalized into `buildURL`, but currently Ember Data + // doesn't provide a way to manage query parameters. They are passed on + // to the underlying ajax/fetch service. + url = `${url}?scope_id=${parentScopeID}`; + const data = serializeIntoHash(store, type, snapshot); + return this.ajax(url, 'POST', { data }); + } + +} diff --git a/addons/api/addon/serializers/scope.js b/addons/api/addon/serializers/scope.js deleted file mode 100644 index 5767a8015..000000000 --- a/addons/api/addon/serializers/scope.js +++ /dev/null @@ -1,36 +0,0 @@ -import ApplicationSerializer from './application'; - -export default class ScopeSerializer extends ApplicationSerializer { - - // =attributes - - attrs = { - scope: { serialize: true } - }; - - // =methods - - /** - * While most resources do not serialize scope into a request body (scope - * is expressed in the URL), scopes themselves _do serialize scope_ as a - * string field `scope_id`. - * Otherwise delegate to default attribute serializer. - * - * @override - * @method serializeAttribute - * @param {Snapshot} snapshot - * @param {Object} json - * @param {String} key - * @param {Object} attribute - */ - serializeAttribute(snapshot, json, key) { - const value = super.serializeAttribute(...arguments); - const parentScope = snapshot.attr('scope'); - if (key === 'scope' && this._canSerialize(key) && parentScope) { - json.scope_id = parentScope.attr('scope_id'); - delete json[key]; - } - return value; - } - -} diff --git a/addons/api/app/adapters/scope.js b/addons/api/app/adapters/scope.js new file mode 100644 index 000000000..10878851f --- /dev/null +++ b/addons/api/app/adapters/scope.js @@ -0,0 +1 @@ +export { default } from 'api/adapters/scope'; diff --git a/addons/api/app/serializers/scope.js b/addons/api/app/serializers/scope.js deleted file mode 100644 index cdc15f3a2..000000000 --- a/addons/api/app/serializers/scope.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from 'api/serializers/scope'; diff --git a/addons/api/tests/unit/adapters/scope-test.js b/addons/api/tests/unit/adapters/scope-test.js new file mode 100644 index 000000000..7dcd482c7 --- /dev/null +++ b/addons/api/tests/unit/adapters/scope-test.js @@ -0,0 +1,23 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; +import setupMirage from 'ember-cli-mirage/test-support/setup-mirage'; + +module('Unit | Adapter | scope', function(hooks) { + setupTest(hooks); + setupMirage(hooks); + + test('it generates correct createRecord URLs with a scope_id query parameter', function (assert) { + assert.expect(1); + this.server.post('/v1/scopes', (schema, { queryParams }) =>{ + assert.equal(queryParams.scope_id, 'o_1'); + }); + const store = this.owner.lookup('service:store'); + const scope = store.createRecord('scope', { + scopeID: 'o_1' + }); + const type = scope.constructor; + const snapshot = scope._createSnapshot(); + const adapter = this.owner.lookup('adapter:scope'); + adapter.createRecord(store, type, snapshot); + }); +}); diff --git a/addons/api/tests/unit/serializers/scope-test.js b/addons/api/tests/unit/serializers/scope-test.js deleted file mode 100644 index 3993238fe..000000000 --- a/addons/api/tests/unit/serializers/scope-test.js +++ /dev/null @@ -1,32 +0,0 @@ -import { module, test } from 'qunit'; -import { setupTest } from 'ember-qunit'; - -module('Unit | Serializer | scope', function(hooks) { - setupTest(hooks); - - // Replace this with your real tests. - test('it exists', function(assert) { - let store = this.owner.lookup('service:store'); - let serializer = store.serializerFor('scope'); - - assert.ok(serializer); - }); - - test('it serializes records', function (assert) { - assert.expect(1); - const store = this.owner.lookup('service:store'); - const record = store.createRecord('scope', { - type: 'org', - name: 'User', - description: 'Description', - scopeID: 'global' - }); - const serializedRecord = record.serialize(); - assert.deepEqual(serializedRecord, { - type: 'org', - name: 'User', - description: 'Description', - scope_id: 'global' - }); - }); -}); diff --git a/ui/core/mirage/config.js b/ui/core/mirage/config.js index 6e554c34a..ed6859c83 100644 --- a/ui/core/mirage/config.js +++ b/ui/core/mirage/config.js @@ -29,8 +29,7 @@ export default function() { // to be expanded to a scope object `scope: {id:..., type:...}` before // saving, which is the gist of this function. const attrs = this.normalizedRequestAttrs(); - const parentScopeAttrs = this.serialize(scopes.find(attrs.scopeId)); - delete attrs.scopeId; + const parentScopeAttrs = this.serialize(scopes.find(request.queryParams.scope_id)); attrs.scope = parentScopeAttrs; return scopes.create(attrs); }); From 26388b8527239f34b6c2591e0a8daaf0d754b5d8 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Thu, 23 Jul 2020 19:31:34 -0400 Subject: [PATCH 29/70] refactor: deduce scope path prefix in adapter rather than passing it via the model save method --- addons/api/addon/adapters/application.js | 32 +++++++++++++------ addons/api/addon/models/base.js | 28 ---------------- .../tests/unit/adapters/application-test.js | 10 +++--- 3 files changed, 29 insertions(+), 41 deletions(-) diff --git a/addons/api/addon/adapters/application.js b/addons/api/addon/adapters/application.js index bfc109b3f..09b772cb7 100644 --- a/addons/api/addon/adapters/application.js +++ b/addons/api/addon/adapters/application.js @@ -59,8 +59,13 @@ export default class ApplicationAdapter extends RESTAdapter.extend( // =methods /** - * Prepends a scope to the URL prefix, if a scope is passed via the - * snapshot's `adapterOptions.scopeID` field. + * Prepends a scope to the URL prefix. Precedence is given to the ID passed + * via `adapterOptions.scopeID`. If this value isn't found in + * `adapterOptions`, we look to the `scope.scope_id` field on the snapshot, + * which most resources have. + * + * Note: scopes themselves never receive a scope URL prefix. + * * @override * @param {string} path * @param {string} parentURL @@ -69,18 +74,27 @@ export default class ApplicationAdapter extends RESTAdapter.extend( * @param {object} snapshot * @return {string} */ - urlPrefix(path, parentURL, modelName, id, snapshot={}) { + urlPrefix(path, parentURL, modelName, id, snapshot) { const prefix = super.urlPrefix(...arguments); const isScope = modelName === 'scope'; let scopePath = ''; + let scopeID = ''; + if (snapshot) { + // Not all snapshots have `attr` (such as array snapshots), + // so we do this sort of ugly check. + if (snapshot.attr) { + const parentScope = snapshot.attr('scope'); + if (parentScope) scopeID = parentScope.attr('scope_id'); + } + // Attempt to get scopeID from adapterOptions and fallback on its current + // value if it wasn't passed through options. + scopeID = getWithDefault(snapshot, 'adapterOptions.scopeID', scopeID); + } // 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}`; - } + // Ensure a slash is added between prefix + scope path if needed. + if (!isScope && scopeID && prefix.charAt(prefix.length - 1) !== '/') { + scopePath = `/scopes/${scopeID}`; } return `${prefix}${scopePath}`; } diff --git a/addons/api/addon/models/base.js b/addons/api/addon/models/base.js index 635d3b831..cfbcce14b 100644 --- a/addons/api/addon/models/base.js +++ b/addons/api/addon/models/base.js @@ -84,32 +84,4 @@ export default class BaseModel extends Model { return !this.canSave; } - // =methods - - /** - * If this model instance has a related scope, the scope's ID will be passed - * via `adapterOptions.scopeID` automatically. End users may optionally - * pass a custom scope ID to override. - * - * @example - * model.save(); // passes the model's current scope ID - * model.save({adapterOptions: {scopeID: 'global'}}); // override scope ID - * - * End users may specify a _custom method_ via `adapterOptions.method`. - * Custom methods are appended to the record's URL by the adapter. - * - * @example - * // Results in a URL: `/v1/model-name/id:my-custom-method` - * model.save({adapterOptions: {method: 'my-custom-method'}}); - * - * @return {Promise} - */ - save(options={adapterOptions: {}}) { - const scopeID = this.scopeID; - if (scopeID && !options.adapterOptions.scopeID) { - options.adapterOptions.scopeID = scopeID; - } - return super.save(options); - } - } diff --git a/addons/api/tests/unit/adapters/application-test.js b/addons/api/tests/unit/adapters/application-test.js index bea9b215c..c3d72d469 100644 --- a/addons/api/tests/unit/adapters/application-test.js +++ b/addons/api/tests/unit/adapters/application-test.js @@ -29,7 +29,7 @@ module('Unit | Adapter | application', function (hooks) { const scopeID = 'global'; const adapter = this.owner.lookup('adapter:application'); const prefix = adapter.urlPrefix(null, null, null, null, { - adapterOptions: {scopeID} + adapterOptions: { scopeID } }); assert.ok(config.api.namespace); assert.equal(prefix, `v1/scopes/${scopeID}`); @@ -40,7 +40,7 @@ module('Unit | Adapter | application', function (hooks) { const scopeID = 'global'; const adapter = this.owner.lookup('adapter:application'); const prefix = adapter.urlPrefix(null, null, 'scope', null, { - adapterOptions: {scopeID} + adapterOptions: { scopeID } }); assert.equal(prefix, `v1`); }); @@ -56,7 +56,7 @@ module('Unit | Adapter | application', function (hooks) { const method = 'set-something'; const adapter = this.owner.lookup('adapter:application'); const suffix = adapter.urlSuffix(null, null, { - adapterOptions: {method} + adapterOptions: { method } }); assert.ok(config.api.namespace); assert.equal(suffix, ':set-something'); @@ -95,7 +95,9 @@ module('Unit | Adapter | application', function (hooks) { assert.ok(true, 'Scoped resource URL was requested.'); return {items: []}; }); - await store.findAll('group', {adapterOptions: {scopeID: 'p_456'}}); + await store.findAll('group', { + adapterOptions: { scopeID: 'p_456' } + }); }); test('it rewrites PUT to PATCH, but leaves others unchanged', function (assert) { From 4ef58a0d6d57ed3529ed3365482e353473d07568 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Fri, 24 Jul 2020 11:15:40 -0400 Subject: [PATCH 30/70] chore: port auth addon from demo branch --- addons/auth/addon/.gitkeep | 0 addons/auth/addon/authenticators/password.js | 87 ++++++++++++ addons/auth/addon/session-stores/cookie.js | 134 ++++++++++++++++++ .../addon/session-stores/local-storage.js | 20 +++ addons/auth/package.json | 4 + .../dummy/app/authenticators/password.js | 6 + .../tests/dummy/app/session-stores/cookie.js | 6 + .../dummy/app/session-stores/local-storage.js | 3 + addons/auth/tests/dummy/mirage/config.js | 22 +++ .../tests/dummy/mirage/scenarios/default.js | 7 + .../dummy/mirage/serializers/application.js | 3 + .../unit/authenticators/password-test.js | 92 ++++++++++++ .../tests/unit/session-stores/cookie-test.js | 92 ++++++++++++ yarn.lock | 87 +++++++++++- 14 files changed, 558 insertions(+), 5 deletions(-) delete mode 100644 addons/auth/addon/.gitkeep create mode 100644 addons/auth/addon/authenticators/password.js create mode 100644 addons/auth/addon/session-stores/cookie.js create mode 100644 addons/auth/addon/session-stores/local-storage.js create mode 100644 addons/auth/tests/dummy/app/authenticators/password.js create mode 100644 addons/auth/tests/dummy/app/session-stores/cookie.js create mode 100644 addons/auth/tests/dummy/app/session-stores/local-storage.js create mode 100644 addons/auth/tests/dummy/mirage/config.js create mode 100644 addons/auth/tests/dummy/mirage/scenarios/default.js create mode 100644 addons/auth/tests/dummy/mirage/serializers/application.js create mode 100644 addons/auth/tests/unit/authenticators/password-test.js create mode 100644 addons/auth/tests/unit/session-stores/cookie-test.js diff --git a/addons/auth/addon/.gitkeep b/addons/auth/addon/.gitkeep deleted file mode 100644 index e69de29bb..000000000 diff --git a/addons/auth/addon/authenticators/password.js b/addons/auth/addon/authenticators/password.js new file mode 100644 index 000000000..f4dc5668c --- /dev/null +++ b/addons/auth/addon/authenticators/password.js @@ -0,0 +1,87 @@ +import BaseAuthenticator from 'ember-simple-auth/authenticators/base'; +import { resolve, reject } from 'rsvp'; +import fetch from 'fetch'; + +/** + * + * This authenticator is intended for use with Ember Simple Auth. + * + * Simple id/password base authenticator sends credentials to an endpoint + * specified in the `authEndpoint` URL. If the HTTP response code is in the + * success range, authentication resolves. Otherwise it rejects. + * + * Upon session invalidation, deauthentication is attempted at the + * `deauthEndpoint`, but is not guaranteed. + * + * This authenticator should not be used directly because it does not specify + * an `authEndpoint` or `deauthEndpoint` of its own. To use, generate an + * application authenticator in your app `authenticators/application.js` and + * extend this class. + * + * @example + * + * import PasswordAuthenticator from 'auth/authenticators/password'; + * export default class ApplicationAuthenticator extends PasswordAuthenticator { + * authEndpoint = '/api/authenticate'; + * deauthEndpoint = '/api/deauthenticate'; + * } + * + */ +export default class PasswordAuthenticator extends BaseAuthenticator { + // =methods + + /** + * Restores the session if data is present. Otherwise rejects. + * @override + * @param {object} data + * @return {Promise} + */ + restore(data) { + return data ? resolve(data) : reject(); + } + + /** + * Posts credentials to the URL specified in `authEndpoint` and resolves + * if a success HTTP status code is received, otherwise rejects. + * + * If requesting cookies, be sure to use the cookie session store. Otherwise + * use the local storage session store. + * + * @override + * @param {object} creds + * @param {string} creds.identification is sent to server as `name` + * @param {string} creds.password + * @param {string} authMethodId ID of the auth method to use + * @param {boolean} requestCookies request cookie tokens (default `true`) + * @return {Promise} + */ + async authenticate( + { identification: name, password }, + authMethodID, + requestCookies = true + ) { + const body = JSON.stringify({ + auth_method_id: authMethodID, + token_type: requestCookies ? 'cookie' : null, + credentials: { name, password }, + }); + const response = await fetch(this.authEndpoint, { method: 'post', body }); + const json = await response.json(); + return response.status < 400 ? resolve(json) : reject(); + } + + /** + * Posts to the `deauthEntpoint` on a best-effort basis and then returns. + * Deauthentication with the server is not guaranteed and request failures + * are ignored. + * + * @override + * @return {Promise} + */ + invalidate() { + fetch(this.deauthEndpoint, { method: 'post' }).catch(() => { + /* no op */ + }); + return super.invalidate(...arguments); + } +} diff --git a/addons/auth/addon/session-stores/cookie.js b/addons/auth/addon/session-stores/cookie.js new file mode 100644 index 000000000..ea0501ad5 --- /dev/null +++ b/addons/auth/addon/session-stores/cookie.js @@ -0,0 +1,134 @@ +import BaseSessionStore from 'ember-simple-auth/session-stores/base'; +import { resolve, reject } from 'rsvp'; +import { get } from '@ember/object'; +import { inject as service } from '@ember/service'; + +/** + * + * This session store is intended for use with Ember Simple Auth when requesting + * that the server send cookies (see authenticators). + * + * The cookie session store is used to maintain session state for auth servers + * that send cookies. In this strategy, the server sets any cookies needed for + * future authorization. This store determines if a session is authenticated + * by looking for the presence of a cookie with name specified in `cookieName`. + * + * This session store should not be used directly because it does not specify + * `cookiePath` or `cookieName`. To use, create an application session store + * and extend this class, being sure to specify these attributes. + * + * @example + * + * import CookieSessionStore from 'auth/session-stores/cookie'; + * export default class ApplicationSessionStore extends CookieSessionStore { + * cookiePath = '/'; + * cookieName = 'auth-token'; + * } + * + */ +export default class CookieSessionStore extends BaseSessionStore { + // =services + + @service cookies; + + // =attributes + + /** + * The name of the cookie that stores the name of the authenticator, derived + * from the name of the session cookie. The authenticator name is used + * internally by Ember Simple Auth, since it supports simultaneous + * authentication schemes within a single application. + * @type {string} + */ + get authenticatorCookieName() { + return `${this.cookieName}-authenticator`; + } + + /** + * Name of the authenticator used, if any. + * @type {?string} + */ + get authenticatorName() { + return this.cookies.read(this.authenticatorCookieName); + } + + /** + * True if both session and authenticator cookies are present. + * False otherwise. + * @type {boolean} + */ + get sessionExists() { + const tokenExists = this.cookies.exists(this.cookieName); + const authenticatorExists = this.cookies.exists( + this.authenticatorCookieName + ); + return tokenExists && authenticatorExists; + } + + // =methods + + /** + * This method is called automatically on initialization to perform cleanup + * if necessary. + * + * Sessions exist only if both the session and authenticator cookies are + * present. If either one is missing, we want to cleanup to be sure they + * are both absent. + */ + init() { + if (!this.sessionExists) this.clear(); + } + + /** + * While the primary session cookie is persisted by the server, not the + * application, we still need to "remember" the name of the authenticator + * used to authenticate to properly restore a session. Thus when this method + * is called with an authenticator name, we save it into a cookie with name + * `authenticatorCookieName`. If this method is called without an + * authenticator, all related cookies are cleared. + * @param {object} data + * @return {Promise} + */ + persist(data) { + const authenticator = get(data, 'authenticated.authenticator'); + const path = this.cookiePath; + if (authenticator) { + this.cookies.write(this.authenticatorCookieName, authenticator, { path }); + } else { + this.clear(); + } + return resolve(); + } + + /** + * If a session exists (as reported by `sessionExists`), then this method + * resolves with the authentication object expected by Ember Simple Auth, + * which must specify the name of the authenticator used to authenticate. + * + * If the session does not exist, all related cookies are cleared and this + * method rejects. + * + * @return {Promise} + */ + restore() { + if (this.sessionExists) { + return resolve({ + authenticated: { + authenticator: this.authenticatorName, + }, + }); + } + this.clear(); + return reject(); + } + + /** + * Clears both cookies related to the session: the server specified session + * cookie as well as the authenticator name cookie. + */ + clear() { + const path = this.cookiePath; + this.cookies.clear(this.cookieName, { path }); + this.cookies.clear(this.authenticatorCookieName, { path }); + } +} diff --git a/addons/auth/addon/session-stores/local-storage.js b/addons/auth/addon/session-stores/local-storage.js new file mode 100644 index 000000000..7c216fa55 --- /dev/null +++ b/addons/auth/addon/session-stores/local-storage.js @@ -0,0 +1,20 @@ +import LocalStorageSessionStore from 'ember-simple-auth/session-stores/local-storage'; + +/** + * + * This session store is intended for use with Ember Simple Auth when requesting + * that the server send a full token (e.g. cookies are not requested, + * see authenticators). + * + * NOTE: when using a local storage scheme, authorization is not provided + * automatically. In order to achieve authorization... TODO + * + * @example + * + * import LocalStorageSessionStore from 'auth/session-stores/local-storage'; + * export default class ApplicationSessionStore extends LocalStorageSessionStore { + * + * } + * + */ +export default class AuthLocalStorageSessionStore extends LocalStorageSessionStore {} diff --git a/addons/auth/package.json b/addons/auth/package.json index cdd1f5623..33199987a 100644 --- a/addons/auth/package.json +++ b/addons/auth/package.json @@ -33,16 +33,20 @@ "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", + "ember-cookies": "^0.5.2", "ember-data": "^3.16.0", "ember-disable-prototype-extensions": "^1.1.3", "ember-export-application-global": "^2.0.1", + "ember-fetch": "^8.0.1", "ember-load-initializers": "^2.1.1", "ember-maybe-import-regenerator": "^0.1.6", "ember-qunit": "^4.6.0", "ember-resolver": "^7.0.0", + "ember-simple-auth": "^3.0.0", "ember-source": "~3.16.0", "ember-source-channel-url": "^2.0.1", "ember-try": "^1.4.0", diff --git a/addons/auth/tests/dummy/app/authenticators/password.js b/addons/auth/tests/dummy/app/authenticators/password.js new file mode 100644 index 000000000..2f312a7e4 --- /dev/null +++ b/addons/auth/tests/dummy/app/authenticators/password.js @@ -0,0 +1,6 @@ +import PasswordAuthenticator from 'auth/authenticators/password'; + +export default class ApplicationPasswordAuthenticator extends PasswordAuthenticator { + authEndpoint = '/authenticate'; + deauthEndpoint = '/deauthenticate'; +} diff --git a/addons/auth/tests/dummy/app/session-stores/cookie.js b/addons/auth/tests/dummy/app/session-stores/cookie.js new file mode 100644 index 000000000..50c1a4441 --- /dev/null +++ b/addons/auth/tests/dummy/app/session-stores/cookie.js @@ -0,0 +1,6 @@ +import CookieSessionStore from 'auth/session-stores/cookie'; + +export default class ApplicationCookieSessionStore extends CookieSessionStore { + cookiePath = '/'; + cookieName = 'my-cookie'; +} diff --git a/addons/auth/tests/dummy/app/session-stores/local-storage.js b/addons/auth/tests/dummy/app/session-stores/local-storage.js new file mode 100644 index 000000000..b7546d920 --- /dev/null +++ b/addons/auth/tests/dummy/app/session-stores/local-storage.js @@ -0,0 +1,3 @@ +import LocalStorageSessionStore from 'auth/session-stores/local-storage'; + +export default class ApplicationLocalStorageSessionStore extends LocalStorageSessionStore {} diff --git a/addons/auth/tests/dummy/mirage/config.js b/addons/auth/tests/dummy/mirage/config.js new file mode 100644 index 000000000..3e74fe7c0 --- /dev/null +++ b/addons/auth/tests/dummy/mirage/config.js @@ -0,0 +1,22 @@ +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 + */ +} diff --git a/addons/auth/tests/dummy/mirage/scenarios/default.js b/addons/auth/tests/dummy/mirage/scenarios/default.js new file mode 100644 index 000000000..d388296b5 --- /dev/null +++ b/addons/auth/tests/dummy/mirage/scenarios/default.js @@ -0,0 +1,7 @@ +export default function (/* server */) { + /* + Seed your development database using your factories. + This data will not be loaded in your tests. + */ + // server.createList('post', 10); +} diff --git a/addons/auth/tests/dummy/mirage/serializers/application.js b/addons/auth/tests/dummy/mirage/serializers/application.js new file mode 100644 index 000000000..6224ab8c9 --- /dev/null +++ b/addons/auth/tests/dummy/mirage/serializers/application.js @@ -0,0 +1,3 @@ +import { JSONAPISerializer } from 'ember-cli-mirage'; + +export default JSONAPISerializer.extend({}); diff --git a/addons/auth/tests/unit/authenticators/password-test.js b/addons/auth/tests/unit/authenticators/password-test.js new file mode 100644 index 000000000..bf516dd43 --- /dev/null +++ b/addons/auth/tests/unit/authenticators/password-test.js @@ -0,0 +1,92 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; +import { Response } from 'miragejs'; +import setupMirage from 'ember-cli-mirage/test-support/setup-mirage'; + +module('Unit | Authenticator | password', function (hooks) { + setupTest(hooks); + setupMirage(hooks); + + test('it authenticates to the specified authEndpoint', async function (assert) { + assert.expect(2); + const authenticator = this.owner.lookup('authenticator:password'); + this.server.post(authenticator.authEndpoint, (schema, request) => { + const json = JSON.parse(request.requestBody); + assert.ok(json.token_type, 'Requested token cookies by default'); + return new Response(200); + }); + await authenticator.authenticate({}, '').then(() => { + assert.ok(true, 'authentication succeeded'); + }); + }); + + test('it can authenticate without requesting cookies', async function (assert) { + assert.expect(2); + const authenticator = this.owner.lookup('authenticator:password'); + this.server.post(authenticator.authEndpoint, (schema, request) => { + const json = JSON.parse(request.requestBody); + assert.notOk(json.token_type, 'Did not request tokens cookies'); + return new Response(200); + }); + await authenticator.authenticate({}, '', false).then(() => { + assert.ok(true, 'authentication succeeded'); + }); + }); + + test('it authenticates with the expected payload', async function (assert) { + assert.expect(1); + const authenticator = this.owner.lookup('authenticator:password'); + this.server.post(authenticator.authEndpoint, (schema, request) => { + const json = JSON.parse(request.requestBody); + assert.deepEqual(json, { + auth_method_id: '123', + token_type: 'cookie', + credentials: { + name: 'foo', + password: 'bar', + }, + }); + return new Response(200); + }); + const creds = { + identification: 'foo', + password: 'bar', + }; + const authMethodID = '123'; + await authenticator.authenticate(creds, authMethodID); + }); + + test('it rejects if the endpoint sends an error status code', async function (assert) { + assert.expect(1); + const authenticator = this.owner.lookup('authenticator:password'); + this.server.post(authenticator.authEndpoint, () => { + return new Response(400); + }); + await authenticator.authenticate({}).catch(() => { + assert.ok(true, 'authentication failed'); + }); + }); + + test('it deauthenticates on invalidation', async function (assert) { + assert.expect(1); + const authenticator = this.owner.lookup('authenticator:password'); + this.server.post(authenticator.deauthEndpoint, () => { + assert.ok(true, 'deauthentication occurred'); + return new Response(200); + }); + await authenticator.invalidate(); + }); + + test('it invalidation succeeds even if the deauthentication request fails', async function (assert) { + assert.expect(2); + const authenticator = this.owner.lookup('authenticator:password'); + this.server.post(authenticator.deauthEndpoint, () => { + assert.ok(true, 'deauthentication was requested'); + return new Response(500); + }); + await authenticator + .invalidate() + .then(() => assert.ok(true)) + .catch(() => assert.notOk(true, 'uh oh, this should not happen')); + }); +}); diff --git a/addons/auth/tests/unit/session-stores/cookie-test.js b/addons/auth/tests/unit/session-stores/cookie-test.js new file mode 100644 index 000000000..464ebab52 --- /dev/null +++ b/addons/auth/tests/unit/session-stores/cookie-test.js @@ -0,0 +1,92 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Session Store | cookie', function (hooks) { + setupTest(hooks); + + let sessionStore; + + hooks.beforeEach(function () { + sessionStore = this.owner.lookup('session-store:cookie'); + sessionStore.clear(); + }); + + hooks.afterEach(function () { + sessionStore.clear(); + }); + + test('it restores a session if both necessary cookies are present (session exists)', async function (assert) { + assert.expect(2); + sessionStore.cookies.write(sessionStore.cookieName, 'token-value', { + path: '/', + }); + sessionStore.cookies.write( + sessionStore.authenticatorCookieName, + 'authenticator-name', + { path: '/' } + ); + assert.ok(sessionStore.sessionExists); + const session = await sessionStore.restore(); + assert.deepEqual(session, { + authenticated: { + authenticator: 'authenticator-name', + }, + }); + }); + + test('it rejects restore if the session does not exist', async function (assert) { + assert.expect(2); + assert.notOk(sessionStore.sessionExists); + await sessionStore.restore().catch(() => {}); + assert.ok(true, 'Rejected'); + }); + + test('it cleans up cookies on restore rejection', async function (assert) { + assert.expect(4); + // For session cookie + sessionStore.cookies.write(sessionStore.cookieName, 'token-value', { + path: '/', + }); + assert.ok( + sessionStore.cookies.exists(sessionStore.cookieName), + 'cookie exists' + ); + await sessionStore.restore().catch(() => {}); + assert.notOk( + sessionStore.cookies.exists(sessionStore.cookieName), + 'cookie was cleared' + ); + // For authenticator name cookie + sessionStore.cookies.write( + sessionStore.authenticatorCookieName, + 'authenticator-name', + { path: '/' } + ); + assert.ok( + sessionStore.cookies.exists(sessionStore.authenticatorCookieName), + 'cookie exists' + ); + await sessionStore.restore().catch(() => {}); + assert.notOk( + sessionStore.cookies.exists(sessionStore.authenticatorCookieName), + 'cookie was cleared' + ); + }); + + test('it persists only an authenticator name cookie, since the server persists the session cookie', async function (assert) { + assert.expect(4); + assert.notOk(sessionStore.cookies.exists(sessionStore.cookieName)); + assert.notOk( + sessionStore.cookies.exists(sessionStore.authenticatorCookieName) + ); + sessionStore.persist({ authenticated: { authenticator: 'foobar' } }); + assert.notOk( + sessionStore.cookies.exists(sessionStore.cookieName), + 'session cookie was not set' + ); + assert.equal( + sessionStore.cookies.read(sessionStore.authenticatorCookieName), + 'foobar' + ); + }); +}); diff --git a/yarn.lock b/yarn.lock index c7cf88da0..5304e5ace 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5335,6 +5335,11 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= +base-64@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/base-64/-/base-64-0.1.0.tgz#780a99c84e7d600260361511c4877613bf24f6bb" + integrity sha1-eAqZyE59YAJgNhURxId2E78k9rs= + base64-arraybuffer@0.1.5: version "0.1.5" resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz#73926771923b5a19747ad666aa5cd4bf9c6e9ce8" @@ -5707,6 +5712,23 @@ broccoli-concat@^3.2.2, broccoli-concat@^3.7.1, broccoli-concat@^3.7.4, broccoli lodash.uniq "^4.2.0" walk-sync "^0.3.2" +broccoli-concat@^4.2.4: + version "4.2.4" + resolved "https://registry.yarnpkg.com/broccoli-concat/-/broccoli-concat-4.2.4.tgz#78e359ddc540b999d815355163bf3cfb6bd67322" + integrity sha512-NgdBIE57r+U/AslBohQr0mCS7PopIWL8dihMI1CzqffQkisAgqWMuddjYmizqRBQlml7crBFaBeUnPDHhf4/RQ== + dependencies: + broccoli-debug "^0.6.5" + broccoli-kitchen-sink-helpers "^0.3.1" + broccoli-plugin "^4.0.2" + ensure-posix-path "^1.0.2" + fast-sourcemap-concat "^2.1.0" + find-index "^1.1.0" + fs-extra "^8.1.0" + fs-tree-diff "^2.0.1" + lodash.merge "^4.6.2" + lodash.omit "^4.1.0" + lodash.uniq "^4.2.0" + broccoli-config-loader@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/broccoli-config-loader/-/broccoli-config-loader-1.0.1.tgz#d10aaf8ebc0cb45c1da5baa82720e1d88d28c80a" @@ -5744,7 +5766,7 @@ broccoli-file-creator@^1.1.1: broccoli-plugin "^1.1.0" mkdirp "^0.5.1" -broccoli-file-creator@^2.1.1: +broccoli-file-creator@^2.0.0, broccoli-file-creator@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/broccoli-file-creator/-/broccoli-file-creator-2.1.1.tgz#7351dd2496c762cfce7736ce9b49e3fce0c7b7db" integrity sha512-YpjOExWr92C5vhnK0kmD81kM7U09kdIRZk9w4ZDCDHuHXW+VE/x6AGEOQQW3loBQQ6Jk+k+TSm8dESy4uZsnjw== @@ -5816,7 +5838,7 @@ broccoli-funnel@^1.0.1: symlink-or-copy "^1.0.0" walk-sync "^0.3.1" -broccoli-funnel@^2.0.0, broccoli-funnel@^2.0.1, broccoli-funnel@^2.0.2, broccoli-funnel@~2.0.2: +"broccoli-funnel@^1.2.0 || ^2.0.0", broccoli-funnel@^2.0.0, broccoli-funnel@^2.0.1, broccoli-funnel@^2.0.2, broccoli-funnel@~2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/broccoli-funnel/-/broccoli-funnel-2.0.2.tgz#0edf629569bc10bd02cc525f74b9a38e71366a75" integrity sha512-/vDTqtv7ipjEZQOVqO4vGDVAOZyuYzQ/EgGoyewfOgh1M7IQAToBKZI0oAQPgMBeFPPlIbfMuAngk+ohPBuaHQ== @@ -5896,7 +5918,7 @@ broccoli-merge-trees@^2.0.0: broccoli-plugin "^1.3.0" merge-trees "^1.0.1" -broccoli-merge-trees@^3.0.0, broccoli-merge-trees@^3.0.1, broccoli-merge-trees@^3.0.2, broccoli-merge-trees@~3.0.2: +"broccoli-merge-trees@^2.0.0 || ^3.0.0", broccoli-merge-trees@^3.0.0, broccoli-merge-trees@^3.0.1, broccoli-merge-trees@^3.0.2, broccoli-merge-trees@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/broccoli-merge-trees/-/broccoli-merge-trees-3.0.2.tgz#f33b451994225522b5c9bcf27d59decfd8ba537d" integrity sha512-ZyPAwrOdlCddduFbsMyyFzJUrvW6b04pMvDiAQZrCwghlvgowJDY+EfoXn+eR1RRA5nmGHJ+B68T63VnpRiT1A== @@ -8329,7 +8351,7 @@ ember-cli-babel-plugin-helpers@^1.0.0, ember-cli-babel-plugin-helpers@^1.1.0: resolved "https://registry.yarnpkg.com/ember-cli-babel-plugin-helpers/-/ember-cli-babel-plugin-helpers-1.1.0.tgz#de3baedd093163b6c2461f95964888c1676325ac" integrity sha512-Zr4my8Xn+CzO0gIuFNXji0eTRml5AxZUTDQz/wsNJ5AJAtyFWCY4QtKdoELNNbiCVGt1lq5yLiwTm4scGKu6xA== -ember-cli-babel@7: +ember-cli-babel@7, ember-cli-babel@^7.1.0: version "7.21.0" resolved "https://registry.yarnpkg.com/ember-cli-babel/-/ember-cli-babel-7.21.0.tgz#c79e888876aee87dfc3260aee7cb580b74264bbc" integrity sha512-jHVi9melAibo0DrAG3GAxid+29xEyjBoU53652B4qcu3Xp58feZGTH/JGXovH7TjvbeNn65zgNyoV3bk1onULw== @@ -8901,6 +8923,14 @@ ember-compatibility-helpers@^1.1.1, ember-compatibility-helpers@^1.1.2, ember-co ember-cli-version-checker "^2.1.1" semver "^5.4.1" +ember-cookies@^0.5.0, ember-cookies@^0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/ember-cookies/-/ember-cookies-0.5.2.tgz#06e33463f2f83254fefaf224cc944dec3fb9d3ba" + integrity sha512-nZ7oG97kBcO9UHjO95ryABpnVx62Bhxo7lIsBJNmWvFXleILm9DGueiAynzXxuYWWPuKIeeSbYakrS1869tNTw== + dependencies: + ember-cli-babel "^7.1.0" + ember-getowner-polyfill "^1.1.0 || ^2.0.0" + ember-copy@1.0.0, ember-copy@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/ember-copy/-/ember-copy-1.0.0.tgz#426554ba6cf65920f31d24d0a3ca2cb1be16e4aa" @@ -9031,6 +9061,25 @@ ember-fetch@^7.0.0: node-fetch "^2.6.0" whatwg-fetch "^3.0.0" +ember-fetch@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/ember-fetch/-/ember-fetch-8.0.1.tgz#dc57f3c3ba464e4a8722785f6b44265c46b38020" + integrity sha512-J+knuBFQJlMQUuij2tziMiVcJ+Ss0lCV2XiCQO2cHmG2PUsfvLlTGZ04uUyOXPqrJISxY2W3Et43fQBg4jy7SA== + dependencies: + abortcontroller-polyfill "^1.4.0" + broccoli-concat "^4.2.4" + broccoli-debug "^0.6.5" + broccoli-merge-trees "^4.2.0" + broccoli-rollup "^2.1.1" + broccoli-stew "^3.0.0" + broccoli-templater "^2.0.1" + calculate-cache-key-for-tree "^2.0.0" + caniuse-api "^3.0.0" + ember-cli-babel "^7.13.2" + ember-cli-typescript "^3.1.3" + node-fetch "^2.6.0" + whatwg-fetch "^3.0.0" + ember-get-config@0.2.4, ember-get-config@^0.2.2, ember-get-config@^0.2.4: version "0.2.4" resolved "https://registry.yarnpkg.com/ember-get-config/-/ember-get-config-0.2.4.tgz#118492a2a03d73e46004ed777928942021fe1ecd" @@ -9039,7 +9088,7 @@ ember-get-config@0.2.4, ember-get-config@^0.2.2, ember-get-config@^0.2.4: broccoli-file-creator "^1.1.1" ember-cli-babel "^6.3.0" -ember-getowner-polyfill@^2.0.0: +"ember-getowner-polyfill@^1.1.0 || ^2.0.0", ember-getowner-polyfill@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/ember-getowner-polyfill/-/ember-getowner-polyfill-2.2.0.tgz#38e7dccbcac69d5ec694000329ec0b2be651d2b2" integrity sha512-rwGMJgbGzxIAiWYjdpAh04Abvt0s3HuS/VjHzUFhVyVg2pzAuz45B9AzOxYXzkp88vFC7FPaiA4kE8NxNk4A4Q== @@ -9247,6 +9296,20 @@ ember-router-generator@^2.0.0: "@babel/traverse" "^7.4.5" recast "^0.18.1" +ember-simple-auth@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ember-simple-auth/-/ember-simple-auth-3.0.0.tgz#d994f512cda1a8a5a6ef5a853fac60c0c0d6acd6" + integrity sha512-JaEw5W/Z9+HIH9I2BmE/JD4KofH4rp79Y51zJpt3UQI1OwWFOzFa+ulN0rK/DVLpGOz7uL7hBIE/rtEcEN1eqA== + dependencies: + base-64 "^0.1.0" + broccoli-file-creator "^2.0.0" + broccoli-funnel "^1.2.0 || ^2.0.0" + broccoli-merge-trees "^2.0.0 || ^3.0.0" + ember-cli-babel "^7.12.0" + ember-cli-is-package-missing "^1.0.0" + ember-cookies "^0.5.0" + silent-error "^1.0.0" + ember-source-channel-url@^1.0.1: version "1.2.0" resolved "https://registry.yarnpkg.com/ember-source-channel-url/-/ember-source-channel-url-1.2.0.tgz#77eb9d0889e5f5370e6c70fcb2696c63ff4a34a1" @@ -10209,6 +10272,20 @@ fast-sourcemap-concat@^1.4.0: source-map-url "^0.3.0" sourcemap-validator "^1.1.0" +fast-sourcemap-concat@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-sourcemap-concat/-/fast-sourcemap-concat-2.1.0.tgz#12dd36bfc38c804093e4bd1de61dd6216f574211" + integrity sha512-L9uADEnnHOeF4U5Kc3gzEs3oFpNCFkiTJXvT+nKmR0zcFqHZJJbszWT7dv4t9558FJRGpCj8UxUpTgz2zwiIZA== + dependencies: + chalk "^2.0.0" + fs-extra "^5.0.0" + heimdalljs-logger "^0.1.9" + memory-streams "^0.1.3" + mkdirp "^0.5.0" + source-map "^0.4.2" + source-map-url "^0.3.0" + sourcemap-validator "^1.1.0" + fastq@^1.6.0: version "1.8.0" resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.8.0.tgz#550e1f9f59bbc65fe185cb6a9b4d95357107f481" From c5280ca27cda7505c8b103314ed281a9b5a7cd67 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Fri, 24 Jul 2020 11:29:19 -0400 Subject: [PATCH 31/70] refactor: auth method is no longer passed via the payload, but is encoded in the path --- addons/auth/addon/authenticators/password.js | 3 --- addons/auth/tests/unit/authenticators/password-test.js | 8 +++----- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/addons/auth/addon/authenticators/password.js b/addons/auth/addon/authenticators/password.js index f4dc5668c..25ba4da1c 100644 --- a/addons/auth/addon/authenticators/password.js +++ b/addons/auth/addon/authenticators/password.js @@ -51,17 +51,14 @@ export default class PasswordAuthenticator extends BaseAuthenticator { * @param {object} creds * @param {string} creds.identification is sent to server as `name` * @param {string} creds.password - * @param {string} authMethodId ID of the auth method to use * @param {boolean} requestCookies request cookie tokens (default `true`) * @return {Promise} */ async authenticate( { identification: name, password }, - authMethodID, requestCookies = true ) { const body = JSON.stringify({ - auth_method_id: authMethodID, token_type: requestCookies ? 'cookie' : null, credentials: { name, password }, }); diff --git a/addons/auth/tests/unit/authenticators/password-test.js b/addons/auth/tests/unit/authenticators/password-test.js index bf516dd43..5ab5d247a 100644 --- a/addons/auth/tests/unit/authenticators/password-test.js +++ b/addons/auth/tests/unit/authenticators/password-test.js @@ -15,7 +15,7 @@ module('Unit | Authenticator | password', function (hooks) { assert.ok(json.token_type, 'Requested token cookies by default'); return new Response(200); }); - await authenticator.authenticate({}, '').then(() => { + await authenticator.authenticate({}).then(() => { assert.ok(true, 'authentication succeeded'); }); }); @@ -28,7 +28,7 @@ module('Unit | Authenticator | password', function (hooks) { assert.notOk(json.token_type, 'Did not request tokens cookies'); return new Response(200); }); - await authenticator.authenticate({}, '', false).then(() => { + await authenticator.authenticate({}, false).then(() => { assert.ok(true, 'authentication succeeded'); }); }); @@ -39,7 +39,6 @@ module('Unit | Authenticator | password', function (hooks) { this.server.post(authenticator.authEndpoint, (schema, request) => { const json = JSON.parse(request.requestBody); assert.deepEqual(json, { - auth_method_id: '123', token_type: 'cookie', credentials: { name: 'foo', @@ -52,8 +51,7 @@ module('Unit | Authenticator | password', function (hooks) { identification: 'foo', password: 'bar', }; - const authMethodID = '123'; - await authenticator.authenticate(creds, authMethodID); + await authenticator.authenticate(creds); }); test('it rejects if the endpoint sends an error status code', async function (assert) { From 69b08ffcb061cdccb3eb083591894e961d8ab632 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Fri, 24 Jul 2020 14:37:50 -0400 Subject: [PATCH 32/70] refactor: auth/deauth URLs should be built via a method call rather than statically --- addons/auth/addon/authenticators/password.js | 23 +++++++++++-------- .../dummy/app/authenticators/password.js | 11 +++++++-- .../unit/authenticators/password-test.js | 12 +++++----- 3 files changed, 28 insertions(+), 18 deletions(-) diff --git a/addons/auth/addon/authenticators/password.js b/addons/auth/addon/authenticators/password.js index 25ba4da1c..7c6d79a4e 100644 --- a/addons/auth/addon/authenticators/password.js +++ b/addons/auth/addon/authenticators/password.js @@ -7,16 +7,16 @@ import fetch from 'fetch'; * This authenticator is intended for use with Ember Simple Auth. * * Simple id/password base authenticator sends credentials to an endpoint - * specified in the `authEndpoint` URL. If the HTTP response code is in the + * specified by the `buildAuthEndpointURL`. If the HTTP response code is in the * success range, authentication resolves. Otherwise it rejects. * - * Upon session invalidation, deauthentication is attempted at the - * `deauthEndpoint`, but is not guaranteed. + * Upon session invalidation, deauthentication is attempted at the URL generated + * by `buildDeauthEndpointURL`, but is not guaranteed. * * This authenticator should not be used directly because it does not specify - * an `authEndpoint` or `deauthEndpoint` of its own. To use, generate an - * application authenticator in your app `authenticators/application.js` and - * extend this class. + * an `buildAuthEndpointURL` or `buildDeauthEndpointURL` of its own. + * To use, generate an application authenticator in your app + * `authenticators/application.js` and extend this class. * * @example * @@ -56,13 +56,15 @@ export default class PasswordAuthenticator extends BaseAuthenticator { */ async authenticate( { identification: name, password }, - requestCookies = true + requestCookies = true, + options ) { const body = JSON.stringify({ token_type: requestCookies ? 'cookie' : null, credentials: { name, password }, }); - const response = await fetch(this.authEndpoint, { method: 'post', body }); + const authEndpointURL = this.buildAuthEndpointURL(options); + const response = await fetch(authEndpointURL, { method: 'post', body }); const json = await response.json(); return response.status < 400 ? resolve(json) : reject(); } @@ -75,8 +77,9 @@ export default class PasswordAuthenticator extends BaseAuthenticator { * @override * @return {Promise} */ - invalidate() { - fetch(this.deauthEndpoint, { method: 'post' }).catch(() => { + invalidate(options) { + const deauthEndpointURL = this.buildDeauthEndpointURL(options); + fetch(deauthEndpointURL, { method: 'post' }).catch(() => { /* no op */ }); return super.invalidate(...arguments); diff --git a/addons/auth/tests/dummy/app/authenticators/password.js b/addons/auth/tests/dummy/app/authenticators/password.js index 2f312a7e4..e259039a3 100644 --- a/addons/auth/tests/dummy/app/authenticators/password.js +++ b/addons/auth/tests/dummy/app/authenticators/password.js @@ -1,6 +1,13 @@ import PasswordAuthenticator from 'auth/authenticators/password'; export default class ApplicationPasswordAuthenticator extends PasswordAuthenticator { - authEndpoint = '/authenticate'; - deauthEndpoint = '/deauthenticate'; + + buildAuthEndpointURL() { + return '/authenticate'; + } + + buildDeauthEndpointURL() { + return '/deauthenticate'; + } + } diff --git a/addons/auth/tests/unit/authenticators/password-test.js b/addons/auth/tests/unit/authenticators/password-test.js index 5ab5d247a..aebdfee56 100644 --- a/addons/auth/tests/unit/authenticators/password-test.js +++ b/addons/auth/tests/unit/authenticators/password-test.js @@ -10,7 +10,7 @@ module('Unit | Authenticator | password', function (hooks) { test('it authenticates to the specified authEndpoint', async function (assert) { assert.expect(2); const authenticator = this.owner.lookup('authenticator:password'); - this.server.post(authenticator.authEndpoint, (schema, request) => { + this.server.post(authenticator.buildAuthEndpointURL(), (schema, request) => { const json = JSON.parse(request.requestBody); assert.ok(json.token_type, 'Requested token cookies by default'); return new Response(200); @@ -23,7 +23,7 @@ module('Unit | Authenticator | password', function (hooks) { test('it can authenticate without requesting cookies', async function (assert) { assert.expect(2); const authenticator = this.owner.lookup('authenticator:password'); - this.server.post(authenticator.authEndpoint, (schema, request) => { + this.server.post(authenticator.buildAuthEndpointURL(), (schema, request) => { const json = JSON.parse(request.requestBody); assert.notOk(json.token_type, 'Did not request tokens cookies'); return new Response(200); @@ -36,7 +36,7 @@ module('Unit | Authenticator | password', function (hooks) { test('it authenticates with the expected payload', async function (assert) { assert.expect(1); const authenticator = this.owner.lookup('authenticator:password'); - this.server.post(authenticator.authEndpoint, (schema, request) => { + this.server.post(authenticator.buildAuthEndpointURL(), (schema, request) => { const json = JSON.parse(request.requestBody); assert.deepEqual(json, { token_type: 'cookie', @@ -57,7 +57,7 @@ module('Unit | Authenticator | password', function (hooks) { test('it rejects if the endpoint sends an error status code', async function (assert) { assert.expect(1); const authenticator = this.owner.lookup('authenticator:password'); - this.server.post(authenticator.authEndpoint, () => { + this.server.post(authenticator.buildAuthEndpointURL(), () => { return new Response(400); }); await authenticator.authenticate({}).catch(() => { @@ -68,7 +68,7 @@ module('Unit | Authenticator | password', function (hooks) { test('it deauthenticates on invalidation', async function (assert) { assert.expect(1); const authenticator = this.owner.lookup('authenticator:password'); - this.server.post(authenticator.deauthEndpoint, () => { + this.server.post(authenticator.buildDeauthEndpointURL(), () => { assert.ok(true, 'deauthentication occurred'); return new Response(200); }); @@ -78,7 +78,7 @@ module('Unit | Authenticator | password', function (hooks) { test('it invalidation succeeds even if the deauthentication request fails', async function (assert) { assert.expect(2); const authenticator = this.owner.lookup('authenticator:password'); - this.server.post(authenticator.deauthEndpoint, () => { + this.server.post(authenticator.buildDeauthEndpointURL(), () => { assert.ok(true, 'deauthentication was requested'); return new Response(500); }); From dba62d81616b8f50950e9755217ad4960ad13c3c Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Fri, 24 Jul 2020 16:11:26 -0400 Subject: [PATCH 33/70] chore: add initial simple auth integration with updated mocks --- ui/core/app/authenticators/password.js | 47 ++++++++++++++ ui/core/app/session-stores/application.js | 64 +++++++++++++++++++ ui/core/mirage/scenarios/default.js | 10 ++- ui/core/package.json | 2 + .../tests/unit/session-stores/application.js | 53 +++++++++++++++ 5 files changed, 174 insertions(+), 2 deletions(-) create mode 100644 ui/core/app/authenticators/password.js create mode 100644 ui/core/app/session-stores/application.js create mode 100644 ui/core/tests/unit/session-stores/application.js diff --git a/ui/core/app/authenticators/password.js b/ui/core/app/authenticators/password.js new file mode 100644 index 000000000..041d5b9dc --- /dev/null +++ b/ui/core/app/authenticators/password.js @@ -0,0 +1,47 @@ +import BasePasswordAuthenticator from 'auth/authenticators/password'; +import { inject as service } from '@ember/service'; + +/** + * A username/password authenticator that authenticates with + * a scope/auth-method endpoint. Authentication occurs on an auth method + * endpoint, while deauthentication occurs on a scope endpoint. + */ +export default class PasswordAuthenticator extends BasePasswordAuthenticator { + + // =services + + @service store; + + // =attributes + + /** + * Generates an auth method URL with which to authenticate. + * @override + * @param {object} options + * @param {string} options.scope.scope_id + * @param {string} options.authMethod.id + * @return {string} + */ + buildAuthEndpointURL({ + scope: { scope_id: scopeID }, + authMethod: { id: authMethodID } + }) { + const adapter = this.store.adapterFor('application'); + const options = { adapterOptions: { scopeID, method: 'authenticate' } }; + return adapter.buildURL('auth-method', authMethodID, options, 'findRecord'); + } + + /** + * Generates a scope URL with which to deauthenticate. + * @override + * @param {object} options + * @param {string} scopeID + * @return {string} + */ + buildDeauthEndpointURL({ scope: { scope_id: scopeID } }) { + const adapter = this.store.adapterFor('application'); + const options = { adapterOptions: { method: 'deauthenticate' } }; + return adapter.buildURL('scope', scopeID, options, 'findRecord'); + } + +} diff --git a/ui/core/app/session-stores/application.js b/ui/core/app/session-stores/application.js new file mode 100644 index 000000000..555804f12 --- /dev/null +++ b/ui/core/app/session-stores/application.js @@ -0,0 +1,64 @@ +import LocalStorageSessionStore from 'auth/session-stores/local-storage'; +import { inject as service } from '@ember/service'; +import { get } from '@ember/object'; +//import config from '../config/environment'; + +export default class ApplicationSessionStore extends LocalStorageSessionStore { + + @service store; + + /** + * If the auth sessionData includes a token, apply it as a bearer token in the + * application adapter's Authorization header. Otherwise clear the header. + * @param {object} sessionData + * @return {object} + */ + persist(sessionData) { + this.addTokenToAuthorization(sessionData); + this.addScopeToSession(sessionData); + return super.persist(...arguments); + } + + /** + * If the session has a full token, it is added as a bearer token to the + * `Authorization` header for all API requests. + * @param {object} sessionData + */ + addTokenToAuthorization(sessionData) { + const token = get(sessionData, 'authenticated.token'); + const adapter = this.store.adapterFor('application'); + const headers = get(adapter, 'headers'); + if (!headers) adapter.headers = {}; + adapter.headers.Authorization = null; + if (token) adapter.headers.Authorization = `Bearer ${token}`; + } + + /** + * @param {object} sessionData + */ + addScopeToSession(sessionData) { + const scopeID = get(this.scope, 'scope.id'); + const authenticated = get(sessionData, 'authenticated'); + if (scopeID && authenticated) authenticated.scopeID = scopeID; + } + +} + +// import CookieSessionStore from 'auth/session-stores/cookie'; +// import config from '../config/environment'; +// +// export default class ApplicationSessionStore extends CookieSessionStore { +// +// // =attributes +// +// /** +// * @type {string} +// */ +// cookiePath = config.rootURL; +// +// /** +// * @type {string} +// */ +// cookieName = config.auth.passwordCookieName; +// +// } diff --git a/ui/core/mirage/scenarios/default.js b/ui/core/mirage/scenarios/default.js index f143f6ec6..9b498425a 100644 --- a/ui/core/mirage/scenarios/default.js +++ b/ui/core/mirage/scenarios/default.js @@ -2,14 +2,20 @@ export default function(server) { // Scope resources - server.create('scope', null, 'withChildren'); + const globalScope = server.create('scope', { id: 'global' }); // creates a global scope + const orgScope = server.create('scope', { + type: 'org', + scope: { id: globalScope.id, type: globalScope.type } + }, 'withChildren'); + + const scope = { id: orgScope.id, type: orgScope.type }; // Auth // Auth methods exist both at the global scope and the org scope. // For simplicity we don't scope our mock auth methods and just return the // same items for request at all scopes. - server.createList('auth-method', 3); + server.createList('auth-method', 3, { scope }); // User server.createList('user', 5); diff --git a/ui/core/package.json b/ui/core/package.json index f6ae93043..37cb246e5 100644 --- a/ui/core/package.json +++ b/ui/core/package.json @@ -32,6 +32,7 @@ "@glimmer/component": "^1.0.0", "@glimmer/tracking": "^1.0.0", "api": "*", + "auth": "*", "babel-eslint": "^10.0.3", "babel-loader": "^8.1.0", "broccoli-asset-rev": "^3.0.0", @@ -68,6 +69,7 @@ "ember-qunit": "^4.6.0", "ember-resolver": "^7.0.0", "ember-route-action-helper": "^2.0.8", + "ember-simple-auth": "^3.0.0", "ember-source": "~3.16.0", "ember-uuid": "^2.1.0", "ember-welcome-page": "^4.0.0", diff --git a/ui/core/tests/unit/session-stores/application.js b/ui/core/tests/unit/session-stores/application.js new file mode 100644 index 000000000..dd7132709 --- /dev/null +++ b/ui/core/tests/unit/session-stores/application.js @@ -0,0 +1,53 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Session Store | application', function (hooks) { + setupTest(hooks); + + let store; + let applicationAdapter; + let sessionStore; + + hooks.beforeEach(function () { + store = this.owner.lookup('service:store'); + applicationAdapter = store.adapterFor('application'); + sessionStore = this.owner.lookup('session-store:application'); + sessionStore.clear(); + }); + + hooks.afterEach(function () { + sessionStore.clear(); + }); + + test('it does not add an authorization header to application adapter on persist if no token is present', async function (assert) { + assert.expect(1); + sessionStore.persist({ authenticated: { token: null } }); + assert.notOk(applicationAdapter.headers.Authorization); + }); + + test('it adds an authorization header to application adapter on persist', async function (assert) { + assert.expect(1); + sessionStore.persist({ authenticated: { token: 'token1234' } }); + assert.equal(applicationAdapter.headers.Authorization, 'Bearer token1234'); + }); + + test('it adds an authorization header to application adapter which already has headers on persist', async function (assert) { + assert.expect(2); + applicationAdapter.headers = { foo: 'bar' }; + sessionStore.persist({ authenticated: { token: 'token1234' } }); + assert.equal(applicationAdapter.headers.foo, 'bar'); + assert.equal(applicationAdapter.headers.Authorization, 'Bearer token1234'); + }); + + test('it adds an org_id to the session data if authenticating within a scope', async function (assert) { + assert.expect(2); + let sessionData = {}; + sessionStore.set('scope', {}); + sessionStore.persist(sessionData); + assert.notOk(sessionData.authenticated, 'There is no authenticated key'); + sessionData = { authenticated: {} }; + sessionStore.set('scope', { org: { id: 1 } }); + sessionStore.persist(sessionData); + assert.equal(sessionData.authenticated.org_id, 1); + }); +}); From 99b529057060bb470ade2aee007adf373a3e5d73 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Fri, 24 Jul 2020 17:05:55 -0400 Subject: [PATCH 34/70] chore: add core authentication flow with acceptance tests --- ui/core/app/authenticators/password.js | 4 +- ui/core/app/router.js | 3 + ui/core/app/routes/scopes.js | 20 ++++++ ui/core/app/routes/scopes/scope.js | 17 +++-- .../app/routes/scopes/scope/authenticate.js | 26 ++++++++ .../scopes/scope/authenticate/method.js | 63 +++++++++++++++++++ ui/core/app/session-stores/application.js | 4 +- .../templates/scopes/scope/authenticate.hbs | 1 + .../scopes/scope/authenticate/method.hbs | 26 ++++++++ ui/core/mirage/config.js | 4 ++ ui/core/mirage/route-handlers/auth.js | 60 ++++++++++++++++++ .../tests/acceptance/authentication-test.js | 53 ++++++++++++++++ .../routes/scopes/scope/authenticate-test.js | 11 ++++ .../scopes/scope/authenticate/method-test.js | 11 ++++ 14 files changed, 290 insertions(+), 13 deletions(-) create mode 100644 ui/core/app/routes/scopes/scope/authenticate.js create mode 100644 ui/core/app/routes/scopes/scope/authenticate/method.js create mode 100644 ui/core/app/templates/scopes/scope/authenticate.hbs create mode 100644 ui/core/app/templates/scopes/scope/authenticate/method.hbs create mode 100644 ui/core/mirage/route-handlers/auth.js create mode 100644 ui/core/tests/acceptance/authentication-test.js create mode 100644 ui/core/tests/unit/routes/scopes/scope/authenticate-test.js create mode 100644 ui/core/tests/unit/routes/scopes/scope/authenticate/method-test.js diff --git a/ui/core/app/authenticators/password.js b/ui/core/app/authenticators/password.js index 041d5b9dc..3224130a3 100644 --- a/ui/core/app/authenticators/password.js +++ b/ui/core/app/authenticators/password.js @@ -23,7 +23,7 @@ export default class PasswordAuthenticator extends BasePasswordAuthenticator { * @return {string} */ buildAuthEndpointURL({ - scope: { scope_id: scopeID }, + scope: { id: scopeID }, authMethod: { id: authMethodID } }) { const adapter = this.store.adapterFor('application'); @@ -38,7 +38,7 @@ export default class PasswordAuthenticator extends BasePasswordAuthenticator { * @param {string} scopeID * @return {string} */ - buildDeauthEndpointURL({ scope: { scope_id: scopeID } }) { + buildDeauthEndpointURL({ scope: { id: scopeID } }) { const adapter = this.store.adapterFor('application'); const options = { adapterOptions: { method: 'deauthenticate' } }; return adapter.buildURL('scope', scopeID, options, 'findRecord'); diff --git a/ui/core/app/router.js b/ui/core/app/router.js index e07a9ee94..ef3536c60 100644 --- a/ui/core/app/router.js +++ b/ui/core/app/router.js @@ -10,6 +10,9 @@ Router.map(function () { this.route('scopes', function() { this.route('scope', { path: ':scope_id' }, function() { + this.route('authenticate', function() { + this.route('method', { path: ':auth_method_id'}); + }); this.route('projects', function() { this.route('project', { path: ':project_id' }, function() {}); this.route('new'); diff --git a/ui/core/app/routes/scopes.js b/ui/core/app/routes/scopes.js index 5fff74b2e..a849878ba 100644 --- a/ui/core/app/routes/scopes.js +++ b/ui/core/app/routes/scopes.js @@ -1,4 +1,24 @@ import Route from '@ember/routing/route'; +import { A } from '@ember/array'; export default class ScopesRoute extends Route { + + // =methods + + /** + * Attempt to load the specified scope from the API. This is allowed + * to fail, since in some cases the user may not have permission to read a + * scope directly, but may have permission to read resources under it. + * If the scope fails to load, we still proceed using a temporary scope object + * consisting of only the specified ID. + * @param {object} params + * @param {string} params.scope_id + * @return {Promise{ScopeModel}} + */ + model() { + // NOTE: In the absence of a `scope_id` query parameter, this endpoint is + // expected to default to the global scope, thus returning org scopes. + return this.store.findAll('scope').catch(() => A([])); + } + } diff --git a/ui/core/app/routes/scopes/scope.js b/ui/core/app/routes/scopes/scope.js index 8cde487bd..c9ecb358f 100644 --- a/ui/core/app/routes/scopes/scope.js +++ b/ui/core/app/routes/scopes/scope.js @@ -14,15 +14,14 @@ export default class ScopesScopeRoute extends Route { * @param {string} params.scope_id * @return {Promise{ScopeModel}} */ - async model({ scope_id: id }) { - try { - return await this.store.findRecord('scope', id); - } catch { - const maybeExistingScope = this.store.peekRecord('scope', id); - // TODO it's unclear if creating a temporary unsaved scope is reliable... - // we may need to revert to a POJO. - return maybeExistingScope || this.store.createRecord('scope', { id }); - } + model({ scope_id: id }) { + return this.store.findRecord('scope', id) + .catch(() => { + const maybeExistingScope = this.store.peekRecord('scope', id); + // TODO it's unclear if creating a temporary unsaved scope is reliable + // we may need to revert to a POJO. + return maybeExistingScope || this.store.createRecord('scope', { id }); + }); } } diff --git a/ui/core/app/routes/scopes/scope/authenticate.js b/ui/core/app/routes/scopes/scope/authenticate.js new file mode 100644 index 000000000..3ae8edbcf --- /dev/null +++ b/ui/core/app/routes/scopes/scope/authenticate.js @@ -0,0 +1,26 @@ +import Route from '@ember/routing/route'; +import { hash } from 'rsvp'; + +export default class ScopesScopeAuthenticateRoute extends Route { + + /** + * Returns all auth methods for the current scope, along with the current + * scope and all scopes (for org navigation). + * @return {Promise} `{scope, scopes, authMethods}` + */ + model() { + const adapterOptions = { scopeID: this.modelFor('scopes.scope').id }; + return hash({ + scope: this.modelFor('scopes.scope'), + scopes: this.modelFor('scopes'), + authMethods: this.store.findAll('auth-method', { adapterOptions }), + + // for integration testing: + // authMethods: A([{ + // id: 'am_1234567890', + // type: 'password' + // }]) + }); + } + +} diff --git a/ui/core/app/routes/scopes/scope/authenticate/method.js b/ui/core/app/routes/scopes/scope/authenticate/method.js new file mode 100644 index 000000000..c4ce43a11 --- /dev/null +++ b/ui/core/app/routes/scopes/scope/authenticate/method.js @@ -0,0 +1,63 @@ +import Route from '@ember/routing/route'; +import { inject as service } from '@ember/service'; +import { action } from '@ember/object'; + +export default class ScopesScopeAuthenticateMethodRoute extends Route { + + // =services + + @service session; + @service notify; + @service intl; + + // =methods + + /** + * Returns an auth method by id. + * @param {object} params + * @return {Promise{AuthMethodModel}} + */ + model({ auth_method_id: id }) { + const adapterOptions = { scopeID: this.modelFor('scopes.scope').id }; + return this.store.findRecord('auth-method', id, adapterOptions); + } + + /** + * Resets the password field on the controller so it is not + * inadvertantly retained in memory. + */ + resetCredentials() { + this.controller.setProperties({ + password: null, + }); + } + + // =actions + + /** + * Delegates authentication to the `password` authenticator. + * If authentication fails, notifies with an alert. + */ + @action + async authenticate(creds) { + this.resetCredentials(); + const authenticatorName = 'authenticator:password'; + const scope = this.modelFor('scopes.scope'); + const authMethod = this.currentModel; + const authMethodId = authMethod.id; + const requestCookies = false; + try { + await this.session.authenticate( + authenticatorName, + creds, + requestCookies, + { scope, authMethod } + ); + this.transitionTo('scopes.scope.projects'); + } catch (e) { + const errorMessage = this.intl.t('errors.titles.authentication-failed'); + this.notify.error(errorMessage, { closeAfter: null }); + } + } + +} diff --git a/ui/core/app/session-stores/application.js b/ui/core/app/session-stores/application.js index 555804f12..80d0603db 100644 --- a/ui/core/app/session-stores/application.js +++ b/ui/core/app/session-stores/application.js @@ -37,9 +37,9 @@ export default class ApplicationSessionStore extends LocalStorageSessionStore { * @param {object} sessionData */ addScopeToSession(sessionData) { - const scopeID = get(this.scope, 'scope.id'); + const scope = get(this, 'scope'); const authenticated = get(sessionData, 'authenticated'); - if (scopeID && authenticated) authenticated.scopeID = scopeID; + if (scope && authenticated) authenticated.scope = scope; } } diff --git a/ui/core/app/templates/scopes/scope/authenticate.hbs b/ui/core/app/templates/scopes/scope/authenticate.hbs new file mode 100644 index 000000000..e2147cab0 --- /dev/null +++ b/ui/core/app/templates/scopes/scope/authenticate.hbs @@ -0,0 +1 @@ +{{outlet}} \ No newline at end of file diff --git a/ui/core/app/templates/scopes/scope/authenticate/method.hbs b/ui/core/app/templates/scopes/scope/authenticate/method.hbs new file mode 100644 index 000000000..60057d773 --- /dev/null +++ b/ui/core/app/templates/scopes/scope/authenticate/method.hbs @@ -0,0 +1,26 @@ + + + + + + + + + diff --git a/ui/core/mirage/config.js b/ui/core/mirage/config.js index ed6859c83..aa0b6e14a 100644 --- a/ui/core/mirage/config.js +++ b/ui/core/mirage/config.js @@ -1,4 +1,5 @@ import config from '../config/environment'; +import { authHandler } from './route-handlers/auth'; import { Response } from 'miragejs'; export default function() { @@ -55,6 +56,9 @@ export default function() { this.patch('/scopes/:scope_id/auth-methods/:id'); this.del('/scopes/:scope_id/auth-methods/:id'); + // Authenticate/deauthenticate route + this.post('/scopes/:scope_id/auth-methods/:id_method', authHandler); + // IAM : Users this.get('/scopes/:scope_id/users'); this.post('/scopes/:scope_id/users'); diff --git a/ui/core/mirage/route-handlers/auth.js b/ui/core/mirage/route-handlers/auth.js new file mode 100644 index 000000000..8bedb771c --- /dev/null +++ b/ui/core/mirage/route-handlers/auth.js @@ -0,0 +1,60 @@ +// import config from '../../config/environment'; +// import { v1 } from 'ember-uuid'; +import { Response } from 'miragejs'; + +// /** +// * Simulates a cookie-based response to authentication requests. Any user/pass +// * values are allowed *except* when `username` equals `error`, in which case a +// * simulated error response is returned. +// * @param {object} schema +// * @param {object} request +// * @return {Response} +// */ +// export default function authenticateHandler(schema, request) { +// const payload = JSON.parse(request.requestBody); +// if (payload.credentials.name === 'error') { +// return new Response(400); +// } else { +// const cookieName = config.auth.passwordCookieName; +// const cookieValue = v1(); +// const cookiePath = config.rootURL; +// document.cookie = `${cookieName}=${cookieValue};path=${cookiePath}`; +// return new Response(200); +// } +// } + +/** + * Mirage doesn't distinguish between `/path:authenticate` and + * `/path:deauthenticate`, so we use the presence of a request body to indicate + * an authentication request and its absence to indicate deauthentication. + */ +export function authHandler({ scopes }, request) { + const [ , method ] = request.params.id_method.split(':'); + const payload = JSON.parse(request.requestBody); + const isAuthenticationRequest = method === 'authenticate'; + if (isAuthenticationRequest) { + // this is an auth request + if (payload.credentials.name === 'error') { + return new Response(400); + } else { + console.log(request); + const scopeAttrs = + this.serialize(scopes.find(request.params.scope_id)); + const scope = scopeAttrs; + return new Response(200, {}, { + scope, + id: 'token123', + token: 'thetokenstring', + user_id: 'user123', + auth_method_id: 'authmethod123', + created_time: '', + updated_time: '', + last_used_time: '', + expiration_time: '' + }); + } + } else { + // this is a deauth request + return new Response(200); + } +} diff --git a/ui/core/tests/acceptance/authentication-test.js b/ui/core/tests/acceptance/authentication-test.js new file mode 100644 index 000000000..89c9b842b --- /dev/null +++ b/ui/core/tests/acceptance/authentication-test.js @@ -0,0 +1,53 @@ +import { module, test } from 'qunit'; +import { visit, currentURL, fillIn, click, find } from '@ember/test-helpers'; +import { setupApplicationTest } from 'ember-qunit'; +import setupMirage from 'ember-cli-mirage/test-support/setup-mirage'; +import a11yAudit from 'ember-a11y-testing/test-support/audit'; + +module('Acceptance | authentication', function(hooks) { + setupApplicationTest(hooks); + setupMirage(hooks); + + let orgScope; + let orgScopeID; + let authMethod; + let authMethodID; + let authMethodAuthenticateURL; + let projectsURL; + + hooks.beforeEach(function () { + orgScope = this.server.create('scope', {type: 'org'}, 'withChildren'); + const scope = { id: orgScope.id, type: orgScope.type }; + authMethod = server.create('auth-method', { scope }); + orgScopeID = orgScope.id; + authMethodID = authMethod.id; + authMethodAuthenticateURL = `/scopes/${orgScopeID}/authenticate/${authMethodID}`; + projectsURL = `/scopes/${orgScopeID}/projects`; + }); + + test('visiting auth method authenticate route', async function(assert) { + assert.expect(1); + await visit(authMethodAuthenticateURL); + await a11yAudit(); + assert.equal(currentURL(), authMethodAuthenticateURL); + }); + + test('failed authentication shows a notification message', async function(assert) { + assert.expect(1); + await visit(authMethodAuthenticateURL); + await fillIn('[name="identification"]', 'error'); + await click('[type="submit"]'); + assert.ok(find('.rose-notification.is-error')); + }); + + test('successful authentication redirects to projects', async function(assert) { + assert.expect(2); + await visit(authMethodAuthenticateURL); + assert.equal(currentURL(), authMethodAuthenticateURL); + await fillIn('[name="identification"]', 'test'); + await fillIn('[name="password"]', 'test'); + await click('[type="submit"]'); + assert.equal(currentURL(), projectsURL); + }); + +}); diff --git a/ui/core/tests/unit/routes/scopes/scope/authenticate-test.js b/ui/core/tests/unit/routes/scopes/scope/authenticate-test.js new file mode 100644 index 000000000..2ee818453 --- /dev/null +++ b/ui/core/tests/unit/routes/scopes/scope/authenticate-test.js @@ -0,0 +1,11 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Route | scopes/scope/authenticate', function(hooks) { + setupTest(hooks); + + test('it exists', function(assert) { + let route = this.owner.lookup('route:scopes/scope/authenticate'); + assert.ok(route); + }); +}); diff --git a/ui/core/tests/unit/routes/scopes/scope/authenticate/method-test.js b/ui/core/tests/unit/routes/scopes/scope/authenticate/method-test.js new file mode 100644 index 000000000..5aed2aefe --- /dev/null +++ b/ui/core/tests/unit/routes/scopes/scope/authenticate/method-test.js @@ -0,0 +1,11 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Route | scopes/scope/authenticate/method', function(hooks) { + setupTest(hooks); + + test('it exists', function(assert) { + let route = this.owner.lookup('route:scopes/scope/authenticate/method'); + assert.ok(route); + }); +}); From 659a49409206768ffa6411e5fcba7ec4bc289ad0 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Fri, 24 Jul 2020 17:49:42 -0400 Subject: [PATCH 35/70] chore: add core deauthenticate flow --- ui/core/app/controllers/application.js | 10 +++ ui/core/app/routes/application.js | 64 ++++++++++++++++++- .../scopes/scope/authenticate/method.js | 1 - ui/core/app/templates/application.hbs | 28 +++++--- ui/core/mirage/config.js | 5 +- ui/core/mirage/route-handlers/auth.js | 50 ++++++--------- .../tests/acceptance/authentication-test.js | 30 +++++++-- .../unit/controllers/application-test.js | 12 ++++ 8 files changed, 152 insertions(+), 48 deletions(-) create mode 100644 ui/core/app/controllers/application.js create mode 100644 ui/core/tests/unit/controllers/application-test.js diff --git a/ui/core/app/controllers/application.js b/ui/core/app/controllers/application.js new file mode 100644 index 000000000..3fd811d62 --- /dev/null +++ b/ui/core/app/controllers/application.js @@ -0,0 +1,10 @@ +import Controller from '@ember/controller'; +import { inject as service } from '@ember/service'; + +export default class ApplicationController extends Controller { + + // =services + + @service session; + +} diff --git a/ui/core/app/routes/application.js b/ui/core/app/routes/application.js index 6cc5ca574..be17ca092 100644 --- a/ui/core/app/routes/application.js +++ b/ui/core/app/routes/application.js @@ -1,6 +1,68 @@ import Route from '@ember/routing/route'; +import ApplicationRouteMixin from 'ember-simple-auth/mixins/application-route-mixin'; +import Ember from 'ember'; +import { getOwner } from '@ember/application'; +import { inject as service } from '@ember/service'; +import { action } from '@ember/object'; +import { later } from '@ember/runloop'; /** * Entry route for the application. */ -export default class ApplicationRoute extends Route {} +export default class ApplicationRoute extends Route.extend( + ApplicationRouteMixin +) { + + // =services + + @service session; + + // =attributes + + routeIfUnauthenticated = 'index'; + + // =methods + + /** + * After becoming authenticated, does nothing. This overrides the default + * behavior of the ApplicationRouteMixin, which is to redirect after auth. + * We'll handle this redirect manually in sub routes. + * @override + */ + sessionAuthenticated() { + // no op + } + + /** + * When the session ends, redirect to authenticate and reload the page to + * purge any in-memory state. + * + * TODO This application route is redirecting to a subroute, assuming + * that the user is logged into a specific organization. This won't always + * necessarily be the case. When we support MSP, users may authenticate + * without an org scope. So this will need to be rethought. + */ + async sessionInvalidated() { + // Catch error in this transition, since it will be aborted by the + // org auth route when it redirects to the first auth method. + await this.transitionTo(this.routeIfUnauthenticated); + // The Ember way of accessing globals... + const document = getOwner(this).lookup('service:-document').documentElement; + // defaultView === window, but without using globals directly + const { location } = document.parentNode.defaultView; + // Wait a beat, then reload the page... + // This is mostly to give the deauth request a chance to fire. + /* istanbul ignore if */ + if (!Ember.testing) later(location, location.reload, 150); + } + + // =actions + + /** + * Delegates invalidation to the session service. + */ + @action + invalidateSession() { + this.session.invalidate(); + } +} diff --git a/ui/core/app/routes/scopes/scope/authenticate/method.js b/ui/core/app/routes/scopes/scope/authenticate/method.js index c4ce43a11..29014e819 100644 --- a/ui/core/app/routes/scopes/scope/authenticate/method.js +++ b/ui/core/app/routes/scopes/scope/authenticate/method.js @@ -44,7 +44,6 @@ export default class ScopesScopeAuthenticateMethodRoute extends Route { const authenticatorName = 'authenticator:password'; const scope = this.modelFor('scopes.scope'); const authMethod = this.currentModel; - const authMethodId = authMethod.id; const requestCookies = false; try { await this.session.authenticate( diff --git a/ui/core/app/templates/application.hbs b/ui/core/app/templates/application.hbs index fbbecf1e7..0f0b66871 100644 --- a/ui/core/app/templates/application.hbs +++ b/ui/core/app/templates/application.hbs @@ -6,6 +6,8 @@ + + {{!-- {{t 'dev.menu-item'}} @@ -15,16 +17,22 @@ {{t 'dev.section'}} {{t 'dev.section'}} - - - {{t 'dev.menu-item'}} - {{t 'dev.button-menu-item'}} - - + --}} + + {{#if this.session.isAuthenticated}} + + + + {{t 'actions.deauthenticate'}} + + + + {{/if}} + diff --git a/ui/core/mirage/config.js b/ui/core/mirage/config.js index aa0b6e14a..2ce197f7b 100644 --- a/ui/core/mirage/config.js +++ b/ui/core/mirage/config.js @@ -1,5 +1,5 @@ import config from '../config/environment'; -import { authHandler } from './route-handlers/auth'; +import { authHandler, deauthHandler } from './route-handlers/auth'; import { Response } from 'miragejs'; export default function() { @@ -56,8 +56,9 @@ export default function() { this.patch('/scopes/:scope_id/auth-methods/:id'); this.del('/scopes/:scope_id/auth-methods/:id'); - // Authenticate/deauthenticate route + // Authenticate/deauthenticate routes this.post('/scopes/:scope_id/auth-methods/:id_method', authHandler); + this.post('/scopes/:id_method', deauthHandler); // IAM : Users this.get('/scopes/:scope_id/users'); diff --git a/ui/core/mirage/route-handlers/auth.js b/ui/core/mirage/route-handlers/auth.js index 8bedb771c..596500f46 100644 --- a/ui/core/mirage/route-handlers/auth.js +++ b/ui/core/mirage/route-handlers/auth.js @@ -23,38 +23,28 @@ import { Response } from 'miragejs'; // } // } -/** - * Mirage doesn't distinguish between `/path:authenticate` and - * `/path:deauthenticate`, so we use the presence of a request body to indicate - * an authentication request and its absence to indicate deauthentication. - */ export function authHandler({ scopes }, request) { - const [ , method ] = request.params.id_method.split(':'); const payload = JSON.parse(request.requestBody); - const isAuthenticationRequest = method === 'authenticate'; - if (isAuthenticationRequest) { - // this is an auth request - if (payload.credentials.name === 'error') { - return new Response(400); - } else { - console.log(request); - const scopeAttrs = - this.serialize(scopes.find(request.params.scope_id)); - const scope = scopeAttrs; - return new Response(200, {}, { - scope, - id: 'token123', - token: 'thetokenstring', - user_id: 'user123', - auth_method_id: 'authmethod123', - created_time: '', - updated_time: '', - last_used_time: '', - expiration_time: '' - }); - } + if (payload.credentials.name === 'error') { + return new Response(400); } else { - // this is a deauth request - return new Response(200); + const scopeAttrs = + this.serialize(scopes.find(request.params.scope_id)); + const scope = scopeAttrs; + return new Response(200, {}, { + scope, + id: 'token123', + token: 'thetokenstring', + user_id: 'user123', + auth_method_id: 'authmethod123', + created_time: '', + updated_time: '', + last_used_time: '', + expiration_time: '' + }); } } + +export function deauthHandler() { + return new Response(200); +} diff --git a/ui/core/tests/acceptance/authentication-test.js b/ui/core/tests/acceptance/authentication-test.js index 89c9b842b..bd5c2a1a5 100644 --- a/ui/core/tests/acceptance/authentication-test.js +++ b/ui/core/tests/acceptance/authentication-test.js @@ -3,6 +3,11 @@ import { visit, currentURL, fillIn, click, find } from '@ember/test-helpers'; import { setupApplicationTest } from 'ember-qunit'; import setupMirage from 'ember-cli-mirage/test-support/setup-mirage'; import a11yAudit from 'ember-a11y-testing/test-support/audit'; +import { + currentSession, + //authenticateSession, + //invalidateSession, +} from 'ember-simple-auth/test-support'; module('Acceptance | authentication', function(hooks) { setupApplicationTest(hooks); @@ -10,6 +15,7 @@ module('Acceptance | authentication', function(hooks) { let orgScope; let orgScopeID; + let scope; let authMethod; let authMethodID; let authMethodAuthenticateURL; @@ -17,8 +23,8 @@ module('Acceptance | authentication', function(hooks) { hooks.beforeEach(function () { orgScope = this.server.create('scope', {type: 'org'}, 'withChildren'); - const scope = { id: orgScope.id, type: orgScope.type }; - authMethod = server.create('auth-method', { scope }); + scope = { id: orgScope.id, type: orgScope.type }; + authMethod = this.server.create('auth-method', { scope }); orgScopeID = orgScope.id; authMethodID = authMethod.id; authMethodAuthenticateURL = `/scopes/${orgScopeID}/authenticate/${authMethodID}`; @@ -33,21 +39,37 @@ module('Acceptance | authentication', function(hooks) { }); test('failed authentication shows a notification message', async function(assert) { - assert.expect(1); + assert.expect(3); await visit(authMethodAuthenticateURL); + assert.notOk(currentSession().isAuthenticated); await fillIn('[name="identification"]', 'error'); await click('[type="submit"]'); assert.ok(find('.rose-notification.is-error')); + assert.notOk(currentSession().isAuthenticated); }); test('successful authentication redirects to projects', async function(assert) { - assert.expect(2); + assert.expect(4); await visit(authMethodAuthenticateURL); + assert.notOk(currentSession().isAuthenticated); assert.equal(currentURL(), authMethodAuthenticateURL); await fillIn('[name="identification"]', 'test'); await fillIn('[name="password"]', 'test'); await click('[type="submit"]'); assert.equal(currentURL(), projectsURL); + assert.ok(currentSession().isAuthenticated); + }); + + test('deauthentication redirects to index', async function(assert) { + assert.expect(3); + await visit(authMethodAuthenticateURL); + await fillIn('[name="identification"]', 'test'); + await fillIn('[name="password"]', 'test'); + await click('[type="submit"]'); + assert.ok(currentSession().isAuthenticated); + await click('.rose-button-header-dropdown'); + assert.notOk(currentSession().isAuthenticated); + assert.equal(currentURL(), '/'); }); }); diff --git a/ui/core/tests/unit/controllers/application-test.js b/ui/core/tests/unit/controllers/application-test.js new file mode 100644 index 000000000..93d19cebe --- /dev/null +++ b/ui/core/tests/unit/controllers/application-test.js @@ -0,0 +1,12 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Controller | application', function(hooks) { + setupTest(hooks); + + // Replace this with your real tests. + test('it exists', function(assert) { + let controller = this.owner.lookup('controller:application'); + assert.ok(controller); + }); +}); From 6c1375fe16bf5908cef4d8c8a67181d699045d6c Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Fri, 24 Jul 2020 18:08:37 -0400 Subject: [PATCH 36/70] chore: setup auth redirection logic --- .../app/routes/scopes/scope/authenticate.js | 17 ++++++++++++++++ .../routes/scopes/scope/authenticate/index.js | 20 +++++++++++++++++++ .../scopes/scope/authenticate/method.js | 6 +++++- .../scopes/scope/authenticate/index.hbs | 1 + .../tests/acceptance/authentication-test.js | 20 ++++++++++++++++++- .../scopes/scope/authenticate/index-test.js | 11 ++++++++++ 6 files changed, 73 insertions(+), 2 deletions(-) create mode 100644 ui/core/app/routes/scopes/scope/authenticate/index.js create mode 100644 ui/core/app/templates/scopes/scope/authenticate/index.hbs create mode 100644 ui/core/tests/unit/routes/scopes/scope/authenticate/index-test.js diff --git a/ui/core/app/routes/scopes/scope/authenticate.js b/ui/core/app/routes/scopes/scope/authenticate.js index 3ae8edbcf..42d238ffd 100644 --- a/ui/core/app/routes/scopes/scope/authenticate.js +++ b/ui/core/app/routes/scopes/scope/authenticate.js @@ -1,8 +1,19 @@ import Route from '@ember/routing/route'; +import { inject as service } from '@ember/service'; import { hash } from 'rsvp'; export default class ScopesScopeAuthenticateRoute extends Route { + // =services + + @service session; + + // =attributes + + routeIfAlreadyAuthenticated = 'scopes.scope.projects'; + + // =methods + /** * Returns all auth methods for the current scope, along with the current * scope and all scopes (for org navigation). @@ -23,4 +34,10 @@ export default class ScopesScopeAuthenticateRoute extends Route { }); } + redirect() { + if (this.session.isAuthenticated) { + this.transitionTo(this.routeIfAlreadyAuthenticated); + } + } + } diff --git a/ui/core/app/routes/scopes/scope/authenticate/index.js b/ui/core/app/routes/scopes/scope/authenticate/index.js new file mode 100644 index 000000000..5ee523b46 --- /dev/null +++ b/ui/core/app/routes/scopes/scope/authenticate/index.js @@ -0,0 +1,20 @@ +import Route from '@ember/routing/route'; + +export default class ScopesScopeAuthenticateIndexRoute extends Route { + + // =methods + + /** + * This index route exists solely to redirect the user on to the + * first auth method. + * @param {object} model + * @param {[AuthMethodModel]} model.authMethods + */ + redirect({ authMethods }) { + const firstAuthMethod = authMethods.firstObject; + if (firstAuthMethod) { + this.transitionTo('scopes.scope.authenticate.method', firstAuthMethod); + } + } + +} diff --git a/ui/core/app/routes/scopes/scope/authenticate/method.js b/ui/core/app/routes/scopes/scope/authenticate/method.js index 29014e819..b52406722 100644 --- a/ui/core/app/routes/scopes/scope/authenticate/method.js +++ b/ui/core/app/routes/scopes/scope/authenticate/method.js @@ -10,6 +10,10 @@ export default class ScopesScopeAuthenticateMethodRoute extends Route { @service notify; @service intl; + // =attributes + + routeAfterAuthentication = 'scopes.scope.projects'; + // =methods /** @@ -52,7 +56,7 @@ export default class ScopesScopeAuthenticateMethodRoute extends Route { requestCookies, { scope, authMethod } ); - this.transitionTo('scopes.scope.projects'); + this.transitionTo(this.routeAfterAuthentication); } catch (e) { const errorMessage = this.intl.t('errors.titles.authentication-failed'); this.notify.error(errorMessage, { closeAfter: null }); diff --git a/ui/core/app/templates/scopes/scope/authenticate/index.hbs b/ui/core/app/templates/scopes/scope/authenticate/index.hbs new file mode 100644 index 000000000..e2147cab0 --- /dev/null +++ b/ui/core/app/templates/scopes/scope/authenticate/index.hbs @@ -0,0 +1 @@ +{{outlet}} \ No newline at end of file diff --git a/ui/core/tests/acceptance/authentication-test.js b/ui/core/tests/acceptance/authentication-test.js index bd5c2a1a5..e71c1a3b0 100644 --- a/ui/core/tests/acceptance/authentication-test.js +++ b/ui/core/tests/acceptance/authentication-test.js @@ -5,7 +5,7 @@ import setupMirage from 'ember-cli-mirage/test-support/setup-mirage'; import a11yAudit from 'ember-a11y-testing/test-support/audit'; import { currentSession, - //authenticateSession, + authenticateSession, //invalidateSession, } from 'ember-simple-auth/test-support'; @@ -18,6 +18,7 @@ module('Acceptance | authentication', function(hooks) { let scope; let authMethod; let authMethodID; + let authenticateURL; let authMethodAuthenticateURL; let projectsURL; @@ -27,10 +28,18 @@ module('Acceptance | authentication', function(hooks) { authMethod = this.server.create('auth-method', { scope }); orgScopeID = orgScope.id; authMethodID = authMethod.id; + authenticateURL = `/scopes/${orgScopeID}/authenticate`; authMethodAuthenticateURL = `/scopes/${orgScopeID}/authenticate/${authMethodID}`; projectsURL = `/scopes/${orgScopeID}/projects`; }); + test('visiting auth methods authenticate route redirects to first auth method', async function(assert) { + assert.expect(1); + await visit(authenticateURL); + await a11yAudit(); + assert.equal(currentURL(), authMethodAuthenticateURL); + }); + test('visiting auth method authenticate route', async function(assert) { assert.expect(1); await visit(authMethodAuthenticateURL); @@ -38,6 +47,15 @@ module('Acceptance | authentication', function(hooks) { assert.equal(currentURL(), authMethodAuthenticateURL); }); + test('visiting any authenticate route while already authenticated redirects to projects', async function(assert) { + assert.expect(2); + authenticateSession(); + await visit(authenticateURL); + assert.equal(currentURL(), projectsURL); + await visit(authMethodAuthenticateURL); + assert.equal(currentURL(), projectsURL); + }); + test('failed authentication shows a notification message', async function(assert) { assert.expect(3); await visit(authMethodAuthenticateURL); diff --git a/ui/core/tests/unit/routes/scopes/scope/authenticate/index-test.js b/ui/core/tests/unit/routes/scopes/scope/authenticate/index-test.js new file mode 100644 index 000000000..88d5a1121 --- /dev/null +++ b/ui/core/tests/unit/routes/scopes/scope/authenticate/index-test.js @@ -0,0 +1,11 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Route | scopes/scope/authenticate/index', function(hooks) { + setupTest(hooks); + + test('it exists', function(assert) { + let route = this.owner.lookup('route:scopes/scope/authenticate/index'); + assert.ok(route); + }); +}); From 4b1bb3a50881c755389376a4f66e244d8e4f95cf Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Fri, 24 Jul 2020 18:32:39 -0400 Subject: [PATCH 37/70] docs: add doc comment --- ui/core/app/routes/scopes/scope/authenticate.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ui/core/app/routes/scopes/scope/authenticate.js b/ui/core/app/routes/scopes/scope/authenticate.js index 42d238ffd..93f565baa 100644 --- a/ui/core/app/routes/scopes/scope/authenticate.js +++ b/ui/core/app/routes/scopes/scope/authenticate.js @@ -10,6 +10,8 @@ export default class ScopesScopeAuthenticateRoute extends Route { // =attributes + // TODO: this assumes we're in an org scope, but the "landing" + // for global scope is probably different. routeIfAlreadyAuthenticated = 'scopes.scope.projects'; // =methods From bbbe3e2819256d6534447c23f3d42cbfcf902c57 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Fri, 24 Jul 2020 19:12:20 -0400 Subject: [PATCH 38/70] chore: setup remaining auth redirect logic --- ui/core/app/router.js | 1 + ui/core/app/routes/application.js | 9 +--- ui/core/app/routes/index.js | 14 ++++++ ui/core/app/routes/scopes/index.js | 29 +++++++++++ ui/core/app/routes/scopes/scope/index.js | 14 ++++++ ui/core/app/routes/scopes/scope/projects.js | 8 +++ ui/core/app/templates/scopes/index.hbs | 1 + ui/core/app/templates/scopes/scope/index.hbs | 1 + ui/core/mirage/config.js | 17 ++----- .../tests/acceptance/authentication-test.js | 49 ++++++++++++++++--- ui/core/tests/unit/routes/index-test.js | 11 +++++ .../tests/unit/routes/scopes/index-test.js | 11 +++++ .../unit/routes/scopes/scope/index-test.js | 11 +++++ 13 files changed, 149 insertions(+), 27 deletions(-) create mode 100644 ui/core/app/routes/index.js create mode 100644 ui/core/app/routes/scopes/index.js create mode 100644 ui/core/app/routes/scopes/scope/index.js create mode 100644 ui/core/app/templates/scopes/index.hbs create mode 100644 ui/core/app/templates/scopes/scope/index.hbs create mode 100644 ui/core/tests/unit/routes/index-test.js create mode 100644 ui/core/tests/unit/routes/scopes/index-test.js create mode 100644 ui/core/tests/unit/routes/scopes/scope/index-test.js diff --git a/ui/core/app/router.js b/ui/core/app/router.js index ef3536c60..3c95ac355 100644 --- a/ui/core/app/router.js +++ b/ui/core/app/router.js @@ -18,6 +18,7 @@ Router.map(function () { this.route('new'); }); }); + this.route('cope'); }); this.route('orgs', function () { diff --git a/ui/core/app/routes/application.js b/ui/core/app/routes/application.js index be17ca092..024a0b887 100644 --- a/ui/core/app/routes/application.js +++ b/ui/core/app/routes/application.js @@ -36,16 +36,11 @@ export default class ApplicationRoute extends Route.extend( /** * When the session ends, redirect to authenticate and reload the page to * purge any in-memory state. - * - * TODO This application route is redirecting to a subroute, assuming - * that the user is logged into a specific organization. This won't always - * necessarily be the case. When we support MSP, users may authenticate - * without an org scope. So this will need to be rethought. */ async sessionInvalidated() { // Catch error in this transition, since it will be aborted by the - // org auth route when it redirects to the first auth method. - await this.transitionTo(this.routeIfUnauthenticated); + // scope auth route when it redirects to the first auth method. + await this.transitionTo(this.routeIfUnauthenticated).catch(() => {}); // The Ember way of accessing globals... const document = getOwner(this).lookup('service:-document').documentElement; // defaultView === window, but without using globals directly diff --git a/ui/core/app/routes/index.js b/ui/core/app/routes/index.js new file mode 100644 index 000000000..4ec1a62b0 --- /dev/null +++ b/ui/core/app/routes/index.js @@ -0,0 +1,14 @@ +import Route from '@ember/routing/route'; + +export default class IndexRoute extends Route { + + // =methods + + /** + * Redirects to scopes route for further processing. + */ + redirect() { + this.transitionTo('scopes'); + } + +} diff --git a/ui/core/app/routes/scopes/index.js b/ui/core/app/routes/scopes/index.js new file mode 100644 index 000000000..06a263171 --- /dev/null +++ b/ui/core/app/routes/scopes/index.js @@ -0,0 +1,29 @@ +import Route from '@ember/routing/route'; +import { inject as service } from '@ember/service'; +import { get } from '@ember/object'; + +export default class ScopesIndexRoute extends Route { + + // =services + + @service session; + + // =methods + + /** + * If authenticated, redirects to the scope route of the authenticted scope. + * If unauthenticated, redirects to the first scope that was loaded (if any). + * @param {[ScopeModel]} model + * @param {?ScopeModel} model.firstObject + */ + redirect({ firstObject }) { + const authenticatedScopeID = + get(this.session, 'data.authenticated.scope.id'); + if (authenticatedScopeID) { + this.transitionTo('scopes.scope', authenticatedScopeID); + } else if (firstObject) { + this.transitionTo('scopes.scope', firstObject.id); + } + } + +} diff --git a/ui/core/app/routes/scopes/scope/index.js b/ui/core/app/routes/scopes/scope/index.js new file mode 100644 index 000000000..0215ea9da --- /dev/null +++ b/ui/core/app/routes/scopes/scope/index.js @@ -0,0 +1,14 @@ +import Route from '@ember/routing/route'; + +export default class ScopesScopeIndexRoute extends Route { + + // =methods + + /** + * Redirects to scopes/scope/authenticate for further processing. + */ + redirect() { + this.transitionTo('scopes.scope.authenticate'); + } + +} diff --git a/ui/core/app/routes/scopes/scope/projects.js b/ui/core/app/routes/scopes/scope/projects.js index ebc5e97e5..a7ab28af6 100644 --- a/ui/core/app/routes/scopes/scope/projects.js +++ b/ui/core/app/routes/scopes/scope/projects.js @@ -7,9 +7,17 @@ export default class ScopesScopeProjectsRoute extends Route { // =services @service notify; + @service session; // =methods + /** + * If arriving here unauthenticated, redirect to index for further processing. + */ + beforeModel() { + if (!this.session.isAuthenticated) this.transitionTo('index'); + } + /** * Loads all scopes under the current scope. * @return {Promise{[ScopeModel]}} diff --git a/ui/core/app/templates/scopes/index.hbs b/ui/core/app/templates/scopes/index.hbs new file mode 100644 index 000000000..e2147cab0 --- /dev/null +++ b/ui/core/app/templates/scopes/index.hbs @@ -0,0 +1 @@ +{{outlet}} \ No newline at end of file diff --git a/ui/core/app/templates/scopes/scope/index.hbs b/ui/core/app/templates/scopes/scope/index.hbs new file mode 100644 index 000000000..e2147cab0 --- /dev/null +++ b/ui/core/app/templates/scopes/scope/index.hbs @@ -0,0 +1 @@ +{{outlet}} \ No newline at end of file diff --git a/ui/core/mirage/config.js b/ui/core/mirage/config.js index 2ce197f7b..fec96c052 100644 --- a/ui/core/mirage/config.js +++ b/ui/core/mirage/config.js @@ -1,6 +1,6 @@ import config from '../config/environment'; import { authHandler, deauthHandler } from './route-handlers/auth'; -import { Response } from 'miragejs'; +//import { Response } from 'miragejs'; export default function() { @@ -21,9 +21,10 @@ export default function() { this.get('/scopes', ({ scopes }, { queryParams: { scope_id } }) => { // Default parent scope is global if (!scope_id) scope_id = 'global'; - return scopes.where(scope => { + const results = scopes.where(scope => { return scope.scope ? scope.scope.id === scope_id : false }); + return results; }); this.post('/scopes', function ({ scopes }, request) { // Parent scope comes through the payload via `scope_id`, but this needs @@ -34,17 +35,7 @@ export default function() { attrs.scope = parentScopeAttrs; return scopes.create(attrs); }); - // To simulate a possible real-world case, org scopes are not returned, - // but project scopes are. - // TODO this should be expanded to support other scenarios. - this.get('/scopes/:id', ({ scopes }, { params: { id: scopeID } }) => { - const scope = scopes.find(scopeID); - if (scope.type !== 'project') { - return new Response(404, {}, {}); - } else { - return scope; - } - }); + this.get('/scopes/:id'); this.patch('/scopes/:id'); this.del('/scopes/:id'); diff --git a/ui/core/tests/acceptance/authentication-test.js b/ui/core/tests/acceptance/authentication-test.js index e71c1a3b0..f6fa1c8ab 100644 --- a/ui/core/tests/acceptance/authentication-test.js +++ b/ui/core/tests/acceptance/authentication-test.js @@ -6,15 +6,19 @@ import a11yAudit from 'ember-a11y-testing/test-support/audit'; import { currentSession, authenticateSession, - //invalidateSession, + invalidateSession, } from 'ember-simple-auth/test-support'; module('Acceptance | authentication', function(hooks) { setupApplicationTest(hooks); setupMirage(hooks); + let indexURL; + let globalScope; let orgScope; let orgScopeID; + let scopesURL; + let orgScopeURL; let scope; let authMethod; let authMethodID; @@ -23,11 +27,19 @@ module('Acceptance | authentication', function(hooks) { let projectsURL; hooks.beforeEach(function () { - orgScope = this.server.create('scope', {type: 'org'}, 'withChildren'); + invalidateSession(); + indexURL = '/'; + globalScope = this.server.create('scope', { id: 'global' }); + orgScope = this.server.create('scope', { + type: 'org', + scope: { id: globalScope.id, type: globalScope.type } + }, 'withChildren'); scope = { id: orgScope.id, type: orgScope.type }; authMethod = this.server.create('auth-method', { scope }); orgScopeID = orgScope.id; authMethodID = authMethod.id; + scopesURL = `/scopes`; + orgScopeURL = `/scopes/${orgScopeID}`; authenticateURL = `/scopes/${orgScopeID}/authenticate`; authMethodAuthenticateURL = `/scopes/${orgScopeID}/authenticate/${authMethodID}`; projectsURL = `/scopes/${orgScopeID}/projects`; @@ -40,6 +52,22 @@ module('Acceptance | authentication', function(hooks) { assert.equal(currentURL(), authMethodAuthenticateURL); }); + test('visiting any authentication parent route while unauthenticated redirects to first authenticate method', async function(assert) { + assert.expect(3); + await visit(indexURL); + assert.equal(currentURL(), authMethodAuthenticateURL); + await visit(scopesURL); + assert.equal(currentURL(), authMethodAuthenticateURL); + assert.notOk(currentSession().isAuthenticated); + }); + + test('visiting projects while unauthenticated redirects to first authenticate method', async function(assert) { + assert.expect(2); + await visit(projectsURL); + assert.equal(currentURL(), authMethodAuthenticateURL); + assert.notOk(currentSession().isAuthenticated); + }); + test('visiting auth method authenticate route', async function(assert) { assert.expect(1); await visit(authMethodAuthenticateURL); @@ -47,13 +75,20 @@ module('Acceptance | authentication', function(hooks) { assert.equal(currentURL(), authMethodAuthenticateURL); }); - test('visiting any authenticate route while already authenticated redirects to projects', async function(assert) { - assert.expect(2); - authenticateSession(); + test('visiting any authentication parent route while already authenticated redirects to projects', async function(assert) { + assert.expect(6); + authenticateSession({ scope }); + await visit(indexURL); + assert.equal(currentURL(), projectsURL); + await visit(scopesURL); + assert.equal(currentURL(), projectsURL); + await visit(orgScopeURL); + assert.equal(currentURL(), projectsURL); await visit(authenticateURL); assert.equal(currentURL(), projectsURL); await visit(authMethodAuthenticateURL); assert.equal(currentURL(), projectsURL); + assert.ok(currentSession().isAuthenticated); }); test('failed authentication shows a notification message', async function(assert) { @@ -78,7 +113,7 @@ module('Acceptance | authentication', function(hooks) { assert.ok(currentSession().isAuthenticated); }); - test('deauthentication redirects to index', async function(assert) { + test('deauthentication redirects to first authenticate method', async function(assert) { assert.expect(3); await visit(authMethodAuthenticateURL); await fillIn('[name="identification"]', 'test'); @@ -87,7 +122,7 @@ module('Acceptance | authentication', function(hooks) { assert.ok(currentSession().isAuthenticated); await click('.rose-button-header-dropdown'); assert.notOk(currentSession().isAuthenticated); - assert.equal(currentURL(), '/'); + assert.equal(currentURL(), authMethodAuthenticateURL); }); }); diff --git a/ui/core/tests/unit/routes/index-test.js b/ui/core/tests/unit/routes/index-test.js new file mode 100644 index 000000000..eaaac17bc --- /dev/null +++ b/ui/core/tests/unit/routes/index-test.js @@ -0,0 +1,11 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Route | index', function(hooks) { + setupTest(hooks); + + test('it exists', function(assert) { + let route = this.owner.lookup('route:index'); + assert.ok(route); + }); +}); diff --git a/ui/core/tests/unit/routes/scopes/index-test.js b/ui/core/tests/unit/routes/scopes/index-test.js new file mode 100644 index 000000000..c2fb98357 --- /dev/null +++ b/ui/core/tests/unit/routes/scopes/index-test.js @@ -0,0 +1,11 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Route | scopes/index', function(hooks) { + setupTest(hooks); + + test('it exists', function(assert) { + let route = this.owner.lookup('route:scopes/index'); + assert.ok(route); + }); +}); diff --git a/ui/core/tests/unit/routes/scopes/scope/index-test.js b/ui/core/tests/unit/routes/scopes/scope/index-test.js new file mode 100644 index 000000000..ba0a5bdba --- /dev/null +++ b/ui/core/tests/unit/routes/scopes/scope/index-test.js @@ -0,0 +1,11 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Route | scopes/scope/index', function(hooks) { + setupTest(hooks); + + test('it exists', function(assert) { + let route = this.owner.lookup('route:scopes/scope/index'); + assert.ok(route); + }); +}); From 1e57d1a896a1304525ee3a33bf0e4695976d45d0 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Mon, 27 Jul 2020 10:16:34 -0400 Subject: [PATCH 39/70] ci: remove connect-client from test step for now --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e455ef34b..f07bec9d6 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "lint:addons:rose": "echo \"\n\nlint: addons/rose\n\" && cd addons/rose && yarn lint", "lint:ui:core": "echo \"\n\nlint: ui/core\n\" && cd ui/core && yarn lint", "lint:ui:connect-client": "echo \"\n\nlint: ui/connect-client\n\" && cd ui/connect-client && yarn lint", - "test": "yarn test:addons:api && yarn test:addons:auth && yarn test:addons:rose && yarn test:ui:core && yarn test:ui:connect-client", + "test": "yarn test:addons:api && yarn test:addons:auth && yarn test:addons:rose && yarn test:ui:core", "test:addons:api": "echo \"\n\ntest: addons/api\n\" && cd addons/api && yarn test", "test:addons:auth": "echo \"\n\ntest: addons/auth\n\" && cd addons/auth && yarn test", "test:addons:rose": "echo \"\n\ntest: addons/rose\n\" && cd addons/rose && yarn test", From 1a836b0210e1bcafd22ba1ad0097a5ee1fe069c9 Mon Sep 17 00:00:00 2001 From: Susmitha Girumala Date: Mon, 27 Jul 2020 14:12:19 -0400 Subject: [PATCH 40/70] =?UTF-8?q?chore:=20=F0=9F=A4=96=20Refactor=20user?= =?UTF-8?q?=20CRUD+L=20to=20/scopes/:scope=5Fid?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/controllers/orgs/org/groups/group.js | 1 - .../app/controllers/orgs/org/groups/new.js | 1 - ui/core/app/controllers/orgs/org/projects.js | 2 - .../app/controllers/orgs/org/projects/new.js | 2 - .../controllers/orgs/org/projects/project.js | 2 - .../org/projects/project/host-catalogs.js | 2 - .../project/host-catalogs/host-catalog.js | 2 - .../org/projects/project/host-catalogs/new.js | 2 - ui/core/app/controllers/orgs/org/roles.js | 1 - ui/core/app/controllers/orgs/org/roles/new.js | 1 - .../app/controllers/orgs/org/roles/role.js | 1 - ui/core/app/controllers/orgs/org/users.js | 1 - .../app/controllers/scopes/scope/projects.js | 2 - .../controllers/scopes/scope/projects/new.js | 2 - .../scopes/scope/projects/project.js | 2 - ui/core/app/router.js | 35 +++--- ui/core/app/routes/orgs.js | 2 - .../app/routes/orgs/org/groups/group/index.js | 3 +- ui/core/app/routes/orgs/org/projects/index.js | 2 +- .../app/routes/orgs/org/projects/project.js | 2 +- .../org/projects/project/host-catalogs.js | 1 - .../project/host-catalogs/host-catalog.js | 2 - .../host-catalogs/host-catalog/index.js | 3 +- .../projects/project/host-catalogs/index.js | 3 +- .../org/projects/project/host-catalogs/new.js | 5 +- .../routes/orgs/org/projects/project/index.js | 3 +- ui/core/app/routes/orgs/org/roles.js | 1 - ui/core/app/routes/orgs/org/users/index.js | 3 +- ui/core/app/routes/scopes.js | 3 +- ui/core/app/routes/scopes/scope.js | 2 - ui/core/app/routes/scopes/scope/projects.js | 2 - .../app/routes/scopes/scope/projects/index.js | 3 +- .../app/routes/scopes/scope/projects/new.js | 4 +- .../routes/scopes/scope/projects/project.js | 2 - .../scopes/scope/projects/project/index.js | 3 +- ui/core/app/routes/scopes/scope/users.js | 73 +++++++++++ .../app/routes/scopes/scope/users/index.js | 3 + ui/core/app/routes/scopes/scope/users/new.js | 13 ++ ui/core/app/routes/scopes/scope/users/user.js | 17 +++ .../routes/scopes/scope/users/user/index.js | 3 + ui/core/app/templates/scopes/scope.hbs | 8 +- ui/core/app/templates/scopes/scope/users.hbs | 3 + .../templates/scopes/scope/users/index.hbs | 63 ++++++++++ .../app/templates/scopes/scope/users/new.hbs | 21 ++++ .../app/templates/scopes/scope/users/user.hbs | 3 + .../scopes/scope/users/user/index.hbs | 35 ++++++ ui/core/tests/acceptance/bread-crumbs-test.js | 117 ++++++++++++++---- ui/core/tests/acceptance/groups-test.js | 9 +- .../tests/acceptance/host-catalogs-test.js | 106 ++++++++-------- ui/core/tests/acceptance/projects-test.js | 16 ++- ui/core/tests/acceptance/roles-test.js | 6 +- ui/core/tests/acceptance/users-test.js | 8 +- .../components/bread-crumbs-test.js | 42 ++++--- .../components/form/host-catalog-test.js | 4 +- .../unit/controllers/orgs/org/groups-test.js | 4 +- .../controllers/orgs/org/groups/group-test.js | 4 +- .../controllers/orgs/org/groups/new-test.js | 4 +- .../controllers/orgs/org/projects-test.js | 4 +- .../controllers/orgs/org/projects/new-test.js | 4 +- .../orgs/org/projects/project-test.js | 4 +- .../projects/project/host-catalogs-test.js | 10 +- .../host-catalogs/host-catalog-test.js | 21 ++-- .../project/host-catalogs/new-test.js | 21 ++-- .../unit/controllers/orgs/org/roles-test.js | 4 +- .../controllers/orgs/org/roles/new-test.js | 4 +- .../controllers/orgs/org/roles/role-test.js | 4 +- .../unit/controllers/orgs/org/users-test.js | 4 +- .../controllers/orgs/org/users/new-test.js | 4 +- .../controllers/orgs/org/users/user-test.js | 4 +- .../controllers/scopes/scope/projects-test.js | 4 +- .../scopes/scope/projects/new-test.js | 4 +- .../scopes/scope/projects/project-test.js | 8 +- .../tests/unit/routes/orgs/org/groups-test.js | 4 +- .../unit/routes/orgs/org/groups/group-test.js | 4 +- .../orgs/org/groups/group/index-test.js | 4 +- .../unit/routes/orgs/org/groups/index-test.js | 4 +- .../unit/routes/orgs/org/groups/new-test.js | 4 +- .../projects/project/host-catalogs-test.js | 10 +- .../host-catalogs/host-catalog-test.js | 19 +-- .../host-catalogs/host-catalog/index-test.js | 19 +-- .../project/host-catalogs/index-test.js | 19 +-- .../project/host-catalogs/new-test.js | 10 +- .../orgs/org/projects/project/index-test.js | 4 +- .../tests/unit/routes/orgs/org/roles-test.js | 4 +- .../unit/routes/orgs/org/roles/index-test.js | 4 +- .../unit/routes/orgs/org/roles/new-test.js | 4 +- .../unit/routes/orgs/org/roles/role-test.js | 4 +- .../tests/unit/routes/orgs/org/users-test.js | 4 +- .../unit/routes/orgs/org/users/index-test.js | 4 +- .../unit/routes/orgs/org/users/new-test.js | 4 +- .../unit/routes/orgs/org/users/user-test.js | 4 +- ui/core/tests/unit/routes/scopes-test.js | 4 +- .../tests/unit/routes/scopes/scope-test.js | 4 +- .../unit/routes/scopes/scope/projects-test.js | 4 +- .../scopes/scope/projects/index-test.js | 4 +- .../routes/scopes/scope/projects/new-test.js | 4 +- .../scopes/scope/projects/project-test.js | 4 +- .../scope/projects/project/index-test.js | 4 +- .../unit/routes/scopes/scope/users-test.js | 11 ++ .../routes/scopes/scope/users/index-test.js | 11 ++ .../routes/scopes/scope/users/new-test.js | 11 ++ .../routes/scopes/scope/users/user-test.js | 11 ++ .../scopes/scope/users/user/index-test.js | 11 ++ 103 files changed, 687 insertions(+), 303 deletions(-) create mode 100644 ui/core/app/routes/scopes/scope/users.js create mode 100644 ui/core/app/routes/scopes/scope/users/index.js create mode 100644 ui/core/app/routes/scopes/scope/users/new.js create mode 100644 ui/core/app/routes/scopes/scope/users/user.js create mode 100644 ui/core/app/routes/scopes/scope/users/user/index.js create mode 100644 ui/core/app/templates/scopes/scope/users.hbs create mode 100644 ui/core/app/templates/scopes/scope/users/index.hbs create mode 100644 ui/core/app/templates/scopes/scope/users/new.hbs create mode 100644 ui/core/app/templates/scopes/scope/users/user.hbs create mode 100644 ui/core/app/templates/scopes/scope/users/user/index.hbs create mode 100644 ui/core/tests/unit/routes/scopes/scope/users-test.js create mode 100644 ui/core/tests/unit/routes/scopes/scope/users/index-test.js create mode 100644 ui/core/tests/unit/routes/scopes/scope/users/new-test.js create mode 100644 ui/core/tests/unit/routes/scopes/scope/users/user-test.js create mode 100644 ui/core/tests/unit/routes/scopes/scope/users/user/index-test.js diff --git a/ui/core/app/controllers/orgs/org/groups/group.js b/ui/core/app/controllers/orgs/org/groups/group.js index 972fb39ed..0d12865f7 100644 --- a/ui/core/app/controllers/orgs/org/groups/group.js +++ b/ui/core/app/controllers/orgs/org/groups/group.js @@ -8,5 +8,4 @@ export default class OrgsOrgGroupsGroupController extends Controller { * @type {string} */ @alias('model.displayName') breadCrumb; - } diff --git a/ui/core/app/controllers/orgs/org/groups/new.js b/ui/core/app/controllers/orgs/org/groups/new.js index 9ca97fbe5..1c5eeb7c9 100644 --- a/ui/core/app/controllers/orgs/org/groups/new.js +++ b/ui/core/app/controllers/orgs/org/groups/new.js @@ -14,5 +14,4 @@ export default class OrgsOrgGroupsNewController extends Controller { get breadCrumb() { return this.intl.t('titles.new'); } - } diff --git a/ui/core/app/controllers/orgs/org/projects.js b/ui/core/app/controllers/orgs/org/projects.js index e1199f8e8..60c1441a7 100644 --- a/ui/core/app/controllers/orgs/org/projects.js +++ b/ui/core/app/controllers/orgs/org/projects.js @@ -2,7 +2,6 @@ import Controller from '@ember/controller'; import { inject as service } from '@ember/service'; export default class OrgsOrgProjectsController extends Controller { - // =services @service intl; @@ -15,5 +14,4 @@ export default class OrgsOrgProjectsController extends Controller { get breadCrumb() { return this.intl.t('resources.projects'); } - } diff --git a/ui/core/app/controllers/orgs/org/projects/new.js b/ui/core/app/controllers/orgs/org/projects/new.js index 5f9540189..769d6e195 100644 --- a/ui/core/app/controllers/orgs/org/projects/new.js +++ b/ui/core/app/controllers/orgs/org/projects/new.js @@ -2,7 +2,6 @@ import Controller from '@ember/controller'; import { inject as service } from '@ember/service'; export default class OrgsOrgProjectsNewController extends Controller { - // =services @service intl; @@ -15,5 +14,4 @@ export default class OrgsOrgProjectsNewController extends Controller { get breadCrumb() { return this.intl.t('titles.new'); } - } diff --git a/ui/core/app/controllers/orgs/org/projects/project.js b/ui/core/app/controllers/orgs/org/projects/project.js index d56cf15d9..c449ef523 100644 --- a/ui/core/app/controllers/orgs/org/projects/project.js +++ b/ui/core/app/controllers/orgs/org/projects/project.js @@ -2,12 +2,10 @@ import Controller from '@ember/controller'; import { alias } from '@ember/object/computed'; export default class OrgsOrgProjectsProjectController extends Controller { - // =attributes /** * @type {string} */ @alias('model.displayName') breadCrumb; - } diff --git a/ui/core/app/controllers/orgs/org/projects/project/host-catalogs.js b/ui/core/app/controllers/orgs/org/projects/project/host-catalogs.js index 80dde2c39..ed10e744b 100644 --- a/ui/core/app/controllers/orgs/org/projects/project/host-catalogs.js +++ b/ui/core/app/controllers/orgs/org/projects/project/host-catalogs.js @@ -2,7 +2,6 @@ import Controller from '@ember/controller'; import { inject as service } from '@ember/service'; export default class OrgsOrgProjectsProjectHostCatalogsController extends Controller { - // =services @service intl; @@ -15,5 +14,4 @@ export default class OrgsOrgProjectsProjectHostCatalogsController extends Contro get breadCrumb() { return this.intl.t('resources.host-catalogs'); } - } diff --git a/ui/core/app/controllers/orgs/org/projects/project/host-catalogs/host-catalog.js b/ui/core/app/controllers/orgs/org/projects/project/host-catalogs/host-catalog.js index 57d0c2724..1e733a5aa 100644 --- a/ui/core/app/controllers/orgs/org/projects/project/host-catalogs/host-catalog.js +++ b/ui/core/app/controllers/orgs/org/projects/project/host-catalogs/host-catalog.js @@ -2,12 +2,10 @@ import Controller from '@ember/controller'; import { alias } from '@ember/object/computed'; export default class OrgsOrgProjectsProjectHostCatalogsHostCatalogController extends Controller { - // =attributes /** * @type {string} */ @alias('model.displayName') breadCrumb; - } diff --git a/ui/core/app/controllers/orgs/org/projects/project/host-catalogs/new.js b/ui/core/app/controllers/orgs/org/projects/project/host-catalogs/new.js index 4fb2aed63..02b865cd0 100644 --- a/ui/core/app/controllers/orgs/org/projects/project/host-catalogs/new.js +++ b/ui/core/app/controllers/orgs/org/projects/project/host-catalogs/new.js @@ -2,7 +2,6 @@ import Controller from '@ember/controller'; import { inject as service } from '@ember/service'; export default class OrgsOrgProjectsProjectHostCatalogsNewController extends Controller { - // =services @service intl; @@ -15,5 +14,4 @@ export default class OrgsOrgProjectsProjectHostCatalogsNewController extends Con get breadCrumb() { return this.intl.t('titles.new'); } - } diff --git a/ui/core/app/controllers/orgs/org/roles.js b/ui/core/app/controllers/orgs/org/roles.js index 7f62b0e1a..16f84ae44 100644 --- a/ui/core/app/controllers/orgs/org/roles.js +++ b/ui/core/app/controllers/orgs/org/roles.js @@ -14,5 +14,4 @@ export default class OrgsOrgRolesController extends Controller { get breadCrumb() { return this.intl.t('resources.roles'); } - } diff --git a/ui/core/app/controllers/orgs/org/roles/new.js b/ui/core/app/controllers/orgs/org/roles/new.js index 38a098eaa..126c6a1b8 100644 --- a/ui/core/app/controllers/orgs/org/roles/new.js +++ b/ui/core/app/controllers/orgs/org/roles/new.js @@ -14,5 +14,4 @@ export default class OrgsOrgRolesNewController extends Controller { get breadCrumb() { return this.intl.t('titles.new'); } - } diff --git a/ui/core/app/controllers/orgs/org/roles/role.js b/ui/core/app/controllers/orgs/org/roles/role.js index 23fe25882..90425d113 100644 --- a/ui/core/app/controllers/orgs/org/roles/role.js +++ b/ui/core/app/controllers/orgs/org/roles/role.js @@ -8,5 +8,4 @@ export default class OrgsOrgRolesRoleController extends Controller { * @type {string} */ @alias('model.displayName') breadCrumb; - } diff --git a/ui/core/app/controllers/orgs/org/users.js b/ui/core/app/controllers/orgs/org/users.js index ae81e0302..fe28ecf0f 100644 --- a/ui/core/app/controllers/orgs/org/users.js +++ b/ui/core/app/controllers/orgs/org/users.js @@ -14,5 +14,4 @@ export default class OrgsOrgUsersController extends Controller { get breadCrumb() { return this.intl.t('resources.users'); } - } diff --git a/ui/core/app/controllers/scopes/scope/projects.js b/ui/core/app/controllers/scopes/scope/projects.js index cdd9bda8a..af28cf813 100644 --- a/ui/core/app/controllers/scopes/scope/projects.js +++ b/ui/core/app/controllers/scopes/scope/projects.js @@ -2,7 +2,6 @@ import Controller from '@ember/controller'; import { inject as service } from '@ember/service'; export default class ScopesScopeProjectsController extends Controller { - // =services @service intl; @@ -15,5 +14,4 @@ export default class ScopesScopeProjectsController extends Controller { get breadCrumb() { return this.intl.t('resources.projects'); } - } diff --git a/ui/core/app/controllers/scopes/scope/projects/new.js b/ui/core/app/controllers/scopes/scope/projects/new.js index 50b30d187..d1f87399f 100644 --- a/ui/core/app/controllers/scopes/scope/projects/new.js +++ b/ui/core/app/controllers/scopes/scope/projects/new.js @@ -2,7 +2,6 @@ import Controller from '@ember/controller'; import { inject as service } from '@ember/service'; export default class ScopesScopeProjectsNewController extends Controller { - // =services @service intl; @@ -15,5 +14,4 @@ export default class ScopesScopeProjectsNewController extends Controller { get breadCrumb() { return this.intl.t('titles.new'); } - } diff --git a/ui/core/app/controllers/scopes/scope/projects/project.js b/ui/core/app/controllers/scopes/scope/projects/project.js index 8fc8bbb1c..8d1219267 100644 --- a/ui/core/app/controllers/scopes/scope/projects/project.js +++ b/ui/core/app/controllers/scopes/scope/projects/project.js @@ -1,7 +1,6 @@ import Controller from '@ember/controller'; export default class ScopesScopeProjectsProjectController extends Controller { - // =attributes /** @@ -10,5 +9,4 @@ export default class ScopesScopeProjectsProjectController extends Controller { get breadCrumb() { return this.model.displayName; } - } diff --git a/ui/core/app/router.js b/ui/core/app/router.js index e07a9ee94..1c3e3e78b 100644 --- a/ui/core/app/router.js +++ b/ui/core/app/router.js @@ -7,11 +7,14 @@ export default class Router extends EmberRouter { } Router.map(function () { - - this.route('scopes', function() { - this.route('scope', { path: ':scope_id' }, function() { - this.route('projects', function() { - this.route('project', { path: ':project_id' }, function() {}); + this.route('scopes', function () { + this.route('scope', { path: ':scope_id' }, function () { + this.route('projects', function () { + this.route('project', { path: ':project_id' }, function () {}); + this.route('new'); + }); + this.route('users', function () { + this.route('user', { path: ':user_id' }, function () {}); this.route('new'); }); }); @@ -20,27 +23,31 @@ Router.map(function () { this.route('orgs', function () { this.route('org', { path: ':org_id' }, function () { this.route('projects', function () { - this.route('project', { path: ':project_id' }, function() { - this.route('host-catalogs', function() { - this.route('host-catalog', { path: ':host_catalog_id' }, function() {}); + this.route('project', { path: ':project_id' }, function () { + this.route('host-catalogs', function () { + this.route( + 'host-catalog', + { path: ':host_catalog_id' }, + function () {} + ); this.route('new'); }); }); this.route('new'); }); - this.route('users', function() { - this.route('user', { path: ':user_id' }, function() {}); + this.route('users', function () { + this.route('user', { path: ':user_id' }, function () {}); this.route('new'); }); - this.route('roles', function() { - this.route('role', { path: ':role_id' }, function() {}); + this.route('roles', function () { + this.route('role', { path: ':role_id' }, function () {}); this.route('new'); }); - this.route('groups', function() { - this.route('group', { path: ':group_id' }, function() {}); + this.route('groups', function () { + this.route('group', { path: ':group_id' }, function () {}); this.route('new'); }); }); diff --git a/ui/core/app/routes/orgs.js b/ui/core/app/routes/orgs.js index 199076b0b..f41f51d52 100644 --- a/ui/core/app/routes/orgs.js +++ b/ui/core/app/routes/orgs.js @@ -1,7 +1,6 @@ import Route from '@ember/routing/route'; export default class OrgsRoute extends Route { - // =methods /** @@ -11,5 +10,4 @@ export default class OrgsRoute extends Route { model() { return this.store.findAll('org'); } - } diff --git a/ui/core/app/routes/orgs/org/groups/group/index.js b/ui/core/app/routes/orgs/org/groups/group/index.js index 521f73a9c..9bd8f2675 100644 --- a/ui/core/app/routes/orgs/org/groups/group/index.js +++ b/ui/core/app/routes/orgs/org/groups/group/index.js @@ -1,4 +1,3 @@ import Route from '@ember/routing/route'; -export default class OrgsOrgGroupsGroupIndexRoute extends Route { -} +export default class OrgsOrgGroupsGroupIndexRoute extends Route {} diff --git a/ui/core/app/routes/orgs/org/projects/index.js b/ui/core/app/routes/orgs/org/projects/index.js index c22022cf9..be2068f6d 100644 --- a/ui/core/app/routes/orgs/org/projects/index.js +++ b/ui/core/app/routes/orgs/org/projects/index.js @@ -1,3 +1,3 @@ import Route from '@ember/routing/route'; -export default class OrgsOrgProjectsIndexRoute extends Route { } +export default class OrgsOrgProjectsIndexRoute extends Route {} diff --git a/ui/core/app/routes/orgs/org/projects/project.js b/ui/core/app/routes/orgs/org/projects/project.js index 8f06533bc..3171733ff 100644 --- a/ui/core/app/routes/orgs/org/projects/project.js +++ b/ui/core/app/routes/orgs/org/projects/project.js @@ -38,7 +38,7 @@ export default class OrgsOrgProjectsProjectRoute extends Route { this.render('orgs/org/projects/project/_sidebar', { into: 'orgs/org', outlet: 'sidebar', - model: model + model: model, }); super.renderTemplate(...arguments); } diff --git a/ui/core/app/routes/orgs/org/projects/project/host-catalogs.js b/ui/core/app/routes/orgs/org/projects/project/host-catalogs.js index eacc28056..685266cb4 100644 --- a/ui/core/app/routes/orgs/org/projects/project/host-catalogs.js +++ b/ui/core/app/routes/orgs/org/projects/project/host-catalogs.js @@ -1,7 +1,6 @@ import Route from '@ember/routing/route'; export default class OrgsOrgProjectsProjectHostCatalogsRoute extends Route { - // =methods /** diff --git a/ui/core/app/routes/orgs/org/projects/project/host-catalogs/host-catalog.js b/ui/core/app/routes/orgs/org/projects/project/host-catalogs/host-catalog.js index ace30520a..47d79f7bb 100644 --- a/ui/core/app/routes/orgs/org/projects/project/host-catalogs/host-catalog.js +++ b/ui/core/app/routes/orgs/org/projects/project/host-catalogs/host-catalog.js @@ -3,7 +3,6 @@ import { inject as service } from '@ember/service'; import { action } from '@ember/object'; export default class OrgsOrgProjectsProjectHostCatalogsHostCatalogRoute extends Route { - // =services @service notify; @@ -63,5 +62,4 @@ export default class OrgsOrgProjectsProjectHostCatalogsHostCatalogRoute extends this.notify.error(error.message, { closeAfter: null }); } } - } diff --git a/ui/core/app/routes/orgs/org/projects/project/host-catalogs/host-catalog/index.js b/ui/core/app/routes/orgs/org/projects/project/host-catalogs/host-catalog/index.js index ffba38db4..a4ba3aa0e 100644 --- a/ui/core/app/routes/orgs/org/projects/project/host-catalogs/host-catalog/index.js +++ b/ui/core/app/routes/orgs/org/projects/project/host-catalogs/host-catalog/index.js @@ -1,4 +1,3 @@ import Route from '@ember/routing/route'; -export default class OrgsOrgProjectsProjectHostCatalogsHostCatalogIndexRoute extends Route { -} +export default class OrgsOrgProjectsProjectHostCatalogsHostCatalogIndexRoute extends Route {} diff --git a/ui/core/app/routes/orgs/org/projects/project/host-catalogs/index.js b/ui/core/app/routes/orgs/org/projects/project/host-catalogs/index.js index a29543a30..b760aed44 100644 --- a/ui/core/app/routes/orgs/org/projects/project/host-catalogs/index.js +++ b/ui/core/app/routes/orgs/org/projects/project/host-catalogs/index.js @@ -1,4 +1,3 @@ import Route from '@ember/routing/route'; -export default class OrgsOrgProjectsProjectHostCatalogsIndexRoute extends Route { -} +export default class OrgsOrgProjectsProjectHostCatalogsIndexRoute extends Route {} diff --git a/ui/core/app/routes/orgs/org/projects/project/host-catalogs/new.js b/ui/core/app/routes/orgs/org/projects/project/host-catalogs/new.js index a2f4e2bc6..c2c224e88 100644 --- a/ui/core/app/routes/orgs/org/projects/project/host-catalogs/new.js +++ b/ui/core/app/routes/orgs/org/projects/project/host-catalogs/new.js @@ -39,7 +39,10 @@ export default class OrgsOrgProjectsProjectHostCatalogsNewRoute extends Route { async save(hostCatalog) { try { await hostCatalog.save(); - this.transitionTo('orgs.org.projects.project.host-catalogs.host-catalog', hostCatalog); + this.transitionTo( + 'orgs.org.projects.project.host-catalogs.host-catalog', + hostCatalog + ); // TODO: replace with translated strings this.notify.success('Host catalog created.'); } catch (error) { diff --git a/ui/core/app/routes/orgs/org/projects/project/index.js b/ui/core/app/routes/orgs/org/projects/project/index.js index 0843242d7..0fc6b90b7 100644 --- a/ui/core/app/routes/orgs/org/projects/project/index.js +++ b/ui/core/app/routes/orgs/org/projects/project/index.js @@ -1,4 +1,3 @@ import Route from '@ember/routing/route'; -export default class OrgsOrgProjectsProjectIndexRoute extends Route { -} +export default class OrgsOrgProjectsProjectIndexRoute extends Route {} diff --git a/ui/core/app/routes/orgs/org/roles.js b/ui/core/app/routes/orgs/org/roles.js index 807c2aeee..972aaf816 100644 --- a/ui/core/app/routes/orgs/org/roles.js +++ b/ui/core/app/routes/orgs/org/roles.js @@ -10,5 +10,4 @@ export default class OrgsOrgRolesRoute extends Route { model() { return this.store.findAll('role'); } - } diff --git a/ui/core/app/routes/orgs/org/users/index.js b/ui/core/app/routes/orgs/org/users/index.js index b6203b0d4..b3047d27e 100644 --- a/ui/core/app/routes/orgs/org/users/index.js +++ b/ui/core/app/routes/orgs/org/users/index.js @@ -1,4 +1,3 @@ import Route from '@ember/routing/route'; -export default class OrgsOrgUsersIndexRoute extends Route { -} +export default class OrgsOrgUsersIndexRoute extends Route {} diff --git a/ui/core/app/routes/scopes.js b/ui/core/app/routes/scopes.js index 5fff74b2e..59fc29266 100644 --- a/ui/core/app/routes/scopes.js +++ b/ui/core/app/routes/scopes.js @@ -1,4 +1,3 @@ import Route from '@ember/routing/route'; -export default class ScopesRoute extends Route { -} +export default class ScopesRoute extends Route {} diff --git a/ui/core/app/routes/scopes/scope.js b/ui/core/app/routes/scopes/scope.js index 8cde487bd..ac03641c7 100644 --- a/ui/core/app/routes/scopes/scope.js +++ b/ui/core/app/routes/scopes/scope.js @@ -1,7 +1,6 @@ import Route from '@ember/routing/route'; export default class ScopesScopeRoute extends Route { - // =methods /** @@ -24,5 +23,4 @@ export default class ScopesScopeRoute extends Route { return maybeExistingScope || this.store.createRecord('scope', { id }); } } - } diff --git a/ui/core/app/routes/scopes/scope/projects.js b/ui/core/app/routes/scopes/scope/projects.js index ebc5e97e5..775dc5ad6 100644 --- a/ui/core/app/routes/scopes/scope/projects.js +++ b/ui/core/app/routes/scopes/scope/projects.js @@ -3,7 +3,6 @@ import { inject as service } from '@ember/service'; import { action } from '@ember/object'; export default class ScopesScopeProjectsRoute extends Route { - // =services @service notify; @@ -82,5 +81,4 @@ export default class ScopesScopeProjectsRoute extends Route { this.notify.error(error.message, { closeAfter: null }); } } - } diff --git a/ui/core/app/routes/scopes/scope/projects/index.js b/ui/core/app/routes/scopes/scope/projects/index.js index 294371052..d763c183c 100644 --- a/ui/core/app/routes/scopes/scope/projects/index.js +++ b/ui/core/app/routes/scopes/scope/projects/index.js @@ -1,4 +1,3 @@ import Route from '@ember/routing/route'; -export default class ScopesScopeProjectsIndexRoute extends Route { -} +export default class ScopesScopeProjectsIndexRoute extends Route {} diff --git a/ui/core/app/routes/scopes/scope/projects/new.js b/ui/core/app/routes/scopes/scope/projects/new.js index 1781f5269..207ca3dbc 100644 --- a/ui/core/app/routes/scopes/scope/projects/new.js +++ b/ui/core/app/routes/scopes/scope/projects/new.js @@ -1,7 +1,6 @@ import Route from '@ember/routing/route'; export default class ScopesScopeProjectsNewRoute extends Route { - // =methods /** @@ -12,8 +11,7 @@ export default class ScopesScopeProjectsNewRoute extends Route { const { id } = this.modelFor('scopes.scope'); return this.store.createRecord('scope', { type: 'project', - scopeID: id + scopeID: id, }); } - } diff --git a/ui/core/app/routes/scopes/scope/projects/project.js b/ui/core/app/routes/scopes/scope/projects/project.js index c2aee8ed3..a418f3291 100644 --- a/ui/core/app/routes/scopes/scope/projects/project.js +++ b/ui/core/app/routes/scopes/scope/projects/project.js @@ -1,7 +1,6 @@ import Route from '@ember/routing/route'; export default class ScopesScopeProjectsProjectRoute extends Route { - // =methods /** @@ -13,5 +12,4 @@ export default class ScopesScopeProjectsProjectRoute extends Route { model({ project_id }) { return this.store.findRecord('scope', project_id); } - } diff --git a/ui/core/app/routes/scopes/scope/projects/project/index.js b/ui/core/app/routes/scopes/scope/projects/project/index.js index f5511f2e7..a004bac08 100644 --- a/ui/core/app/routes/scopes/scope/projects/project/index.js +++ b/ui/core/app/routes/scopes/scope/projects/project/index.js @@ -1,4 +1,3 @@ import Route from '@ember/routing/route'; -export default class ScopesScopeProjectsProjectIndexRoute extends Route { -} +export default class ScopesScopeProjectsProjectIndexRoute extends Route {} diff --git a/ui/core/app/routes/scopes/scope/users.js b/ui/core/app/routes/scopes/scope/users.js new file mode 100644 index 000000000..2720f56b1 --- /dev/null +++ b/ui/core/app/routes/scopes/scope/users.js @@ -0,0 +1,73 @@ +import Route from '@ember/routing/route'; +import { inject as service } from '@ember/service'; +import { action } from '@ember/object'; + +export default class ScopesScopeUsersRoute extends Route { + // =services + + @service intl; + @service notify; + + // =methods + + /** + * Load all users under current scope. + * @return {Promise{[UserModel]}} + */ + async model() { + return this.store.findAll('user', this.scopeAdapterOptions()); + } + + // =actions + /** + * Rollback changes on user. + * @param {UserModel} user + */ + @action + cancel(user) { + const { isNew } = user; + user.rollbackAttributes(); + if (isNew) this.transitionTo('scopes.scope.users'); + } + + /** + * Save a user in current scope. + * @param {UserModel} user + * @param {Event} e + */ + @action + async save(user) { + try { + await user.save(this.scopeAdapterOptions()); + this.refresh(); + this.notify.success(this.intl.t('notify.save-success')); + this.transitionTo('scopes.scope.users.user', user); + } catch (error) { + // TODO: replace with translated strings + this.notify.error(error.message, { closeAfter: null }); + } + } + + /** + * Delete user in current scope and redirect to index. + * @param {UserModel} user + */ + @action + async delete(user) { + try { + await user.destroyRecord(this.scopeAdapterOptions()); + this.refresh(); + this.transitionTo('scopes.scope.users'); + this.notify.success(this.intl.t('notify.delete-success')); + } catch (error) { + // TODO: replace with translated strings + this.notify.error(error.message, { closeAfter: null }); + } + } + + scopeAdapterOptions() { + const { id: scope_id } = this.modelFor('scopes.scope'); + const adapterOptions = { scopeID: scope_id }; + return { adapterOptions }; + } +} diff --git a/ui/core/app/routes/scopes/scope/users/index.js b/ui/core/app/routes/scopes/scope/users/index.js new file mode 100644 index 000000000..f59170106 --- /dev/null +++ b/ui/core/app/routes/scopes/scope/users/index.js @@ -0,0 +1,3 @@ +import Route from '@ember/routing/route'; + +export default class ScopesScopeUsersIndexRoute extends Route {} diff --git a/ui/core/app/routes/scopes/scope/users/new.js b/ui/core/app/routes/scopes/scope/users/new.js new file mode 100644 index 000000000..9b486a48b --- /dev/null +++ b/ui/core/app/routes/scopes/scope/users/new.js @@ -0,0 +1,13 @@ +import Route from '@ember/routing/route'; + +export default class ScopesScopeUsersNewRoute extends Route { + // =methods + + /** + * Creates a new unsaved user. + * @return {UserModel} + */ + model() { + return this.store.createRecord('user'); + } +} diff --git a/ui/core/app/routes/scopes/scope/users/user.js b/ui/core/app/routes/scopes/scope/users/user.js new file mode 100644 index 000000000..f28ff4d56 --- /dev/null +++ b/ui/core/app/routes/scopes/scope/users/user.js @@ -0,0 +1,17 @@ +import Route from '@ember/routing/route'; + +export default class ScopesScopeUsersUserRoute extends Route { + // =methods + + /** + * Load a user in current scope. + * @param {object} params + * @param {string} params.user_id + * @return {UserModel} + */ + async model({ user_id }) { + const { id: scope_id } = this.modelFor('scopes.scope'); + const adapterOptions = { scopeID: scope_id }; + return this.store.findRecord('user', user_id, { adapterOptions }); + } +} diff --git a/ui/core/app/routes/scopes/scope/users/user/index.js b/ui/core/app/routes/scopes/scope/users/user/index.js new file mode 100644 index 000000000..fde042f35 --- /dev/null +++ b/ui/core/app/routes/scopes/scope/users/user/index.js @@ -0,0 +1,3 @@ +import Route from '@ember/routing/route'; + +export default class ScopesScopeUsersUserIndexRoute extends Route {} diff --git a/ui/core/app/templates/scopes/scope.hbs b/ui/core/app/templates/scopes/scope.hbs index 2d1a73607..ee969738f 100644 --- a/ui/core/app/templates/scopes/scope.hbs +++ b/ui/core/app/templates/scopes/scope.hbs @@ -8,8 +8,14 @@ + + + {{t "resources.users"}} + + + {{outlet "sidebar"}} - + {{!-- diff --git a/ui/core/app/templates/scopes/scope/users.hbs b/ui/core/app/templates/scopes/scope/users.hbs new file mode 100644 index 000000000..67ccbeb9e --- /dev/null +++ b/ui/core/app/templates/scopes/scope/users.hbs @@ -0,0 +1,3 @@ +{{title (t "resources.users")}} + +{{outlet}} diff --git a/ui/core/app/templates/scopes/scope/users/index.hbs b/ui/core/app/templates/scopes/scope/users/index.hbs new file mode 100644 index 000000000..1ee0f3ac2 --- /dev/null +++ b/ui/core/app/templates/scopes/scope/users/index.hbs @@ -0,0 +1,63 @@ + + + + + + + +

{{t "resources.users"}}

+
+ + + {{#if @model}} + {{t 'titles.new'}} + {{/if}} + + + + + {{#if @model}} + + + + + {{t 'form.label.name'}} + {{t 'form.label.description'}} + + + + {{#each @model as |user|}} + + + + {{user.name}} + + + {{user.description}} + + {{/each}} + + + + {{else}} + + + + + {{t 'descriptions.users-welcome'}} + + + + {{t 'titles.new'}} + + + + + {{/if}} + + + +
diff --git a/ui/core/app/templates/scopes/scope/users/new.hbs b/ui/core/app/templates/scopes/scope/users/new.hbs new file mode 100644 index 000000000..08e72531c --- /dev/null +++ b/ui/core/app/templates/scopes/scope/users/new.hbs @@ -0,0 +1,21 @@ +{{title (t 'titles.new')}} + + + + + + + + +

{{t "titles.new"}}

+
+ + + + + +
diff --git a/ui/core/app/templates/scopes/scope/users/user.hbs b/ui/core/app/templates/scopes/scope/users/user.hbs new file mode 100644 index 000000000..d66e83390 --- /dev/null +++ b/ui/core/app/templates/scopes/scope/users/user.hbs @@ -0,0 +1,3 @@ +{{title @model.displayName}} + +{{outlet}} diff --git a/ui/core/app/templates/scopes/scope/users/user/index.hbs b/ui/core/app/templates/scopes/scope/users/user/index.hbs new file mode 100644 index 000000000..d80978336 --- /dev/null +++ b/ui/core/app/templates/scopes/scope/users/user/index.hbs @@ -0,0 +1,35 @@ + + + + + + + +

{{t "resources.user"}}

+
+ + + + + {{t 'titles.details'}} + + + + + + + + + {{t 'actions.delete'}} + + + +
diff --git a/ui/core/tests/acceptance/bread-crumbs-test.js b/ui/core/tests/acceptance/bread-crumbs-test.js index 7c33595e9..2575f1c75 100644 --- a/ui/core/tests/acceptance/bread-crumbs-test.js +++ b/ui/core/tests/acceptance/bread-crumbs-test.js @@ -55,109 +55,176 @@ module('Acceptance | breadcrumbs', function (hooks) { assert.expect(3); await visit(projectURL); assert.equal(currentURL(), projectURL, 'We begin on the expected page.'); - crumbPath = new URL(find('.rose-nav-breadcrumbs-link:last-child').href).pathname; + crumbPath = new URL(find('.rose-nav-breadcrumbs-link:last-child').href) + .pathname; assert.equal(crumbPath, projectURL, 'Last crumb has expected URL.'); await click(find('.rose-nav-breadcrumbs-link:first-child')); - assert.equal(currentURL(), projectsURL, 'After navigating via breadcrumbs, we are one level up.'); + assert.equal( + currentURL(), + projectsURL, + 'After navigating via breadcrumbs, we are one level up.' + ); }); test('can navigate via breadcrumbs to projects from new project', async function (assert) { assert.expect(3); await visit(newProjectURL); assert.equal(currentURL(), newProjectURL, 'We begin on the expected page.'); - crumbPath = new URL(find('.rose-nav-breadcrumbs-link:last-child').href).pathname; + crumbPath = new URL(find('.rose-nav-breadcrumbs-link:last-child').href) + .pathname; assert.equal(crumbPath, newProjectURL, 'Last crumb has expected URL.'); await click(find('.rose-nav-breadcrumbs-link:first-child')); - assert.equal(currentURL(), projectsURL, 'After navigating via breadcrumbs, we are one level up.'); + assert.equal( + currentURL(), + projectsURL, + 'After navigating via breadcrumbs, we are one level up.' + ); }); test('can navigate via breadcrumbs to project from host catalogs', async function (assert) { assert.expect(3); await visit(hostCatalogsURL); - assert.equal(currentURL(), hostCatalogsURL, 'We begin on the expected page.'); - crumbPath = new URL(find('.rose-nav-breadcrumbs-link:last-child').href).pathname; + assert.equal( + currentURL(), + hostCatalogsURL, + 'We begin on the expected page.' + ); + crumbPath = new URL(find('.rose-nav-breadcrumbs-link:last-child').href) + .pathname; assert.equal(crumbPath, hostCatalogsURL, 'Last crumb has expected URL.'); await click(findAll('.rose-nav-breadcrumbs-link')[1]); - assert.equal(currentURL(), projectURL, 'After navigating via breadcrumbs, we are one level up.'); + assert.equal( + currentURL(), + projectURL, + 'After navigating via breadcrumbs, we are one level up.' + ); }); test('can navigate via breadcrumbs to host catalogs from host catalog', async function (assert) { assert.expect(3); await visit(hostCatalogURL); - assert.equal(currentURL(), hostCatalogURL, 'We begin on the expected page.'); - crumbPath = new URL(find('.rose-nav-breadcrumbs-link:last-child').href).pathname; + assert.equal( + currentURL(), + hostCatalogURL, + 'We begin on the expected page.' + ); + crumbPath = new URL(find('.rose-nav-breadcrumbs-link:last-child').href) + .pathname; assert.equal(crumbPath, hostCatalogURL, 'Last crumb has expected URL.'); await click(findAll('.rose-nav-breadcrumbs-link')[2]); - assert.equal(currentURL(), hostCatalogsURL, 'After navigating via breadcrumbs, we are one level up.'); + assert.equal( + currentURL(), + hostCatalogsURL, + 'After navigating via breadcrumbs, we are one level up.' + ); }); test('can navigate via breadcrumbs to host catalogs from new host catalog', async function (assert) { assert.expect(3); await visit(newHostCatalogURL); - assert.equal(currentURL(), newHostCatalogURL, 'We begin on the expected page.'); - crumbPath = new URL(find('.rose-nav-breadcrumbs-link:last-child').href).pathname; + assert.equal( + currentURL(), + newHostCatalogURL, + 'We begin on the expected page.' + ); + crumbPath = new URL(find('.rose-nav-breadcrumbs-link:last-child').href) + .pathname; assert.equal(crumbPath, newHostCatalogURL, 'Last crumb has expected URL.'); await click(findAll('.rose-nav-breadcrumbs-link')[2]); - assert.equal(currentURL(), hostCatalogsURL, 'After navigating via breadcrumbs, we are one level up.'); + assert.equal( + currentURL(), + hostCatalogsURL, + 'After navigating via breadcrumbs, we are one level up.' + ); }); test('can navigate via breadcrumbs to roles from role', async function (assert) { assert.expect(3); await visit(roleURL); assert.equal(currentURL(), roleURL, 'We begin on the expected page.'); - crumbPath = new URL(find('.rose-nav-breadcrumbs-link:last-child').href).pathname; + crumbPath = new URL(find('.rose-nav-breadcrumbs-link:last-child').href) + .pathname; assert.equal(crumbPath, roleURL, 'Last crumb has expected URL.'); await click(find('.rose-nav-breadcrumbs-link:first-child')); - assert.equal(currentURL(), rolesURL, 'After navigating via breadcrumbs, we are one level up.'); + assert.equal( + currentURL(), + rolesURL, + 'After navigating via breadcrumbs, we are one level up.' + ); }); test('can navigate via breadcrumbs to roles from new role', async function (assert) { assert.expect(3); await visit(newRoleURL); assert.equal(currentURL(), newRoleURL, 'We begin on the expected page.'); - crumbPath = new URL(find('.rose-nav-breadcrumbs-link:last-child').href).pathname; + crumbPath = new URL(find('.rose-nav-breadcrumbs-link:last-child').href) + .pathname; assert.equal(crumbPath, newRoleURL, 'Last crumb has expected URL.'); await click(find('.rose-nav-breadcrumbs-link:first-child')); - assert.equal(currentURL(), rolesURL, 'After navigating via breadcrumbs, we are one level up.'); + assert.equal( + currentURL(), + rolesURL, + 'After navigating via breadcrumbs, we are one level up.' + ); }); test('can navigate via breadcrumbs to groups from group', async function (assert) { assert.expect(3); await visit(groupURL); assert.equal(currentURL(), groupURL, 'We begin on the expected page.'); - crumbPath = new URL(find('.rose-nav-breadcrumbs-link:last-child').href).pathname; + crumbPath = new URL(find('.rose-nav-breadcrumbs-link:last-child').href) + .pathname; assert.equal(crumbPath, groupURL, 'Last crumb has expected URL.'); await click(find('.rose-nav-breadcrumbs-link:first-child')); - assert.equal(currentURL(), groupsURL, 'After navigating via breadcrumbs, we are one level up.'); + assert.equal( + currentURL(), + groupsURL, + 'After navigating via breadcrumbs, we are one level up.' + ); }); test('can navigate via breadcrumbs to groups from new group', async function (assert) { assert.expect(3); await visit(newGroupURL); assert.equal(currentURL(), newGroupURL, 'We begin on the expected page.'); - crumbPath = new URL(find('.rose-nav-breadcrumbs-link:last-child').href).pathname; + crumbPath = new URL(find('.rose-nav-breadcrumbs-link:last-child').href) + .pathname; assert.equal(crumbPath, newGroupURL, 'Last crumb has expected URL.'); await click(find('.rose-nav-breadcrumbs-link:first-child')); - assert.equal(currentURL(), groupsURL, 'After navigating via breadcrumbs, we are one level up.'); + assert.equal( + currentURL(), + groupsURL, + 'After navigating via breadcrumbs, we are one level up.' + ); }); test('can navigate via breadcrumbs to users from user', async function (assert) { assert.expect(3); await visit(userURL); assert.equal(currentURL(), userURL, 'We begin on the expected page.'); - crumbPath = new URL(find('.rose-nav-breadcrumbs-link:last-child').href).pathname; + crumbPath = new URL(find('.rose-nav-breadcrumbs-link:last-child').href) + .pathname; assert.equal(crumbPath, userURL, 'Last crumb has expected URL.'); await click(find('.rose-nav-breadcrumbs-link:first-child')); - assert.equal(currentURL(), usersURL, 'After navigating via breadcrumbs, we are one level up.'); + assert.equal( + currentURL(), + usersURL, + 'After navigating via breadcrumbs, we are one level up.' + ); }); test('can navigate via breadcrumbs to users from new user', async function (assert) { assert.expect(3); await visit(newUserURL); assert.equal(currentURL(), newUserURL, 'We begin on th expected page.'); - crumbPath = new URL(find('.rose-nav-breadcrumbs-link:last-child').href).pathname; + crumbPath = new URL(find('.rose-nav-breadcrumbs-link:last-child').href) + .pathname; assert.equal(crumbPath, newUserURL, 'Last crumb has expected URL.'); await click(find('.rose-nav-breadcrumbs-link:first-child')); - assert.equal(currentURL(), usersURL, 'After navigating via breadcrumbs, we are one level up.'); + assert.equal( + currentURL(), + usersURL, + 'After navigating via breadcrumbs, we are one level up.' + ); }); }); diff --git a/ui/core/tests/acceptance/groups-test.js b/ui/core/tests/acceptance/groups-test.js index 7f3f2ec69..5564f9d62 100644 --- a/ui/core/tests/acceptance/groups-test.js +++ b/ui/core/tests/acceptance/groups-test.js @@ -5,8 +5,7 @@ import setupMirage from 'ember-cli-mirage/test-support/setup-mirage'; import a11yAudit from 'ember-a11y-testing/test-support/audit'; import { Response } from 'miragejs'; - -module('Acceptance | groups', function(hooks) { +module('Acceptance | groups', function (hooks) { setupApplicationTest(hooks); setupMirage(hooks); @@ -80,7 +79,7 @@ module('Acceptance | groups', function(hooks) { test('can save changes to an existing group', async function (assert) { assert.expect(2); - this.server.createList('group', 1, {name: 'Admin group'}); + this.server.createList('group', 1, { name: 'Admin group' }); await visit('/orgs/1/groups/1'); await fillIn('[name="name"]', 'Updated admin group'); await click('.rose-form-actions [type="submit"]'); @@ -90,14 +89,14 @@ module('Acceptance | groups', function(hooks) { test('can cancel changes to an existing group', async function (assert) { assert.expect(1); - this.server.createList('group', 1, {name: 'Admin group'}); + this.server.createList('group', 1, { name: 'Admin group' }); await visit('/orgs/1/groups/1'); await fillIn('[name="name"]', 'Updated admin group'); await click('.rose-form-actions [type="button"]'); assert.equal(find('[name="name"]').value, 'Admin group'); }); - test('can delete a group', async function(assert) { + test('can delete a group', async function (assert) { assert.expect(2); this.server.createList('group', 1); assert.equal(this.server.db.groups.length, 1); diff --git a/ui/core/tests/acceptance/host-catalogs-test.js b/ui/core/tests/acceptance/host-catalogs-test.js index 4be43309f..a2684e694 100644 --- a/ui/core/tests/acceptance/host-catalogs-test.js +++ b/ui/core/tests/acceptance/host-catalogs-test.js @@ -5,7 +5,7 @@ import setupMirage from 'ember-cli-mirage/test-support/setup-mirage'; import a11yAudit from 'ember-a11y-testing/test-support/audit'; import { Response } from 'miragejs'; -module('Acceptance | host catalogs', function(hooks) { +module('Acceptance | host catalogs', function (hooks) { setupApplicationTest(hooks); setupMirage(hooks); @@ -22,7 +22,7 @@ module('Acceptance | host catalogs', function(hooks) { assert.equal(currentURL(), '/orgs/1/projects/1/host-catalogs'); }); - test('can navigate to host catalog form', async function(assert) { + test('can navigate to host catalog form', async function (assert) { assert.expect(1); this.server.createList('project', 1); this.server.createList('host-catalog', 1); @@ -32,7 +32,7 @@ module('Acceptance | host catalogs', function(hooks) { assert.equal(currentURL(), '/orgs/1/projects/1/host-catalogs/1'); }); - test('can delete host catalog', async function(assert) { + test('can delete host catalog', async function (assert) { assert.expect(2); this.server.createList('project', 1); this.server.createList('host-catalog', 1); @@ -42,7 +42,7 @@ module('Acceptance | host catalogs', function(hooks) { assert.equal(this.server.db.hostCatalogs.length, 0); }); - test('can update host catalog and save changes', async function(assert) { + test('can update host catalog and save changes', async function (assert) { assert.expect(1); this.server.createList('project', 1); this.server.createList('host-catalog', 1); @@ -52,7 +52,7 @@ module('Acceptance | host catalogs', function(hooks) { assert.equal(this.server.db.hostCatalogs[0].name, 'Test Name'); }); - test('can update host catalog and cancel changes', async function(assert) { + test('can update host catalog and cancel changes', async function (assert) { assert.expect(1); this.server.createList('project', 1); this.server.createList('host-catalog', 1); @@ -62,7 +62,7 @@ module('Acceptance | host catalogs', function(hooks) { assert.notEqual(this.server.db.hostCatalogs[0].name, 'Test Name'); }); - test('can create host catalog and save changes', async function(assert) { + test('can create host catalog and save changes', async function (assert) { assert.expect(2); this.server.createList('project', 1); await visit('/orgs/1/projects/1/host-catalogs/new'); @@ -72,7 +72,7 @@ module('Acceptance | host catalogs', function(hooks) { assert.equal(currentURL(), '/orgs/1/projects/1/host-catalogs/1'); }); - test('can create host catalog and cancel changes', async function(assert) { + test('can create host catalog and cancel changes', async function (assert) { assert.expect(2); this.server.createList('project', 1); await visit('/orgs/1/projects/1/host-catalogs/new'); @@ -86,17 +86,20 @@ module('Acceptance | host catalogs', function(hooks) { assert.expect(1); this.server.createList('project', 1); this.server.createList('host-catalog', 1); - this.server.patch('/orgs/:org_id/projects/:project_id/host-catalogs/:id', () => { - return new Response( - 490, - {}, - { - status: 490, - code: 'error', - message: 'Oops.', - } - ); - }); + this.server.patch( + '/orgs/:org_id/projects/:project_id/host-catalogs/:id', + () => { + return new Response( + 490, + {}, + { + status: 490, + code: 'error', + message: 'Oops.', + } + ); + } + ); await visit('/orgs/1/projects/1/host-catalogs/1'); await fillIn('[name="name"]', 'random string'); await click('form [type="submit"]'); @@ -112,17 +115,20 @@ module('Acceptance | host catalogs', function(hooks) { assert.expect(1); this.server.createList('project', 1); this.server.createList('host-catalog', 1); - this.server.del('/orgs/:org_id/projects/:project_id/host-catalogs/:id', () => { - return new Response( - 490, - {}, - { - status: 490, - code: 'error', - message: 'Oops.', - } - ); - }); + this.server.del( + '/orgs/:org_id/projects/:project_id/host-catalogs/:id', + () => { + return new Response( + 490, + {}, + { + status: 490, + code: 'error', + message: 'Oops.', + } + ); + } + ); await visit('/orgs/1/projects/1/host-catalogs/1'); await click('.rose-button-warning'); await a11yAudit(); @@ -175,25 +181,28 @@ module('Acceptance | host catalogs', function(hooks) { assert.expect(2); this.server.createList('project', 1); this.server.createList('host-catalog', 1); - this.server.patch('/orgs/:org_id/projects/:project_id/host-catalogs/:id', () => { - return new Response( - 400, - {}, - { - status: 400, - code: 'invalid_argument', - message: 'The request was invalid.', - details: { - fields: [ - { - name: 'name', - message: 'Name is required.', - }, - ], - }, - } - ); - }); + this.server.patch( + '/orgs/:org_id/projects/:project_id/host-catalogs/:id', + () => { + return new Response( + 400, + {}, + { + status: 400, + code: 'invalid_argument', + message: 'The request was invalid.', + details: { + fields: [ + { + name: 'name', + message: 'Name is required.', + }, + ], + }, + } + ); + } + ); await visit('/orgs/1/projects/1/host-catalogs/1'); await fillIn('[name="name"]', 'random string'); await click('form [type="submit"]'); @@ -209,5 +218,4 @@ module('Acceptance | host catalogs', function(hooks) { 'Displays field-level errors.' ); }); - }); diff --git a/ui/core/tests/acceptance/projects-test.js b/ui/core/tests/acceptance/projects-test.js index 57ba7c17e..bfa87857c 100644 --- a/ui/core/tests/acceptance/projects-test.js +++ b/ui/core/tests/acceptance/projects-test.js @@ -20,9 +20,11 @@ module('Acceptance | projects', function (hooks) { let initialProjectScopesCount; hooks.beforeEach(function () { - org = this.server.create('scope', {type: 'org'}, 'withChildren'); - getProjectScopesCount = () => this.server.schema.scopes.where({type: 'project'}).length; - getFirstProjectScope = () => this.server.schema.scopes.where({type: 'project'}).models[0]; + org = this.server.create('scope', { type: 'org' }, 'withChildren'); + getProjectScopesCount = () => + this.server.schema.scopes.where({ type: 'project' }).length; + getFirstProjectScope = () => + this.server.schema.scopes.where({ type: 'project' }).models[0]; initialProjectScopesCount = getProjectScopesCount(); orgID = org.id; projectsURL = `/scopes/${orgID}/projects`; @@ -44,7 +46,11 @@ module('Acceptance | projects', function (hooks) { assert.notEqual(wrongProjectsURL, projectsURL); await visit(wrongProjectsURL); await a11yAudit(); - assert.equal(currentURL(), projectsURL, 'Wrong projects path was redirected to correct path.'); + assert.equal( + currentURL(), + projectsURL, + 'Wrong projects path was redirected to correct path.' + ); }); test('can create new projects', async function (assert) { @@ -119,7 +125,7 @@ module('Acceptance | projects', function (hooks) { assert.equal(find('[name="name"]').value, existingProject.name); }); - test('can delete project', async function(assert) { + test('can delete project', async function (assert) { assert.expect(1); await visit(existingProjectURL); await click('.rose-button-warning'); diff --git a/ui/core/tests/acceptance/roles-test.js b/ui/core/tests/acceptance/roles-test.js index 976ba47b7..038d367ae 100644 --- a/ui/core/tests/acceptance/roles-test.js +++ b/ui/core/tests/acceptance/roles-test.js @@ -79,7 +79,7 @@ module('Acceptance | roles', function (hooks) { test('can save changes to an existing role', async function (assert) { assert.expect(2); - this.server.createList('role', 1, {name: 'Admin role'}); + this.server.createList('role', 1, { name: 'Admin role' }); await visit('/orgs/1/roles/1'); await fillIn('[name="name"]', 'Updated admin role'); await click('.rose-form-actions [type="submit"]'); @@ -89,14 +89,14 @@ module('Acceptance | roles', function (hooks) { test('can cancel changes to an existing role', async function (assert) { assert.expect(1); - this.server.createList('role', 1, {name: 'Admin role'}); + this.server.createList('role', 1, { name: 'Admin role' }); await visit('/orgs/1/roles/1'); await fillIn('[name="name"]', 'Updated admin role'); await click('.rose-form-actions [type="button"]'); assert.equal(find('[name="name"]').value, 'Admin role'); }); - test('can delete a role', async function(assert) { + test('can delete a role', async function (assert) { assert.expect(2); this.server.createList('role', 1); assert.equal(this.server.db.roles.length, 1); diff --git a/ui/core/tests/acceptance/users-test.js b/ui/core/tests/acceptance/users-test.js index f1160c7f3..af68f19bc 100644 --- a/ui/core/tests/acceptance/users-test.js +++ b/ui/core/tests/acceptance/users-test.js @@ -5,7 +5,7 @@ import setupMirage from 'ember-cli-mirage/test-support/setup-mirage'; import a11yAudit from 'ember-a11y-testing/test-support/audit'; import { Response } from 'miragejs'; -module('Acceptance | users', function(hooks) { +module('Acceptance | users', function (hooks) { setupApplicationTest(hooks); setupMirage(hooks); @@ -80,7 +80,7 @@ module('Acceptance | users', function(hooks) { test('can save changes to an existing user', async function (assert) { assert.expect(2); - this.server.createList('user', 1, {name: 'Test User'}); + this.server.createList('user', 1, { name: 'Test User' }); await visit('/orgs/1/users/1'); await fillIn('[name="name"]', 'Updated user name'); await click('.rose-form-actions [type="submit"]'); @@ -90,14 +90,14 @@ module('Acceptance | users', function(hooks) { test('can cancel changes to an existing user', async function (assert) { assert.expect(1); - this.server.createList('user', 1, {name: 'Test User'}); + this.server.createList('user', 1, { name: 'Test User' }); await visit('/orgs/1/users/1'); await fillIn('[name="name"]', 'Unsaved user name'); await click('.rose-form-actions [type="button"]'); assert.equal(find('[name="name"]').value, 'Test User'); }); - test('can delete an user', async function(assert) { + test('can delete an user', async function (assert) { assert.expect(2); this.server.createList('user', 1); assert.equal(this.server.db.users.length, 1); diff --git a/ui/core/tests/integration/components/bread-crumbs-test.js b/ui/core/tests/integration/components/bread-crumbs-test.js index a6f3579c8..2e59085cb 100644 --- a/ui/core/tests/integration/components/bread-crumbs-test.js +++ b/ui/core/tests/integration/components/bread-crumbs-test.js @@ -3,11 +3,11 @@ import { setupRenderingTest } from 'ember-qunit'; import { render, find, findAll } from '@ember/test-helpers'; import { hbs } from 'ember-cli-htmlbars'; -module('Integration | Component | bread-crumbs', function(hooks) { +module('Integration | Component | bread-crumbs', function (hooks) { setupRenderingTest(hooks); - test('it renders', async function(assert) { - this.set('breadCrumbs', [{label: 'Level 1'}]); + test('it renders', async function (assert) { + this.set('breadCrumbs', [{ label: 'Level 1' }]); await render(hbs``); // Default index route @@ -15,28 +15,34 @@ module('Integration | Component | bread-crumbs', function(hooks) { assert.equal(find('.rose-nav-breadcrumbs').textContent.trim(), 'Level 1'); }); - test('it renders with model', async function(assert) { + test('it renders with model', async function (assert) { let store = this.owner.lookup('service:store'); - let project = store.createRecord('project', {id: '1234'}); + let project = store.createRecord('project', { id: '1234' }); - this.set('breadCrumbs', [{ - label: 'Level 2', - path: 'orgs.org.projects.project', - model: project - }]); + this.set('breadCrumbs', [ + { + label: 'Level 2', + path: 'orgs.org.projects.project', + model: project, + }, + ]); await render(hbs``); assert.equal(find('.rose-nav-breadcrumbs').textContent.trim(), 'Level 2'); }); - test('it renders multiple breadCrumbs', async function(assert) { - this.set('breadCrumbs', [{ - label: 'Level 1' - },{ - label: 'Level 2' - }, { - label: 'Level 3' - }]); + test('it renders multiple breadCrumbs', async function (assert) { + this.set('breadCrumbs', [ + { + label: 'Level 1', + }, + { + label: 'Level 2', + }, + { + label: 'Level 3', + }, + ]); await render(hbs``); assert.equal(findAll('a').length, 3); diff --git a/ui/core/tests/integration/components/form/host-catalog-test.js b/ui/core/tests/integration/components/form/host-catalog-test.js index 0e3b51865..20dbd545f 100644 --- a/ui/core/tests/integration/components/form/host-catalog-test.js +++ b/ui/core/tests/integration/components/form/host-catalog-test.js @@ -3,10 +3,10 @@ import { setupRenderingTest } from 'ember-qunit'; import { render, find } from '@ember/test-helpers'; import { hbs } from 'ember-cli-htmlbars'; -module('Integration | Component | form/host-catalog', function(hooks) { +module('Integration | Component | form/host-catalog', function (hooks) { setupRenderingTest(hooks); - test('it renders', async function(assert) { + test('it renders', async function (assert) { // Set any properties with this.set('myProperty', 'value'); // Handle any actions with this.set('myAction', function(val) { ... }); this.submit = () => {}; diff --git a/ui/core/tests/unit/controllers/orgs/org/groups-test.js b/ui/core/tests/unit/controllers/orgs/org/groups-test.js index 0ca58eed0..338abe601 100644 --- a/ui/core/tests/unit/controllers/orgs/org/groups-test.js +++ b/ui/core/tests/unit/controllers/orgs/org/groups-test.js @@ -1,11 +1,11 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; -module('Unit | Controller | orgs/org/groups', function(hooks) { +module('Unit | Controller | orgs/org/groups', function (hooks) { setupTest(hooks); // Replace this with your real tests. - test('it exists', function(assert) { + test('it exists', function (assert) { let controller = this.owner.lookup('controller:orgs/org/groups'); assert.ok(controller); }); diff --git a/ui/core/tests/unit/controllers/orgs/org/groups/group-test.js b/ui/core/tests/unit/controllers/orgs/org/groups/group-test.js index 49cf5b3a6..c49531dfb 100644 --- a/ui/core/tests/unit/controllers/orgs/org/groups/group-test.js +++ b/ui/core/tests/unit/controllers/orgs/org/groups/group-test.js @@ -1,11 +1,11 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; -module('Unit | Controller | orgs/org/groups/group', function(hooks) { +module('Unit | Controller | orgs/org/groups/group', function (hooks) { setupTest(hooks); // Replace this with your real tests. - test('it exists', function(assert) { + test('it exists', function (assert) { let controller = this.owner.lookup('controller:orgs/org/groups/group'); assert.ok(controller); }); diff --git a/ui/core/tests/unit/controllers/orgs/org/groups/new-test.js b/ui/core/tests/unit/controllers/orgs/org/groups/new-test.js index ef02542c9..a85f11078 100644 --- a/ui/core/tests/unit/controllers/orgs/org/groups/new-test.js +++ b/ui/core/tests/unit/controllers/orgs/org/groups/new-test.js @@ -1,11 +1,11 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; -module('Unit | Controller | orgs/org/groups/new', function(hooks) { +module('Unit | Controller | orgs/org/groups/new', function (hooks) { setupTest(hooks); // Replace this with your real tests. - test('it exists', function(assert) { + test('it exists', function (assert) { let controller = this.owner.lookup('controller:orgs/org/groups/new'); assert.ok(controller); }); diff --git a/ui/core/tests/unit/controllers/orgs/org/projects-test.js b/ui/core/tests/unit/controllers/orgs/org/projects-test.js index 6176a173c..c4d200e50 100644 --- a/ui/core/tests/unit/controllers/orgs/org/projects-test.js +++ b/ui/core/tests/unit/controllers/orgs/org/projects-test.js @@ -1,11 +1,11 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; -module('Unit | Controller | orgs/org/projects', function(hooks) { +module('Unit | Controller | orgs/org/projects', function (hooks) { setupTest(hooks); // Replace this with your real tests. - test('it exists', function(assert) { + test('it exists', function (assert) { let controller = this.owner.lookup('controller:orgs/org/projects'); assert.ok(controller); }); diff --git a/ui/core/tests/unit/controllers/orgs/org/projects/new-test.js b/ui/core/tests/unit/controllers/orgs/org/projects/new-test.js index 9e3731313..055c60a9f 100644 --- a/ui/core/tests/unit/controllers/orgs/org/projects/new-test.js +++ b/ui/core/tests/unit/controllers/orgs/org/projects/new-test.js @@ -1,11 +1,11 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; -module('Unit | Controller | orgs/org/projects/new', function(hooks) { +module('Unit | Controller | orgs/org/projects/new', function (hooks) { setupTest(hooks); // Replace this with your real tests. - test('it exists', function(assert) { + test('it exists', function (assert) { let controller = this.owner.lookup('controller:orgs/org/projects/new'); assert.ok(controller); }); diff --git a/ui/core/tests/unit/controllers/orgs/org/projects/project-test.js b/ui/core/tests/unit/controllers/orgs/org/projects/project-test.js index 36a2c2482..067429ee5 100644 --- a/ui/core/tests/unit/controllers/orgs/org/projects/project-test.js +++ b/ui/core/tests/unit/controllers/orgs/org/projects/project-test.js @@ -1,11 +1,11 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; -module('Unit | Controller | orgs/org/projects/project', function(hooks) { +module('Unit | Controller | orgs/org/projects/project', function (hooks) { setupTest(hooks); // Replace this with your real tests. - test('it exists', function(assert) { + test('it exists', function (assert) { let controller = this.owner.lookup('controller:orgs/org/projects/project'); assert.ok(controller); }); diff --git a/ui/core/tests/unit/controllers/orgs/org/projects/project/host-catalogs-test.js b/ui/core/tests/unit/controllers/orgs/org/projects/project/host-catalogs-test.js index 808f14138..6127a3173 100644 --- a/ui/core/tests/unit/controllers/orgs/org/projects/project/host-catalogs-test.js +++ b/ui/core/tests/unit/controllers/orgs/org/projects/project/host-catalogs-test.js @@ -1,12 +1,16 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; -module('Unit | Controller | orgs/org/projects/project/host-catalogs', function(hooks) { +module('Unit | Controller | orgs/org/projects/project/host-catalogs', function ( + hooks +) { setupTest(hooks); // Replace this with your real tests. - test('it exists', function(assert) { - let controller = this.owner.lookup('controller:orgs/org/projects/project/host-catalogs'); + test('it exists', function (assert) { + let controller = this.owner.lookup( + 'controller:orgs/org/projects/project/host-catalogs' + ); assert.ok(controller); }); }); diff --git a/ui/core/tests/unit/controllers/orgs/org/projects/project/host-catalogs/host-catalog-test.js b/ui/core/tests/unit/controllers/orgs/org/projects/project/host-catalogs/host-catalog-test.js index 4efc4b85c..38b3a9c9d 100644 --- a/ui/core/tests/unit/controllers/orgs/org/projects/project/host-catalogs/host-catalog-test.js +++ b/ui/core/tests/unit/controllers/orgs/org/projects/project/host-catalogs/host-catalog-test.js @@ -1,12 +1,17 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; -module('Unit | Controller | orgs/org/projects/project/host-catalogs/host-catalog', function(hooks) { - setupTest(hooks); +module( + 'Unit | Controller | orgs/org/projects/project/host-catalogs/host-catalog', + function (hooks) { + setupTest(hooks); - // Replace this with your real tests. - test('it exists', function(assert) { - let controller = this.owner.lookup('controller:orgs/org/projects/project/host-catalogs/host-catalog'); - assert.ok(controller); - }); -}); + // Replace this with your real tests. + test('it exists', function (assert) { + let controller = this.owner.lookup( + 'controller:orgs/org/projects/project/host-catalogs/host-catalog' + ); + assert.ok(controller); + }); + } +); diff --git a/ui/core/tests/unit/controllers/orgs/org/projects/project/host-catalogs/new-test.js b/ui/core/tests/unit/controllers/orgs/org/projects/project/host-catalogs/new-test.js index 1981e1857..ee0f326a1 100644 --- a/ui/core/tests/unit/controllers/orgs/org/projects/project/host-catalogs/new-test.js +++ b/ui/core/tests/unit/controllers/orgs/org/projects/project/host-catalogs/new-test.js @@ -1,12 +1,17 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; -module('Unit | Controller | orgs/org/projects/project/host-catalogs/new', function(hooks) { - setupTest(hooks); +module( + 'Unit | Controller | orgs/org/projects/project/host-catalogs/new', + function (hooks) { + setupTest(hooks); - // Replace this with your real tests. - test('it exists', function(assert) { - let controller = this.owner.lookup('controller:orgs/org/projects/project/host-catalogs/new'); - assert.ok(controller); - }); -}); + // Replace this with your real tests. + test('it exists', function (assert) { + let controller = this.owner.lookup( + 'controller:orgs/org/projects/project/host-catalogs/new' + ); + assert.ok(controller); + }); + } +); diff --git a/ui/core/tests/unit/controllers/orgs/org/roles-test.js b/ui/core/tests/unit/controllers/orgs/org/roles-test.js index 816459cd3..12c57a108 100644 --- a/ui/core/tests/unit/controllers/orgs/org/roles-test.js +++ b/ui/core/tests/unit/controllers/orgs/org/roles-test.js @@ -1,10 +1,10 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; -module('Unit | Controller | orgs/org/roles', function(hooks) { +module('Unit | Controller | orgs/org/roles', function (hooks) { setupTest(hooks); - test('it exists', function(assert) { + test('it exists', function (assert) { let controller = this.owner.lookup('controller:orgs/org/roles'); assert.ok(controller); }); diff --git a/ui/core/tests/unit/controllers/orgs/org/roles/new-test.js b/ui/core/tests/unit/controllers/orgs/org/roles/new-test.js index 8d948ae06..c9e4cfcc8 100644 --- a/ui/core/tests/unit/controllers/orgs/org/roles/new-test.js +++ b/ui/core/tests/unit/controllers/orgs/org/roles/new-test.js @@ -1,10 +1,10 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; -module('Unit | Controller | orgs/org/roles/new', function(hooks) { +module('Unit | Controller | orgs/org/roles/new', function (hooks) { setupTest(hooks); - test('it exists', function(assert) { + test('it exists', function (assert) { let controller = this.owner.lookup('controller:orgs/org/roles/new'); assert.ok(controller); }); diff --git a/ui/core/tests/unit/controllers/orgs/org/roles/role-test.js b/ui/core/tests/unit/controllers/orgs/org/roles/role-test.js index cd56c941f..515ae8420 100644 --- a/ui/core/tests/unit/controllers/orgs/org/roles/role-test.js +++ b/ui/core/tests/unit/controllers/orgs/org/roles/role-test.js @@ -1,10 +1,10 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; -module('Unit | Controller | orgs/org/roles/role', function(hooks) { +module('Unit | Controller | orgs/org/roles/role', function (hooks) { setupTest(hooks); - test('it exists', function(assert) { + test('it exists', function (assert) { let controller = this.owner.lookup('controller:orgs/org/roles/role'); assert.ok(controller); }); diff --git a/ui/core/tests/unit/controllers/orgs/org/users-test.js b/ui/core/tests/unit/controllers/orgs/org/users-test.js index c5be2fb52..5f0c57d8c 100644 --- a/ui/core/tests/unit/controllers/orgs/org/users-test.js +++ b/ui/core/tests/unit/controllers/orgs/org/users-test.js @@ -1,11 +1,11 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; -module('Unit | Controller | orgs/org/users', function(hooks) { +module('Unit | Controller | orgs/org/users', function (hooks) { setupTest(hooks); // Replace this with your real tests. - test('it exists', function(assert) { + test('it exists', function (assert) { let controller = this.owner.lookup('controller:orgs/org/users'); assert.ok(controller); }); diff --git a/ui/core/tests/unit/controllers/orgs/org/users/new-test.js b/ui/core/tests/unit/controllers/orgs/org/users/new-test.js index e6925b4d7..80a7e1890 100644 --- a/ui/core/tests/unit/controllers/orgs/org/users/new-test.js +++ b/ui/core/tests/unit/controllers/orgs/org/users/new-test.js @@ -1,11 +1,11 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; -module('Unit | Controller | orgs/org/users/new', function(hooks) { +module('Unit | Controller | orgs/org/users/new', function (hooks) { setupTest(hooks); // Replace this with your real tests. - test('it exists', function(assert) { + test('it exists', function (assert) { let controller = this.owner.lookup('controller:orgs/org/users/new'); assert.ok(controller); }); diff --git a/ui/core/tests/unit/controllers/orgs/org/users/user-test.js b/ui/core/tests/unit/controllers/orgs/org/users/user-test.js index f35f620fa..e16d4ab6b 100644 --- a/ui/core/tests/unit/controllers/orgs/org/users/user-test.js +++ b/ui/core/tests/unit/controllers/orgs/org/users/user-test.js @@ -1,11 +1,11 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; -module('Unit | Controller | orgs/org/users/user', function(hooks) { +module('Unit | Controller | orgs/org/users/user', function (hooks) { setupTest(hooks); // Replace this with your real tests. - test('it exists', function(assert) { + test('it exists', function (assert) { let controller = this.owner.lookup('controller:orgs/org/users/user'); assert.ok(controller); }); diff --git a/ui/core/tests/unit/controllers/scopes/scope/projects-test.js b/ui/core/tests/unit/controllers/scopes/scope/projects-test.js index 21125f233..052687cd2 100644 --- a/ui/core/tests/unit/controllers/scopes/scope/projects-test.js +++ b/ui/core/tests/unit/controllers/scopes/scope/projects-test.js @@ -1,11 +1,11 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; -module('Unit | Controller | scopes/scope/projects', function(hooks) { +module('Unit | Controller | scopes/scope/projects', function (hooks) { setupTest(hooks); // Replace this with your real tests. - test('it exists', function(assert) { + test('it exists', function (assert) { let controller = this.owner.lookup('controller:scopes/scope/projects'); assert.ok(controller); }); diff --git a/ui/core/tests/unit/controllers/scopes/scope/projects/new-test.js b/ui/core/tests/unit/controllers/scopes/scope/projects/new-test.js index eeb857274..8da2a6f6d 100644 --- a/ui/core/tests/unit/controllers/scopes/scope/projects/new-test.js +++ b/ui/core/tests/unit/controllers/scopes/scope/projects/new-test.js @@ -1,11 +1,11 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; -module('Unit | Controller | scopes/scope/projects/new', function(hooks) { +module('Unit | Controller | scopes/scope/projects/new', function (hooks) { setupTest(hooks); // Replace this with your real tests. - test('it exists', function(assert) { + test('it exists', function (assert) { let controller = this.owner.lookup('controller:scopes/scope/projects/new'); assert.ok(controller); }); diff --git a/ui/core/tests/unit/controllers/scopes/scope/projects/project-test.js b/ui/core/tests/unit/controllers/scopes/scope/projects/project-test.js index 26a255430..ee20af93d 100644 --- a/ui/core/tests/unit/controllers/scopes/scope/projects/project-test.js +++ b/ui/core/tests/unit/controllers/scopes/scope/projects/project-test.js @@ -1,12 +1,14 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; -module('Unit | Controller | scopes/scope/projects/project', function(hooks) { +module('Unit | Controller | scopes/scope/projects/project', function (hooks) { setupTest(hooks); // Replace this with your real tests. - test('it exists', function(assert) { - let controller = this.owner.lookup('controller:scopes/scope/projects/project'); + test('it exists', function (assert) { + let controller = this.owner.lookup( + 'controller:scopes/scope/projects/project' + ); assert.ok(controller); }); }); diff --git a/ui/core/tests/unit/routes/orgs/org/groups-test.js b/ui/core/tests/unit/routes/orgs/org/groups-test.js index a56f69e76..465afedcd 100644 --- a/ui/core/tests/unit/routes/orgs/org/groups-test.js +++ b/ui/core/tests/unit/routes/orgs/org/groups-test.js @@ -1,10 +1,10 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; -module('Unit | Route | orgs/org/groups', function(hooks) { +module('Unit | Route | orgs/org/groups', function (hooks) { setupTest(hooks); - test('it exists', function(assert) { + test('it exists', function (assert) { let route = this.owner.lookup('route:orgs/org/groups'); assert.ok(route); }); diff --git a/ui/core/tests/unit/routes/orgs/org/groups/group-test.js b/ui/core/tests/unit/routes/orgs/org/groups/group-test.js index cec8245a8..ca6107bc1 100644 --- a/ui/core/tests/unit/routes/orgs/org/groups/group-test.js +++ b/ui/core/tests/unit/routes/orgs/org/groups/group-test.js @@ -1,10 +1,10 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; -module('Unit | Route | orgs/org/groups/group', function(hooks) { +module('Unit | Route | orgs/org/groups/group', function (hooks) { setupTest(hooks); - test('it exists', function(assert) { + test('it exists', function (assert) { let route = this.owner.lookup('route:orgs/org/groups/group'); assert.ok(route); }); diff --git a/ui/core/tests/unit/routes/orgs/org/groups/group/index-test.js b/ui/core/tests/unit/routes/orgs/org/groups/group/index-test.js index 2496b24bf..e0c5c06d4 100644 --- a/ui/core/tests/unit/routes/orgs/org/groups/group/index-test.js +++ b/ui/core/tests/unit/routes/orgs/org/groups/group/index-test.js @@ -1,10 +1,10 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; -module('Unit | Route | orgs/org/groups/group/index', function(hooks) { +module('Unit | Route | orgs/org/groups/group/index', function (hooks) { setupTest(hooks); - test('it exists', function(assert) { + test('it exists', function (assert) { let route = this.owner.lookup('route:orgs/org/groups/group/index'); assert.ok(route); }); diff --git a/ui/core/tests/unit/routes/orgs/org/groups/index-test.js b/ui/core/tests/unit/routes/orgs/org/groups/index-test.js index c2f9135eb..60c10cfef 100644 --- a/ui/core/tests/unit/routes/orgs/org/groups/index-test.js +++ b/ui/core/tests/unit/routes/orgs/org/groups/index-test.js @@ -1,10 +1,10 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; -module('Unit | Route | orgs/org/groups/index', function(hooks) { +module('Unit | Route | orgs/org/groups/index', function (hooks) { setupTest(hooks); - test('it exists', function(assert) { + test('it exists', function (assert) { let route = this.owner.lookup('route:orgs/org/groups/index'); assert.ok(route); }); diff --git a/ui/core/tests/unit/routes/orgs/org/groups/new-test.js b/ui/core/tests/unit/routes/orgs/org/groups/new-test.js index 09f10c831..e119c3d53 100644 --- a/ui/core/tests/unit/routes/orgs/org/groups/new-test.js +++ b/ui/core/tests/unit/routes/orgs/org/groups/new-test.js @@ -1,10 +1,10 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; -module('Unit | Route | orgs/org/groups/new', function(hooks) { +module('Unit | Route | orgs/org/groups/new', function (hooks) { setupTest(hooks); - test('it exists', function(assert) { + test('it exists', function (assert) { let route = this.owner.lookup('route:orgs/org/groups/new'); assert.ok(route); }); diff --git a/ui/core/tests/unit/routes/orgs/org/projects/project/host-catalogs-test.js b/ui/core/tests/unit/routes/orgs/org/projects/project/host-catalogs-test.js index f7c144429..761c0bfa8 100644 --- a/ui/core/tests/unit/routes/orgs/org/projects/project/host-catalogs-test.js +++ b/ui/core/tests/unit/routes/orgs/org/projects/project/host-catalogs-test.js @@ -1,11 +1,15 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; -module('Unit | Route | orgs/org/projects/project/host-catalogs', function(hooks) { +module('Unit | Route | orgs/org/projects/project/host-catalogs', function ( + hooks +) { setupTest(hooks); - test('it exists', function(assert) { - let route = this.owner.lookup('route:orgs/org/projects/project/host-catalogs'); + test('it exists', function (assert) { + let route = this.owner.lookup( + 'route:orgs/org/projects/project/host-catalogs' + ); assert.ok(route); }); }); diff --git a/ui/core/tests/unit/routes/orgs/org/projects/project/host-catalogs/host-catalog-test.js b/ui/core/tests/unit/routes/orgs/org/projects/project/host-catalogs/host-catalog-test.js index 6313bc8f0..5dd167f8a 100644 --- a/ui/core/tests/unit/routes/orgs/org/projects/project/host-catalogs/host-catalog-test.js +++ b/ui/core/tests/unit/routes/orgs/org/projects/project/host-catalogs/host-catalog-test.js @@ -1,11 +1,16 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; -module('Unit | Route | orgs/org/projects/project/host-catalogs/host-catalog', function(hooks) { - setupTest(hooks); +module( + 'Unit | Route | orgs/org/projects/project/host-catalogs/host-catalog', + function (hooks) { + setupTest(hooks); - test('it exists', function(assert) { - let route = this.owner.lookup('route:orgs/org/projects/project/host-catalogs/host-catalog'); - assert.ok(route); - }); -}); + test('it exists', function (assert) { + let route = this.owner.lookup( + 'route:orgs/org/projects/project/host-catalogs/host-catalog' + ); + assert.ok(route); + }); + } +); diff --git a/ui/core/tests/unit/routes/orgs/org/projects/project/host-catalogs/host-catalog/index-test.js b/ui/core/tests/unit/routes/orgs/org/projects/project/host-catalogs/host-catalog/index-test.js index a19bd70e9..1472a5b92 100644 --- a/ui/core/tests/unit/routes/orgs/org/projects/project/host-catalogs/host-catalog/index-test.js +++ b/ui/core/tests/unit/routes/orgs/org/projects/project/host-catalogs/host-catalog/index-test.js @@ -1,11 +1,16 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; -module('Unit | Route | orgs/org/projects/project/host-catalogs/host-catalog/index', function(hooks) { - setupTest(hooks); +module( + 'Unit | Route | orgs/org/projects/project/host-catalogs/host-catalog/index', + function (hooks) { + setupTest(hooks); - test('it exists', function(assert) { - let route = this.owner.lookup('route:orgs/org/projects/project/host-catalogs/host-catalog/index'); - assert.ok(route); - }); -}); + test('it exists', function (assert) { + let route = this.owner.lookup( + 'route:orgs/org/projects/project/host-catalogs/host-catalog/index' + ); + assert.ok(route); + }); + } +); diff --git a/ui/core/tests/unit/routes/orgs/org/projects/project/host-catalogs/index-test.js b/ui/core/tests/unit/routes/orgs/org/projects/project/host-catalogs/index-test.js index d8a8d036e..0d7a5e317 100644 --- a/ui/core/tests/unit/routes/orgs/org/projects/project/host-catalogs/index-test.js +++ b/ui/core/tests/unit/routes/orgs/org/projects/project/host-catalogs/index-test.js @@ -1,11 +1,16 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; -module('Unit | Route | orgs/org/projects/project/host-catalogs/index', function(hooks) { - setupTest(hooks); +module( + 'Unit | Route | orgs/org/projects/project/host-catalogs/index', + function (hooks) { + setupTest(hooks); - test('it exists', function(assert) { - let route = this.owner.lookup('route:orgs/org/projects/project/host-catalogs/index'); - assert.ok(route); - }); -}); + test('it exists', function (assert) { + let route = this.owner.lookup( + 'route:orgs/org/projects/project/host-catalogs/index' + ); + assert.ok(route); + }); + } +); diff --git a/ui/core/tests/unit/routes/orgs/org/projects/project/host-catalogs/new-test.js b/ui/core/tests/unit/routes/orgs/org/projects/project/host-catalogs/new-test.js index 2529656b7..8b441cfd6 100644 --- a/ui/core/tests/unit/routes/orgs/org/projects/project/host-catalogs/new-test.js +++ b/ui/core/tests/unit/routes/orgs/org/projects/project/host-catalogs/new-test.js @@ -1,11 +1,15 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; -module('Unit | Route | orgs/org/projects/project/host-catalogs/new', function(hooks) { +module('Unit | Route | orgs/org/projects/project/host-catalogs/new', function ( + hooks +) { setupTest(hooks); - test('it exists', function(assert) { - let route = this.owner.lookup('route:orgs/org/projects/project/host-catalogs/new'); + test('it exists', function (assert) { + let route = this.owner.lookup( + 'route:orgs/org/projects/project/host-catalogs/new' + ); assert.ok(route); }); }); diff --git a/ui/core/tests/unit/routes/orgs/org/projects/project/index-test.js b/ui/core/tests/unit/routes/orgs/org/projects/project/index-test.js index bc1d36103..08f446f6f 100644 --- a/ui/core/tests/unit/routes/orgs/org/projects/project/index-test.js +++ b/ui/core/tests/unit/routes/orgs/org/projects/project/index-test.js @@ -1,10 +1,10 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; -module('Unit | Route | orgs/org/projects/project/index', function(hooks) { +module('Unit | Route | orgs/org/projects/project/index', function (hooks) { setupTest(hooks); - test('it exists', function(assert) { + test('it exists', function (assert) { let route = this.owner.lookup('route:orgs/org/projects/project/index'); assert.ok(route); }); diff --git a/ui/core/tests/unit/routes/orgs/org/roles-test.js b/ui/core/tests/unit/routes/orgs/org/roles-test.js index 38bc3acb2..7cd35dab6 100644 --- a/ui/core/tests/unit/routes/orgs/org/roles-test.js +++ b/ui/core/tests/unit/routes/orgs/org/roles-test.js @@ -1,10 +1,10 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; -module('Unit | Route | orgs/org/roles', function(hooks) { +module('Unit | Route | orgs/org/roles', function (hooks) { setupTest(hooks); - test('it exists', function(assert) { + test('it exists', function (assert) { let route = this.owner.lookup('route:orgs/org/roles'); assert.ok(route); }); diff --git a/ui/core/tests/unit/routes/orgs/org/roles/index-test.js b/ui/core/tests/unit/routes/orgs/org/roles/index-test.js index 12f247adb..979d26062 100644 --- a/ui/core/tests/unit/routes/orgs/org/roles/index-test.js +++ b/ui/core/tests/unit/routes/orgs/org/roles/index-test.js @@ -1,10 +1,10 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; -module('Unit | Route | orgs/org/roles/index', function(hooks) { +module('Unit | Route | orgs/org/roles/index', function (hooks) { setupTest(hooks); - test('it exists', function(assert) { + test('it exists', function (assert) { let route = this.owner.lookup('route:orgs/org/roles/index'); assert.ok(route); }); diff --git a/ui/core/tests/unit/routes/orgs/org/roles/new-test.js b/ui/core/tests/unit/routes/orgs/org/roles/new-test.js index d613eb34a..59e26a949 100644 --- a/ui/core/tests/unit/routes/orgs/org/roles/new-test.js +++ b/ui/core/tests/unit/routes/orgs/org/roles/new-test.js @@ -1,10 +1,10 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; -module('Unit | Route | orgs/org/roles/new', function(hooks) { +module('Unit | Route | orgs/org/roles/new', function (hooks) { setupTest(hooks); - test('it exists', function(assert) { + test('it exists', function (assert) { let route = this.owner.lookup('route:orgs/org/roles/new'); assert.ok(route); }); diff --git a/ui/core/tests/unit/routes/orgs/org/roles/role-test.js b/ui/core/tests/unit/routes/orgs/org/roles/role-test.js index 149fd657e..207a79ea2 100644 --- a/ui/core/tests/unit/routes/orgs/org/roles/role-test.js +++ b/ui/core/tests/unit/routes/orgs/org/roles/role-test.js @@ -1,10 +1,10 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; -module('Unit | Route | orgs/org/roles/role', function(hooks) { +module('Unit | Route | orgs/org/roles/role', function (hooks) { setupTest(hooks); - test('it exists', function(assert) { + test('it exists', function (assert) { let route = this.owner.lookup('route:orgs/org/roles/role'); assert.ok(route); }); diff --git a/ui/core/tests/unit/routes/orgs/org/users-test.js b/ui/core/tests/unit/routes/orgs/org/users-test.js index 93956225b..b4c420690 100644 --- a/ui/core/tests/unit/routes/orgs/org/users-test.js +++ b/ui/core/tests/unit/routes/orgs/org/users-test.js @@ -1,10 +1,10 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; -module('Unit | Route | orgs/org/users', function(hooks) { +module('Unit | Route | orgs/org/users', function (hooks) { setupTest(hooks); - test('it exists', function(assert) { + test('it exists', function (assert) { let route = this.owner.lookup('route:orgs/org/users'); assert.ok(route); }); diff --git a/ui/core/tests/unit/routes/orgs/org/users/index-test.js b/ui/core/tests/unit/routes/orgs/org/users/index-test.js index cd215662e..21310f7a1 100644 --- a/ui/core/tests/unit/routes/orgs/org/users/index-test.js +++ b/ui/core/tests/unit/routes/orgs/org/users/index-test.js @@ -1,10 +1,10 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; -module('Unit | Route | orgs/org/users/index', function(hooks) { +module('Unit | Route | orgs/org/users/index', function (hooks) { setupTest(hooks); - test('it exists', function(assert) { + test('it exists', function (assert) { let route = this.owner.lookup('route:orgs/org/users/index'); assert.ok(route); }); diff --git a/ui/core/tests/unit/routes/orgs/org/users/new-test.js b/ui/core/tests/unit/routes/orgs/org/users/new-test.js index 1aef05bab..395979cad 100644 --- a/ui/core/tests/unit/routes/orgs/org/users/new-test.js +++ b/ui/core/tests/unit/routes/orgs/org/users/new-test.js @@ -1,10 +1,10 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; -module('Unit | Route | orgs/org/users/new', function(hooks) { +module('Unit | Route | orgs/org/users/new', function (hooks) { setupTest(hooks); - test('it exists', function(assert) { + test('it exists', function (assert) { let route = this.owner.lookup('route:orgs/org/users/new'); assert.ok(route); }); diff --git a/ui/core/tests/unit/routes/orgs/org/users/user-test.js b/ui/core/tests/unit/routes/orgs/org/users/user-test.js index 4bd6f26a2..48a657875 100644 --- a/ui/core/tests/unit/routes/orgs/org/users/user-test.js +++ b/ui/core/tests/unit/routes/orgs/org/users/user-test.js @@ -1,10 +1,10 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; -module('Unit | Route | orgs/org/users/user', function(hooks) { +module('Unit | Route | orgs/org/users/user', function (hooks) { setupTest(hooks); - test('it exists', function(assert) { + test('it exists', function (assert) { let route = this.owner.lookup('route:orgs/org/users/user'); assert.ok(route); }); diff --git a/ui/core/tests/unit/routes/scopes-test.js b/ui/core/tests/unit/routes/scopes-test.js index 06a5af092..914469429 100644 --- a/ui/core/tests/unit/routes/scopes-test.js +++ b/ui/core/tests/unit/routes/scopes-test.js @@ -1,10 +1,10 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; -module('Unit | Route | scopes', function(hooks) { +module('Unit | Route | scopes', function (hooks) { setupTest(hooks); - test('it exists', function(assert) { + test('it exists', function (assert) { let route = this.owner.lookup('route:scopes'); assert.ok(route); }); diff --git a/ui/core/tests/unit/routes/scopes/scope-test.js b/ui/core/tests/unit/routes/scopes/scope-test.js index 62c97d9f0..3cdcf409a 100644 --- a/ui/core/tests/unit/routes/scopes/scope-test.js +++ b/ui/core/tests/unit/routes/scopes/scope-test.js @@ -1,10 +1,10 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; -module('Unit | Route | scopes/scope', function(hooks) { +module('Unit | Route | scopes/scope', function (hooks) { setupTest(hooks); - test('it exists', function(assert) { + test('it exists', function (assert) { let route = this.owner.lookup('route:scopes/scope'); assert.ok(route); }); diff --git a/ui/core/tests/unit/routes/scopes/scope/projects-test.js b/ui/core/tests/unit/routes/scopes/scope/projects-test.js index 4a8dde55f..127213dac 100644 --- a/ui/core/tests/unit/routes/scopes/scope/projects-test.js +++ b/ui/core/tests/unit/routes/scopes/scope/projects-test.js @@ -1,10 +1,10 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; -module('Unit | Route | scopes/scope/projects', function(hooks) { +module('Unit | Route | scopes/scope/projects', function (hooks) { setupTest(hooks); - test('it exists', function(assert) { + test('it exists', function (assert) { let route = this.owner.lookup('route:scopes/scope/projects'); assert.ok(route); }); diff --git a/ui/core/tests/unit/routes/scopes/scope/projects/index-test.js b/ui/core/tests/unit/routes/scopes/scope/projects/index-test.js index 71c813a35..74a15f224 100644 --- a/ui/core/tests/unit/routes/scopes/scope/projects/index-test.js +++ b/ui/core/tests/unit/routes/scopes/scope/projects/index-test.js @@ -1,10 +1,10 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; -module('Unit | Route | scopes/scope/projects/index', function(hooks) { +module('Unit | Route | scopes/scope/projects/index', function (hooks) { setupTest(hooks); - test('it exists', function(assert) { + test('it exists', function (assert) { let route = this.owner.lookup('route:scopes/scope/projects/index'); assert.ok(route); }); diff --git a/ui/core/tests/unit/routes/scopes/scope/projects/new-test.js b/ui/core/tests/unit/routes/scopes/scope/projects/new-test.js index 6d22e1fa7..8c28e87b5 100644 --- a/ui/core/tests/unit/routes/scopes/scope/projects/new-test.js +++ b/ui/core/tests/unit/routes/scopes/scope/projects/new-test.js @@ -1,10 +1,10 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; -module('Unit | Route | scopes/scope/projects/new', function(hooks) { +module('Unit | Route | scopes/scope/projects/new', function (hooks) { setupTest(hooks); - test('it exists', function(assert) { + test('it exists', function (assert) { let route = this.owner.lookup('route:scopes/scope/projects/new'); assert.ok(route); }); diff --git a/ui/core/tests/unit/routes/scopes/scope/projects/project-test.js b/ui/core/tests/unit/routes/scopes/scope/projects/project-test.js index 54555a07c..ed0ffe51a 100644 --- a/ui/core/tests/unit/routes/scopes/scope/projects/project-test.js +++ b/ui/core/tests/unit/routes/scopes/scope/projects/project-test.js @@ -1,10 +1,10 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; -module('Unit | Route | scopes/scope/projects/project', function(hooks) { +module('Unit | Route | scopes/scope/projects/project', function (hooks) { setupTest(hooks); - test('it exists', function(assert) { + test('it exists', function (assert) { let route = this.owner.lookup('route:scopes/scope/projects/project'); assert.ok(route); }); diff --git a/ui/core/tests/unit/routes/scopes/scope/projects/project/index-test.js b/ui/core/tests/unit/routes/scopes/scope/projects/project/index-test.js index c81b2f5a7..f14370229 100644 --- a/ui/core/tests/unit/routes/scopes/scope/projects/project/index-test.js +++ b/ui/core/tests/unit/routes/scopes/scope/projects/project/index-test.js @@ -1,10 +1,10 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; -module('Unit | Route | scopes/scope/projects/project/index', function(hooks) { +module('Unit | Route | scopes/scope/projects/project/index', function (hooks) { setupTest(hooks); - test('it exists', function(assert) { + test('it exists', function (assert) { let route = this.owner.lookup('route:scopes/scope/projects/project/index'); assert.ok(route); }); diff --git a/ui/core/tests/unit/routes/scopes/scope/users-test.js b/ui/core/tests/unit/routes/scopes/scope/users-test.js new file mode 100644 index 000000000..12f7813b1 --- /dev/null +++ b/ui/core/tests/unit/routes/scopes/scope/users-test.js @@ -0,0 +1,11 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Route | scopes/scope/users', function (hooks) { + setupTest(hooks); + + test('it exists', function (assert) { + let route = this.owner.lookup('route:scopes/scope/users'); + assert.ok(route); + }); +}); diff --git a/ui/core/tests/unit/routes/scopes/scope/users/index-test.js b/ui/core/tests/unit/routes/scopes/scope/users/index-test.js new file mode 100644 index 000000000..f2c453604 --- /dev/null +++ b/ui/core/tests/unit/routes/scopes/scope/users/index-test.js @@ -0,0 +1,11 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Route | scopes/scope/users/index', function (hooks) { + setupTest(hooks); + + test('it exists', function (assert) { + let route = this.owner.lookup('route:scopes/scope/users/index'); + assert.ok(route); + }); +}); diff --git a/ui/core/tests/unit/routes/scopes/scope/users/new-test.js b/ui/core/tests/unit/routes/scopes/scope/users/new-test.js new file mode 100644 index 000000000..147af3ba2 --- /dev/null +++ b/ui/core/tests/unit/routes/scopes/scope/users/new-test.js @@ -0,0 +1,11 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Route | scopes/scope/users/new', function (hooks) { + setupTest(hooks); + + test('it exists', function (assert) { + let route = this.owner.lookup('route:scopes/scope/users/new'); + assert.ok(route); + }); +}); diff --git a/ui/core/tests/unit/routes/scopes/scope/users/user-test.js b/ui/core/tests/unit/routes/scopes/scope/users/user-test.js new file mode 100644 index 000000000..69f34e847 --- /dev/null +++ b/ui/core/tests/unit/routes/scopes/scope/users/user-test.js @@ -0,0 +1,11 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Route | scopes/scope/users/user', function (hooks) { + setupTest(hooks); + + test('it exists', function (assert) { + let route = this.owner.lookup('route:scopes/scope/users/user'); + assert.ok(route); + }); +}); diff --git a/ui/core/tests/unit/routes/scopes/scope/users/user/index-test.js b/ui/core/tests/unit/routes/scopes/scope/users/user/index-test.js new file mode 100644 index 000000000..21302ee4b --- /dev/null +++ b/ui/core/tests/unit/routes/scopes/scope/users/user/index-test.js @@ -0,0 +1,11 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Route | scopes/scope/users/user/index', function (hooks) { + setupTest(hooks); + + test('it exists', function (assert) { + let route = this.owner.lookup('route:scopes/scope/users/user/index'); + assert.ok(route); + }); +}); From 330a7ceb3b1cc65759e00319a63c19b28b747be6 Mon Sep 17 00:00:00 2001 From: Susmitha Girumala Date: Mon, 27 Jul 2020 14:34:39 -0400 Subject: [PATCH 41/70] =?UTF-8?q?chore:=20=F0=9F=A4=96=20Update=20deauthen?= =?UTF-8?q?ticate=20params=20to=20still=20use=20org=5Fid?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of session invalidation being provided: { scope: { id: scopeID } }, it gets: {authenticator: "authenticator:password", org_id: "1"}. --- ui/core/app/authenticators/password.js | 2 +- ui/core/translations/en-us.yaml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/ui/core/app/authenticators/password.js b/ui/core/app/authenticators/password.js index 3224130a3..bd2011911 100644 --- a/ui/core/app/authenticators/password.js +++ b/ui/core/app/authenticators/password.js @@ -38,7 +38,7 @@ export default class PasswordAuthenticator extends BasePasswordAuthenticator { * @param {string} scopeID * @return {string} */ - buildDeauthEndpointURL({ scope: { id: scopeID } }) { + buildDeauthEndpointURL({ authenticator, org_id: scopeID }) { const adapter = this.store.adapterFor('application'); const options = { adapterOptions: { method: 'deauthenticate' } }; return adapter.buildURL('scope', scopeID, options, 'findRecord'); diff --git a/ui/core/translations/en-us.yaml b/ui/core/translations/en-us.yaml index e770de25c..f01e4cf72 100644 --- a/ui/core/translations/en-us.yaml +++ b/ui/core/translations/en-us.yaml @@ -4,6 +4,7 @@ actions: dismiss: Dismiss save: Save help: Need help? + deauthenticate: Deauthenticate resources: user: User users: Users From 356c02b3658ada49984c203896f181fea55fd99b Mon Sep 17 00:00:00 2001 From: Susmitha Girumala Date: Mon, 27 Jul 2020 14:48:33 -0400 Subject: [PATCH 42/70] =?UTF-8?q?chore:=20=F0=9F=A4=96=20Show=20sidebar=20?= =?UTF-8?q?only=20when=20authenticated?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ui/core/app/templates/scopes/scope.hbs | 46 ++++++++++++++------------ 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/ui/core/app/templates/scopes/scope.hbs b/ui/core/app/templates/scopes/scope.hbs index 2d1a73607..276ca4835 100644 --- a/ui/core/app/templates/scopes/scope.hbs +++ b/ui/core/app/templates/scopes/scope.hbs @@ -1,32 +1,34 @@ + {{#if this.session.isAuthenticated}} + - + + + {{t "resources.projects"}} + + - - - {{t "resources.projects"}} - - + {{outlet "sidebar"}} - {{outlet "sidebar"}} - - {{!-- - - - {{t "resources.users"}} - - - {{t "resources.groups"}} - + {{!-- + + + {{t "resources.users"}} + - - {{t "resources.roles"}} - - - --}} + + {{t "resources.groups"}} + - + + {{t "resources.roles"}} + +
+ --}} + + + {{/if}}
From 3f1a7f155c2f57dc7034b57e3c52f5eda0bfdc68 Mon Sep 17 00:00:00 2001 From: Susmitha Girumala Date: Mon, 27 Jul 2020 14:49:42 -0400 Subject: [PATCH 43/70] =?UTF-8?q?chore:=20=F0=9F=A4=96=20Port=20authentica?= =?UTF-8?q?te=20template=20from=20pre-scope=20template?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../templates/scopes/scope/authenticate.hbs | 6 ++++- .../scopes/scope/authenticate/index.hbs | 1 - .../scopes/scope/authenticate/method.hbs | 26 +++++++++++++++++++ ui/core/translations/en-us.yaml | 16 ++++++++++++ 4 files changed, 47 insertions(+), 2 deletions(-) diff --git a/ui/core/app/templates/scopes/scope/authenticate.hbs b/ui/core/app/templates/scopes/scope/authenticate.hbs index e2147cab0..5471432d9 100644 --- a/ui/core/app/templates/scopes/scope/authenticate.hbs +++ b/ui/core/app/templates/scopes/scope/authenticate.hbs @@ -1 +1,5 @@ -{{outlet}} \ No newline at end of file + +
+ {{outlet}} +
+
diff --git a/ui/core/app/templates/scopes/scope/authenticate/index.hbs b/ui/core/app/templates/scopes/scope/authenticate/index.hbs index e2147cab0..e69de29bb 100644 --- a/ui/core/app/templates/scopes/scope/authenticate/index.hbs +++ b/ui/core/app/templates/scopes/scope/authenticate/index.hbs @@ -1 +0,0 @@ -{{outlet}} \ No newline at end of file diff --git a/ui/core/app/templates/scopes/scope/authenticate/method.hbs b/ui/core/app/templates/scopes/scope/authenticate/method.hbs index 60057d773..c6e4157e2 100644 --- a/ui/core/app/templates/scopes/scope/authenticate/method.hbs +++ b/ui/core/app/templates/scopes/scope/authenticate/method.hbs @@ -1,3 +1,29 @@ +{{#if @model}} +

{{t 'actions.authenticate'}}

+

{{t "auth.descriptions.choose-org"}}

+ + {{#each @model as |org|}} + + {{org.displayName}} + + {{/each}} + +{{/if}} + +{{#unless @model}} + + + {{t "auth.descriptions.no-orgs"}} + + +{{/unless}} + Date: Mon, 27 Jul 2020 16:19:08 -0400 Subject: [PATCH 44/70] =?UTF-8?q?chore:=20=F0=9F=A4=96=20Update=20auth=20t?= =?UTF-8?q?emplates=20under=20scopes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ui/core/app/authenticators/password.js | 2 +- ui/core/app/router.js | 1 - ui/core/app/routes/scopes/scope.js | 1 - ui/core/app/templates/scopes/scope.hbs | 45 ++++++++-------- .../templates/scopes/scope/authenticate.hbs | 53 +++++++++++++++++-- .../scopes/scope/authenticate/index.hbs | 12 +++++ .../scopes/scope/authenticate/method.hbs | 26 --------- ui/core/app/templates/scopes/scope/index.hbs | 2 +- ui/core/mirage/scenarios/default.js | 11 ++++ 9 files changed, 96 insertions(+), 57 deletions(-) diff --git a/ui/core/app/authenticators/password.js b/ui/core/app/authenticators/password.js index bd2011911..41e75d078 100644 --- a/ui/core/app/authenticators/password.js +++ b/ui/core/app/authenticators/password.js @@ -38,7 +38,7 @@ export default class PasswordAuthenticator extends BasePasswordAuthenticator { * @param {string} scopeID * @return {string} */ - buildDeauthEndpointURL({ authenticator, org_id: scopeID }) { + buildDeauthEndpointURL({ org_id: scopeID }) { const adapter = this.store.adapterFor('application'); const options = { adapterOptions: { method: 'deauthenticate' } }; return adapter.buildURL('scope', scopeID, options, 'findRecord'); diff --git a/ui/core/app/router.js b/ui/core/app/router.js index 3c95ac355..ef3536c60 100644 --- a/ui/core/app/router.js +++ b/ui/core/app/router.js @@ -18,7 +18,6 @@ Router.map(function () { this.route('new'); }); }); - this.route('cope'); }); this.route('orgs', function () { diff --git a/ui/core/app/routes/scopes/scope.js b/ui/core/app/routes/scopes/scope.js index c9ecb358f..939ef5076 100644 --- a/ui/core/app/routes/scopes/scope.js +++ b/ui/core/app/routes/scopes/scope.js @@ -23,5 +23,4 @@ export default class ScopesScopeRoute extends Route { return maybeExistingScope || this.store.createRecord('scope', { id }); }); } - } diff --git a/ui/core/app/templates/scopes/scope.hbs b/ui/core/app/templates/scopes/scope.hbs index 276ca4835..35287c253 100644 --- a/ui/core/app/templates/scopes/scope.hbs +++ b/ui/core/app/templates/scopes/scope.hbs @@ -1,34 +1,31 @@ - {{#if this.session.isAuthenticated}} - - - - {{t "resources.projects"}} - - + + + + {{t "resources.projects"}} + + - {{outlet "sidebar"}} + {{outlet "sidebar"}} - {{!-- - - - {{t "resources.users"}} - + {{!-- + + + {{t "resources.users"}} + - - {{t "resources.groups"}} - + + {{t "resources.groups"}} + - - {{t "resources.roles"}} - - - --}} - - - {{/if}} + + {{t "resources.roles"}} + + + --}} +
diff --git a/ui/core/app/templates/scopes/scope/authenticate.hbs b/ui/core/app/templates/scopes/scope/authenticate.hbs index 5471432d9..c0e8c5485 100644 --- a/ui/core/app/templates/scopes/scope/authenticate.hbs +++ b/ui/core/app/templates/scopes/scope/authenticate.hbs @@ -1,5 +1,52 @@ -
- {{outlet}} -
+ + + +

{{t 'actions.authenticate'}}

+

{{t 'auth.descriptions.authenticating-with-org' name=@model.scope.displayName}}

+
+ + + + + {{#each @model.scopes as |scope|}} + + {{scope.displayName}} + + {{/each}} + + + + + + {{#each @model.authMethods as |authMethod|}} + + {{t (concat 'auth.method-types.' authMethod.type)}} + + {{/each}} + + + + + {{#if @model.authMethods}} + {{outlet}} + {{/if}} + {{#unless @model.authMethods}} + + + + {{t "auth.descriptions.no-methods"}} + + + + {{/unless}} + + +
diff --git a/ui/core/app/templates/scopes/scope/authenticate/index.hbs b/ui/core/app/templates/scopes/scope/authenticate/index.hbs index e69de29bb..22cc46f4e 100644 --- a/ui/core/app/templates/scopes/scope/authenticate/index.hbs +++ b/ui/core/app/templates/scopes/scope/authenticate/index.hbs @@ -0,0 +1,12 @@ +{{#unless @model}} + + + + {{t "auth.descriptions.no-methods"}} + + + +{{/unless}} diff --git a/ui/core/app/templates/scopes/scope/authenticate/method.hbs b/ui/core/app/templates/scopes/scope/authenticate/method.hbs index c6e4157e2..60057d773 100644 --- a/ui/core/app/templates/scopes/scope/authenticate/method.hbs +++ b/ui/core/app/templates/scopes/scope/authenticate/method.hbs @@ -1,29 +1,3 @@ -{{#if @model}} -

{{t 'actions.authenticate'}}

-

{{t "auth.descriptions.choose-org"}}

- - {{#each @model as |org|}} - - {{org.displayName}} - - {{/each}} - -{{/if}} - -{{#unless @model}} - - - {{t "auth.descriptions.no-orgs"}} - - -{{/unless}} - Date: Mon, 27 Jul 2020 16:42:02 -0400 Subject: [PATCH 45/70] =?UTF-8?q?chore:=20=F0=9F=A4=96=20Refactor=20group?= =?UTF-8?q?=20CRUD+L=20to=20/scopes/:scope=5Fid?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ui/core/app/router.js | 4 ++ ui/core/app/routes/scopes/scope/groups.js | 72 +++++++++++++++++++ .../app/routes/scopes/scope/groups/group.js | 17 +++++ .../routes/scopes/scope/groups/group/index.js | 3 + .../app/routes/scopes/scope/groups/index.js | 3 + ui/core/app/routes/scopes/scope/groups/new.js | 13 ++++ ui/core/app/routes/scopes/scope/users.js | 4 +- ui/core/app/templates/scopes/scope.hbs | 3 + ui/core/app/templates/scopes/scope/groups.hbs | 3 + .../templates/scopes/scope/groups/group.hbs | 3 + .../scopes/scope/groups/group/index.hbs | 35 +++++++++ .../templates/scopes/scope/groups/index.hbs | 63 ++++++++++++++++ .../app/templates/scopes/scope/groups/new.hbs | 21 ++++++ .../unit/routes/scopes/scope/groups-test.js | 11 +++ .../routes/scopes/scope/groups/group-test.js | 11 +++ .../scopes/scope/groups/group/index-test.js | 11 +++ .../routes/scopes/scope/groups/index-test.js | 11 +++ .../routes/scopes/scope/groups/new-test.js | 11 +++ 18 files changed, 297 insertions(+), 2 deletions(-) create mode 100644 ui/core/app/routes/scopes/scope/groups.js create mode 100644 ui/core/app/routes/scopes/scope/groups/group.js create mode 100644 ui/core/app/routes/scopes/scope/groups/group/index.js create mode 100644 ui/core/app/routes/scopes/scope/groups/index.js create mode 100644 ui/core/app/routes/scopes/scope/groups/new.js create mode 100644 ui/core/app/templates/scopes/scope/groups.hbs create mode 100644 ui/core/app/templates/scopes/scope/groups/group.hbs create mode 100644 ui/core/app/templates/scopes/scope/groups/group/index.hbs create mode 100644 ui/core/app/templates/scopes/scope/groups/index.hbs create mode 100644 ui/core/app/templates/scopes/scope/groups/new.hbs create mode 100644 ui/core/tests/unit/routes/scopes/scope/groups-test.js create mode 100644 ui/core/tests/unit/routes/scopes/scope/groups/group-test.js create mode 100644 ui/core/tests/unit/routes/scopes/scope/groups/group/index-test.js create mode 100644 ui/core/tests/unit/routes/scopes/scope/groups/index-test.js create mode 100644 ui/core/tests/unit/routes/scopes/scope/groups/new-test.js diff --git a/ui/core/app/router.js b/ui/core/app/router.js index 1c3e3e78b..0e7e5f7f7 100644 --- a/ui/core/app/router.js +++ b/ui/core/app/router.js @@ -17,6 +17,10 @@ Router.map(function () { this.route('user', { path: ':user_id' }, function () {}); this.route('new'); }); + this.route('groups', function () { + this.route('group', { path: ':group_id' }, function () {}); + this.route('new'); + }); }); }); diff --git a/ui/core/app/routes/scopes/scope/groups.js b/ui/core/app/routes/scopes/scope/groups.js new file mode 100644 index 000000000..0344e53f1 --- /dev/null +++ b/ui/core/app/routes/scopes/scope/groups.js @@ -0,0 +1,72 @@ +import Route from '@ember/routing/route'; +import { inject as service } from '@ember/service'; +import { action } from '@ember/object'; + +export default class ScopesScopeGroupsRoute extends Route { + // =services + + @service intl; + @service notify; + + // =methods + + /** + * Load all groups under current scope + * @return {Promise[GroupModel]} + */ + async model() { + return this.store.findAll('group', this.scopeAdapterOptions()); + } + + // =actions + /** + * Rollback changes on a group. + * @param {GroupModel} group + */ + @action + cancel(group) { + const { isNew } = group; + group.rollbackAttributes(); + if (isNew) this.transitionTo('scopes.scope.groups'); + } + + /** + * Save a group in current scope. + * @param {GroupModel} group + */ + @action + async save(group) { + try { + await group.save(this.scopeAdapterOptions()); + this.refresh(); + this.notify.success(this.intl.t('notify.save-success')); + this.transitionTo('scopes.scope.groups.group', group); + } catch (error) { + //TODO: replace with translated strings + this.notify.error(error.message, { closeAfter: null }); + } + } + + /** + * Delete a group in current scope and redirect to index + * @param {GroupModel} group + */ + @action + async delete(group) { + try { + await group.destroyRecord(this.scopeAdapterOptions()); + this.refresh(); + this.transitionTo('scopes.scope.groups'); + this.notify.success(this.intl.t('notify.group.delete-success')); + } catch (error) { + //TODO: replace with translated strings + this.notify.error(error.message, { closeAfter: null }); + } + } + + scopeAdapterOptions() { + const { id: scope_id } = this.modelFor('scopes.scope'); + const adapterOptions = { scopeID: scope_id }; + return { adapterOptions }; + } +} diff --git a/ui/core/app/routes/scopes/scope/groups/group.js b/ui/core/app/routes/scopes/scope/groups/group.js new file mode 100644 index 000000000..da536ed07 --- /dev/null +++ b/ui/core/app/routes/scopes/scope/groups/group.js @@ -0,0 +1,17 @@ +import Route from '@ember/routing/route'; + +export default class ScopesScopeGroupsGroupRoute extends Route { + // =methods + + /** + * Load a group in current scope. + * @param {object} params + * @param {string} params.group_id + * @return {groupModel} + */ + async model({ group_id }) { + const { id: scope_id } = this.modelFor('scopes.scope'); + const adapterOptions = { scopeID: scope_id }; + return this.store.findRecord('group', group_id, { adapterOptions }); + } +} diff --git a/ui/core/app/routes/scopes/scope/groups/group/index.js b/ui/core/app/routes/scopes/scope/groups/group/index.js new file mode 100644 index 000000000..90f0fb3a1 --- /dev/null +++ b/ui/core/app/routes/scopes/scope/groups/group/index.js @@ -0,0 +1,3 @@ +import Route from '@ember/routing/route'; + +export default class ScopesScopeGroupsGroupIndexRoute extends Route {} diff --git a/ui/core/app/routes/scopes/scope/groups/index.js b/ui/core/app/routes/scopes/scope/groups/index.js new file mode 100644 index 000000000..7d2a20fb0 --- /dev/null +++ b/ui/core/app/routes/scopes/scope/groups/index.js @@ -0,0 +1,3 @@ +import Route from '@ember/routing/route'; + +export default class ScopesScopeGroupsIndexRoute extends Route {} diff --git a/ui/core/app/routes/scopes/scope/groups/new.js b/ui/core/app/routes/scopes/scope/groups/new.js new file mode 100644 index 000000000..e33233f5f --- /dev/null +++ b/ui/core/app/routes/scopes/scope/groups/new.js @@ -0,0 +1,13 @@ +import Route from '@ember/routing/route'; + +export default class ScopesScopeGroupsNewRoute extends Route { + // =methods + + /** + * Creates a new unsaved group. + * @return {GroupModel} + */ + model() { + return this.store.createRecord('group'); + } +} diff --git a/ui/core/app/routes/scopes/scope/users.js b/ui/core/app/routes/scopes/scope/users.js index 2720f56b1..bf0ba95c4 100644 --- a/ui/core/app/routes/scopes/scope/users.js +++ b/ui/core/app/routes/scopes/scope/users.js @@ -20,7 +20,7 @@ export default class ScopesScopeUsersRoute extends Route { // =actions /** - * Rollback changes on user. + * Rollback changes on an user. * @param {UserModel} user */ @action @@ -31,7 +31,7 @@ export default class ScopesScopeUsersRoute extends Route { } /** - * Save a user in current scope. + * Save an user in current scope. * @param {UserModel} user * @param {Event} e */ diff --git a/ui/core/app/templates/scopes/scope.hbs b/ui/core/app/templates/scopes/scope.hbs index ee969738f..f3199821b 100644 --- a/ui/core/app/templates/scopes/scope.hbs +++ b/ui/core/app/templates/scopes/scope.hbs @@ -12,6 +12,9 @@ {{t "resources.users"}} + + {{t "resources.groups"}} + {{outlet "sidebar"}} diff --git a/ui/core/app/templates/scopes/scope/groups.hbs b/ui/core/app/templates/scopes/scope/groups.hbs new file mode 100644 index 000000000..c53da7f4b --- /dev/null +++ b/ui/core/app/templates/scopes/scope/groups.hbs @@ -0,0 +1,3 @@ +{{title (t "resources.groups")}} + +{{outlet}} diff --git a/ui/core/app/templates/scopes/scope/groups/group.hbs b/ui/core/app/templates/scopes/scope/groups/group.hbs new file mode 100644 index 000000000..d66e83390 --- /dev/null +++ b/ui/core/app/templates/scopes/scope/groups/group.hbs @@ -0,0 +1,3 @@ +{{title @model.displayName}} + +{{outlet}} diff --git a/ui/core/app/templates/scopes/scope/groups/group/index.hbs b/ui/core/app/templates/scopes/scope/groups/group/index.hbs new file mode 100644 index 000000000..ac8915f59 --- /dev/null +++ b/ui/core/app/templates/scopes/scope/groups/group/index.hbs @@ -0,0 +1,35 @@ + + + + + + + +

{{t "resources.group"}}

+
+ + + + + {{t 'titles.details'}} + + + + + + + + + {{t 'actions.delete'}} + + + +
diff --git a/ui/core/app/templates/scopes/scope/groups/index.hbs b/ui/core/app/templates/scopes/scope/groups/index.hbs new file mode 100644 index 000000000..cddaa478b --- /dev/null +++ b/ui/core/app/templates/scopes/scope/groups/index.hbs @@ -0,0 +1,63 @@ + + + + + + + +

{{t "resources.groups"}}

+
+ + + {{#if @model}} + {{t 'titles.new'}} + {{/if}} + + + + + {{#if @model}} + + + + + {{t 'form.label.name'}} + {{t 'form.label.description'}} + + + + {{#each @model as |group|}} + + + + {{group.name}} + + + {{group.description}} + + {{/each}} + + + + {{else}} + + + + + {{t 'descriptions.groups-welcome'}} + + + + {{t 'titles.new'}} + + + + + {{/if}} + + + +
diff --git a/ui/core/app/templates/scopes/scope/groups/new.hbs b/ui/core/app/templates/scopes/scope/groups/new.hbs new file mode 100644 index 000000000..b8f782b23 --- /dev/null +++ b/ui/core/app/templates/scopes/scope/groups/new.hbs @@ -0,0 +1,21 @@ +{{title (t 'titles.new')}} + + + + + + + + +

{{t "titles.new"}}

+
+ + + + + +
diff --git a/ui/core/tests/unit/routes/scopes/scope/groups-test.js b/ui/core/tests/unit/routes/scopes/scope/groups-test.js new file mode 100644 index 000000000..879a3925b --- /dev/null +++ b/ui/core/tests/unit/routes/scopes/scope/groups-test.js @@ -0,0 +1,11 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Route | scopes/scope/groups', function (hooks) { + setupTest(hooks); + + test('it exists', function (assert) { + let route = this.owner.lookup('route:scopes/scope/groups'); + assert.ok(route); + }); +}); diff --git a/ui/core/tests/unit/routes/scopes/scope/groups/group-test.js b/ui/core/tests/unit/routes/scopes/scope/groups/group-test.js new file mode 100644 index 000000000..8f8cc6713 --- /dev/null +++ b/ui/core/tests/unit/routes/scopes/scope/groups/group-test.js @@ -0,0 +1,11 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Route | scopes/scope/groups/group', function (hooks) { + setupTest(hooks); + + test('it exists', function (assert) { + let route = this.owner.lookup('route:scopes/scope/groups/group'); + assert.ok(route); + }); +}); diff --git a/ui/core/tests/unit/routes/scopes/scope/groups/group/index-test.js b/ui/core/tests/unit/routes/scopes/scope/groups/group/index-test.js new file mode 100644 index 000000000..3981831ad --- /dev/null +++ b/ui/core/tests/unit/routes/scopes/scope/groups/group/index-test.js @@ -0,0 +1,11 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Route | scopes/scope/groups/group/index', function (hooks) { + setupTest(hooks); + + test('it exists', function (assert) { + let route = this.owner.lookup('route:scopes/scope/groups/group/index'); + assert.ok(route); + }); +}); diff --git a/ui/core/tests/unit/routes/scopes/scope/groups/index-test.js b/ui/core/tests/unit/routes/scopes/scope/groups/index-test.js new file mode 100644 index 000000000..f50de2922 --- /dev/null +++ b/ui/core/tests/unit/routes/scopes/scope/groups/index-test.js @@ -0,0 +1,11 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Route | scopes/scope/groups/index', function (hooks) { + setupTest(hooks); + + test('it exists', function (assert) { + let route = this.owner.lookup('route:scopes/scope/groups/index'); + assert.ok(route); + }); +}); diff --git a/ui/core/tests/unit/routes/scopes/scope/groups/new-test.js b/ui/core/tests/unit/routes/scopes/scope/groups/new-test.js new file mode 100644 index 000000000..6e689bdd3 --- /dev/null +++ b/ui/core/tests/unit/routes/scopes/scope/groups/new-test.js @@ -0,0 +1,11 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Route | scopes/scope/groups/new', function (hooks) { + setupTest(hooks); + + test('it exists', function (assert) { + let route = this.owner.lookup('route:scopes/scope/groups/new'); + assert.ok(route); + }); +}); From 3ac5f766ff9e8ca040284e07a791e39a14d0277a Mon Sep 17 00:00:00 2001 From: Susmitha Girumala Date: Mon, 27 Jul 2020 16:51:41 -0400 Subject: [PATCH 46/70] =?UTF-8?q?chore:=20=F0=9F=A4=96=20Refactor=20role?= =?UTF-8?q?=20CRUD+L=20under=20/scopes/:scope=5Fid?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ui/core/app/router.js | 4 ++ ui/core/app/routes/scopes/scope/roles.js | 72 +++++++++++++++++++ .../app/routes/scopes/scope/roles/index.js | 3 + ui/core/app/routes/scopes/scope/roles/new.js | 13 ++++ ui/core/app/routes/scopes/scope/roles/role.js | 17 +++++ .../routes/scopes/scope/roles/role/index.js | 3 + ui/core/app/routes/scopes/scope/users.js | 2 +- ui/core/app/templates/scopes/scope.hbs | 3 + ui/core/app/templates/scopes/scope/roles.hbs | 3 + .../templates/scopes/scope/roles/index.hbs | 63 ++++++++++++++++ .../app/templates/scopes/scope/roles/new.hbs | 19 +++++ .../app/templates/scopes/scope/roles/role.hbs | 3 + .../scopes/scope/roles/role/index.hbs | 27 +++++++ .../unit/routes/scopes/scope/roles-test.js | 11 +++ .../routes/scopes/scope/roles/index-test.js | 11 +++ .../routes/scopes/scope/roles/new-test.js | 11 +++ .../routes/scopes/scope/roles/role-test.js | 11 +++ .../scopes/scope/roles/role/index-test.js | 11 +++ 18 files changed, 286 insertions(+), 1 deletion(-) create mode 100644 ui/core/app/routes/scopes/scope/roles.js create mode 100644 ui/core/app/routes/scopes/scope/roles/index.js create mode 100644 ui/core/app/routes/scopes/scope/roles/new.js create mode 100644 ui/core/app/routes/scopes/scope/roles/role.js create mode 100644 ui/core/app/routes/scopes/scope/roles/role/index.js create mode 100644 ui/core/app/templates/scopes/scope/roles.hbs create mode 100644 ui/core/app/templates/scopes/scope/roles/index.hbs create mode 100644 ui/core/app/templates/scopes/scope/roles/new.hbs create mode 100644 ui/core/app/templates/scopes/scope/roles/role.hbs create mode 100644 ui/core/app/templates/scopes/scope/roles/role/index.hbs create mode 100644 ui/core/tests/unit/routes/scopes/scope/roles-test.js create mode 100644 ui/core/tests/unit/routes/scopes/scope/roles/index-test.js create mode 100644 ui/core/tests/unit/routes/scopes/scope/roles/new-test.js create mode 100644 ui/core/tests/unit/routes/scopes/scope/roles/role-test.js create mode 100644 ui/core/tests/unit/routes/scopes/scope/roles/role/index-test.js diff --git a/ui/core/app/router.js b/ui/core/app/router.js index 0e7e5f7f7..accba55a0 100644 --- a/ui/core/app/router.js +++ b/ui/core/app/router.js @@ -21,6 +21,10 @@ Router.map(function () { this.route('group', { path: ':group_id' }, function () {}); this.route('new'); }); + this.route('roles', function () { + this.route('role', { path: ':role_id' }, function () {}); + this.route('new'); + }); }); }); diff --git a/ui/core/app/routes/scopes/scope/roles.js b/ui/core/app/routes/scopes/scope/roles.js new file mode 100644 index 000000000..fd451c719 --- /dev/null +++ b/ui/core/app/routes/scopes/scope/roles.js @@ -0,0 +1,72 @@ +import Route from '@ember/routing/route'; +import { inject as service } from '@ember/service'; +import { action } from '@ember/object'; + +export default class ScopesScopeRolesRoute extends Route { + // =services + + @service intl; + @service notify; + + // =methods + + /** + * Load all roles under current scope. + * @return {Promise{[RoleModel]}} + */ + async model() { + return this.store.findAll('role', this.scopeAdapterOptions()); + } + + // =actions + /** + * Rollback changes on an role. + * @param {RoleModel} role + */ + @action + cancel(role) { + const { isNew } = role; + role.rollbackAttributes(); + if (isNew) this.transitionTo('scopes.scope.roles'); + } + + /** + * Save an role in current scope. + * @param {RoleModel} role + */ + @action + async save(role) { + try { + await role.save(this.scopeAdapterOptions()); + this.refresh(); + this.notify.success(this.intl.t('notify.save-success')); + this.transitionTo('scopes.scope.roles.role', role); + } catch (error) { + // TODO: replace with translated strings + this.notify.error(error.message, { closeAfter: null }); + } + } + + /** + * Delete role in current scope and redirect to index. + * @param {RoleModel} role + */ + @action + async delete(role) { + try { + await role.destroyRecord(this.scopeAdapterOptions()); + this.refresh(); + this.notify.success(this.intl.t('notify.role.delete-success')); + this.transitionTo('scopes.scope.roles'); + } catch (error) { + // TODO: replace with translated strings + this.notify.error(error.message, { closeAfter: null }); + } + } + + scopeAdapterOptions() { + const { id: scope_id } = this.modelFor('scopes.scope'); + const adapterOptions = { scopeID: scope_id }; + return { adapterOptions }; + } +} diff --git a/ui/core/app/routes/scopes/scope/roles/index.js b/ui/core/app/routes/scopes/scope/roles/index.js new file mode 100644 index 000000000..f2a8d7af1 --- /dev/null +++ b/ui/core/app/routes/scopes/scope/roles/index.js @@ -0,0 +1,3 @@ +import Route from '@ember/routing/route'; + +export default class ScopesScopeRolesIndexRoute extends Route {} diff --git a/ui/core/app/routes/scopes/scope/roles/new.js b/ui/core/app/routes/scopes/scope/roles/new.js new file mode 100644 index 000000000..f39f6e6c2 --- /dev/null +++ b/ui/core/app/routes/scopes/scope/roles/new.js @@ -0,0 +1,13 @@ +import Route from '@ember/routing/route'; + +export default class ScopesScopeRolesNewRoute extends Route { + // =methods + + /** + * Creates a new unsaved role. + * @return {RoleModel} + */ + model() { + return this.store.createRecord('role'); + } +} diff --git a/ui/core/app/routes/scopes/scope/roles/role.js b/ui/core/app/routes/scopes/scope/roles/role.js new file mode 100644 index 000000000..781bb4e47 --- /dev/null +++ b/ui/core/app/routes/scopes/scope/roles/role.js @@ -0,0 +1,17 @@ +import Route from '@ember/routing/route'; + +export default class ScopesScopeRolesRoleRoute extends Route { + // =methods + + /** + * Load a role in current scope. + * @param {object} params + * @param {string} params.role_id + * @return {RoleModel} + */ + async model({ role_id }) { + const { id: scope_id } = this.modelFor('scopes.scope'); + const adapterOptions = { scopeID: scope_id }; + return this.store.findRecord('role', role_id, { adapterOptions }); + } +} diff --git a/ui/core/app/routes/scopes/scope/roles/role/index.js b/ui/core/app/routes/scopes/scope/roles/role/index.js new file mode 100644 index 000000000..359e9c9f5 --- /dev/null +++ b/ui/core/app/routes/scopes/scope/roles/role/index.js @@ -0,0 +1,3 @@ +import Route from '@ember/routing/route'; + +export default class ScopesScopeRolesRoleIndexRoute extends Route {} diff --git a/ui/core/app/routes/scopes/scope/users.js b/ui/core/app/routes/scopes/scope/users.js index bf0ba95c4..8f4f65905 100644 --- a/ui/core/app/routes/scopes/scope/users.js +++ b/ui/core/app/routes/scopes/scope/users.js @@ -58,7 +58,7 @@ export default class ScopesScopeUsersRoute extends Route { await user.destroyRecord(this.scopeAdapterOptions()); this.refresh(); this.transitionTo('scopes.scope.users'); - this.notify.success(this.intl.t('notify.delete-success')); + this.notify.success(this.intl.t('notify.user.delete-success')); } catch (error) { // TODO: replace with translated strings this.notify.error(error.message, { closeAfter: null }); diff --git a/ui/core/app/templates/scopes/scope.hbs b/ui/core/app/templates/scopes/scope.hbs index f3199821b..55e35d7e0 100644 --- a/ui/core/app/templates/scopes/scope.hbs +++ b/ui/core/app/templates/scopes/scope.hbs @@ -15,6 +15,9 @@ {{t "resources.groups"}} + + {{t "resources.roles"}} + {{outlet "sidebar"}} diff --git a/ui/core/app/templates/scopes/scope/roles.hbs b/ui/core/app/templates/scopes/scope/roles.hbs new file mode 100644 index 000000000..6d511b7f7 --- /dev/null +++ b/ui/core/app/templates/scopes/scope/roles.hbs @@ -0,0 +1,3 @@ +{{title (t "resources.roles")}} + +{{outlet}} diff --git a/ui/core/app/templates/scopes/scope/roles/index.hbs b/ui/core/app/templates/scopes/scope/roles/index.hbs new file mode 100644 index 000000000..e0e28cf9a --- /dev/null +++ b/ui/core/app/templates/scopes/scope/roles/index.hbs @@ -0,0 +1,63 @@ + + + + + + + +

{{t "resources.roles"}}

+
+ + + {{#if @model}} + {{t 'titles.new'}} + {{/if}} + + + + + {{#if @model}} + + + + + {{t 'form.label.name'}} + {{t 'form.label.description'}} + + + + {{#each @model as |role|}} + + + + {{role.displayName}} + + + {{role.description}} + + {{/each}} + + + + {{else}} + + + + + {{t 'descriptions.roles-welcome'}} + + + + {{t 'titles.new'}} + + + + + {{/if}} + + + +
diff --git a/ui/core/app/templates/scopes/scope/roles/new.hbs b/ui/core/app/templates/scopes/scope/roles/new.hbs new file mode 100644 index 000000000..efddd8fb9 --- /dev/null +++ b/ui/core/app/templates/scopes/scope/roles/new.hbs @@ -0,0 +1,19 @@ +{{title (t 'titles.new')}} + + + + + + + +

{{t "titles.new"}}

+
+ + + + +
diff --git a/ui/core/app/templates/scopes/scope/roles/role.hbs b/ui/core/app/templates/scopes/scope/roles/role.hbs new file mode 100644 index 000000000..d66e83390 --- /dev/null +++ b/ui/core/app/templates/scopes/scope/roles/role.hbs @@ -0,0 +1,3 @@ +{{title @model.displayName}} + +{{outlet}} diff --git a/ui/core/app/templates/scopes/scope/roles/role/index.hbs b/ui/core/app/templates/scopes/scope/roles/role/index.hbs new file mode 100644 index 000000000..1b35c1929 --- /dev/null +++ b/ui/core/app/templates/scopes/scope/roles/role/index.hbs @@ -0,0 +1,27 @@ + + + + + + + +

{{t "resources.role"}}

+
+ + + + + + {{t 'actions.delete'}} + + + +
diff --git a/ui/core/tests/unit/routes/scopes/scope/roles-test.js b/ui/core/tests/unit/routes/scopes/scope/roles-test.js new file mode 100644 index 000000000..f2aa633ee --- /dev/null +++ b/ui/core/tests/unit/routes/scopes/scope/roles-test.js @@ -0,0 +1,11 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Route | scopes/scope/roles', function (hooks) { + setupTest(hooks); + + test('it exists', function (assert) { + let route = this.owner.lookup('route:scopes/scope/roles'); + assert.ok(route); + }); +}); diff --git a/ui/core/tests/unit/routes/scopes/scope/roles/index-test.js b/ui/core/tests/unit/routes/scopes/scope/roles/index-test.js new file mode 100644 index 000000000..aad8dd94c --- /dev/null +++ b/ui/core/tests/unit/routes/scopes/scope/roles/index-test.js @@ -0,0 +1,11 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Route | scopes/scope/roles/index', function (hooks) { + setupTest(hooks); + + test('it exists', function (assert) { + let route = this.owner.lookup('route:scopes/scope/roles/index'); + assert.ok(route); + }); +}); diff --git a/ui/core/tests/unit/routes/scopes/scope/roles/new-test.js b/ui/core/tests/unit/routes/scopes/scope/roles/new-test.js new file mode 100644 index 000000000..1ddf02ba6 --- /dev/null +++ b/ui/core/tests/unit/routes/scopes/scope/roles/new-test.js @@ -0,0 +1,11 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Route | scopes/scope/roles/new', function (hooks) { + setupTest(hooks); + + test('it exists', function (assert) { + let route = this.owner.lookup('route:scopes/scope/roles/new'); + assert.ok(route); + }); +}); diff --git a/ui/core/tests/unit/routes/scopes/scope/roles/role-test.js b/ui/core/tests/unit/routes/scopes/scope/roles/role-test.js new file mode 100644 index 000000000..41e292c3c --- /dev/null +++ b/ui/core/tests/unit/routes/scopes/scope/roles/role-test.js @@ -0,0 +1,11 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Route | scopes/scope/roles/role', function (hooks) { + setupTest(hooks); + + test('it exists', function (assert) { + let route = this.owner.lookup('route:scopes/scope/roles/role'); + assert.ok(route); + }); +}); diff --git a/ui/core/tests/unit/routes/scopes/scope/roles/role/index-test.js b/ui/core/tests/unit/routes/scopes/scope/roles/role/index-test.js new file mode 100644 index 000000000..1a4aecbf1 --- /dev/null +++ b/ui/core/tests/unit/routes/scopes/scope/roles/role/index-test.js @@ -0,0 +1,11 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Route | scopes/scope/roles/role/index', function (hooks) { + setupTest(hooks); + + test('it exists', function (assert) { + let route = this.owner.lookup('route:scopes/scope/roles/role/index'); + assert.ok(route); + }); +}); From d2a4cc3f551579b41561d84f88419f674f2e5e7a Mon Sep 17 00:00:00 2001 From: Susmitha Girumala Date: Mon, 27 Jul 2020 17:32:33 -0400 Subject: [PATCH 47/70] =?UTF-8?q?chore:=20=F0=9F=A4=96=20Refactor=20IAM=20?= =?UTF-8?q?breadcrumbs=20under=20/scopes/:scope=5Fid?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ui/core/app/controllers/scopes/scope/groups.js | 18 ++++++++++++++++++ .../controllers/scopes/scope/groups/group.js | 13 +++++++++++++ .../app/controllers/scopes/scope/groups/new.js | 18 ++++++++++++++++++ ui/core/app/controllers/scopes/scope/roles.js | 18 ++++++++++++++++++ .../app/controllers/scopes/scope/roles/new.js | 18 ++++++++++++++++++ .../app/controllers/scopes/scope/roles/role.js | 13 +++++++++++++ ui/core/app/controllers/scopes/scope/users.js | 18 ++++++++++++++++++ .../app/controllers/scopes/scope/users/new.js | 18 ++++++++++++++++++ .../app/controllers/scopes/scope/users/user.js | 13 +++++++++++++ ui/core/mirage/config.js | 2 +- .../controllers/scopes/scope/groups-test.js | 12 ++++++++++++ .../scopes/scope/groups/group-test.js | 12 ++++++++++++ .../scopes/scope/groups/new-test.js | 12 ++++++++++++ .../controllers/scopes/scope/roles-test.js | 12 ++++++++++++ .../controllers/scopes/scope/roles/new-test.js | 12 ++++++++++++ .../scopes/scope/roles/role-test.js | 12 ++++++++++++ .../controllers/scopes/scope/users-test.js | 12 ++++++++++++ .../controllers/scopes/scope/users/new-test.js | 12 ++++++++++++ .../scopes/scope/users/user-test.js | 12 ++++++++++++ 19 files changed, 256 insertions(+), 1 deletion(-) create mode 100644 ui/core/app/controllers/scopes/scope/groups.js create mode 100644 ui/core/app/controllers/scopes/scope/groups/group.js create mode 100644 ui/core/app/controllers/scopes/scope/groups/new.js create mode 100644 ui/core/app/controllers/scopes/scope/roles.js create mode 100644 ui/core/app/controllers/scopes/scope/roles/new.js create mode 100644 ui/core/app/controllers/scopes/scope/roles/role.js create mode 100644 ui/core/app/controllers/scopes/scope/users.js create mode 100644 ui/core/app/controllers/scopes/scope/users/new.js create mode 100644 ui/core/app/controllers/scopes/scope/users/user.js create mode 100644 ui/core/tests/unit/controllers/scopes/scope/groups-test.js create mode 100644 ui/core/tests/unit/controllers/scopes/scope/groups/group-test.js create mode 100644 ui/core/tests/unit/controllers/scopes/scope/groups/new-test.js create mode 100644 ui/core/tests/unit/controllers/scopes/scope/roles-test.js create mode 100644 ui/core/tests/unit/controllers/scopes/scope/roles/new-test.js create mode 100644 ui/core/tests/unit/controllers/scopes/scope/roles/role-test.js create mode 100644 ui/core/tests/unit/controllers/scopes/scope/users-test.js create mode 100644 ui/core/tests/unit/controllers/scopes/scope/users/new-test.js create mode 100644 ui/core/tests/unit/controllers/scopes/scope/users/user-test.js diff --git a/ui/core/app/controllers/scopes/scope/groups.js b/ui/core/app/controllers/scopes/scope/groups.js new file mode 100644 index 000000000..000d18739 --- /dev/null +++ b/ui/core/app/controllers/scopes/scope/groups.js @@ -0,0 +1,18 @@ +import Controller from '@ember/controller'; +import { inject as service } from '@ember/service'; + +export default class ScopesScopeGroupsController extends Controller { + // =services + + @service intl; + + // =attributes + + /** + * Translated groups breadcrumb + * @type {string} + */ + get breadCrumb() { + return this.intl.t('resources.groups'); + } +} diff --git a/ui/core/app/controllers/scopes/scope/groups/group.js b/ui/core/app/controllers/scopes/scope/groups/group.js new file mode 100644 index 000000000..121559b29 --- /dev/null +++ b/ui/core/app/controllers/scopes/scope/groups/group.js @@ -0,0 +1,13 @@ +import Controller from '@ember/controller'; + +export default class ScopesScopeGroupsGroupController extends Controller { + // =attributes + + /** + * A group breadcrumb + * @type {string} + */ + get breadCrumb() { + return this.model.id; + } +} diff --git a/ui/core/app/controllers/scopes/scope/groups/new.js b/ui/core/app/controllers/scopes/scope/groups/new.js new file mode 100644 index 000000000..0de96d821 --- /dev/null +++ b/ui/core/app/controllers/scopes/scope/groups/new.js @@ -0,0 +1,18 @@ +import Controller from '@ember/controller'; +import { inject as service } from '@ember/service'; + +export default class ScopesScopeGroupsNewController extends Controller { + // =services + + @service intl; + + // =attributes + + /** + * Translated new group breadcrumb + * @type {string} + */ + get breadCrumb() { + return this.intl.t('titles.new'); + } +} diff --git a/ui/core/app/controllers/scopes/scope/roles.js b/ui/core/app/controllers/scopes/scope/roles.js new file mode 100644 index 000000000..bbd84249e --- /dev/null +++ b/ui/core/app/controllers/scopes/scope/roles.js @@ -0,0 +1,18 @@ +import Controller from '@ember/controller'; +import { inject as service } from '@ember/service'; + +export default class ScopesScopeRolesController extends Controller { + // =services + + @service intl; + + // =attributes + + /** + * Translated roles breadcrumb + * @type {string} + */ + get breadCrumb() { + return this.intl.t('resources.roles'); + } +} diff --git a/ui/core/app/controllers/scopes/scope/roles/new.js b/ui/core/app/controllers/scopes/scope/roles/new.js new file mode 100644 index 000000000..598ec9760 --- /dev/null +++ b/ui/core/app/controllers/scopes/scope/roles/new.js @@ -0,0 +1,18 @@ +import Controller from '@ember/controller'; +import { inject as service } from '@ember/service'; + +export default class ScopesScopeRolesNewController extends Controller { + // =services + + @service intl; + + // =attributes + + /** + * Translated new role breadcrumb + * @type {string} + */ + get breadCrumb() { + return this.intl.t('titles.new'); + } +} diff --git a/ui/core/app/controllers/scopes/scope/roles/role.js b/ui/core/app/controllers/scopes/scope/roles/role.js new file mode 100644 index 000000000..f18b1df9a --- /dev/null +++ b/ui/core/app/controllers/scopes/scope/roles/role.js @@ -0,0 +1,13 @@ +import Controller from '@ember/controller'; + +export default class ScopesScopeRolesRoleController extends Controller { + // =attributes + + /** + * A role breadcrumb + * @type {string} + */ + get breadCrumb() { + return this.model.id; + } +} diff --git a/ui/core/app/controllers/scopes/scope/users.js b/ui/core/app/controllers/scopes/scope/users.js new file mode 100644 index 000000000..c88b342fc --- /dev/null +++ b/ui/core/app/controllers/scopes/scope/users.js @@ -0,0 +1,18 @@ +import Controller from '@ember/controller'; +import { inject as service } from '@ember/service'; + +export default class ScopesScopeUsersController extends Controller { + // =services + + @service intl; + + // =attributes + + /** + * Translated users breadcrumb + * @type {string} + */ + get breadCrumb() { + return this.intl.t('resources.users'); + } +} diff --git a/ui/core/app/controllers/scopes/scope/users/new.js b/ui/core/app/controllers/scopes/scope/users/new.js new file mode 100644 index 000000000..91d3f0cb4 --- /dev/null +++ b/ui/core/app/controllers/scopes/scope/users/new.js @@ -0,0 +1,18 @@ +import Controller from '@ember/controller'; +import { inject as service } from '@ember/service'; + +export default class ScopesScopeUsersNewController extends Controller { + // =services + + @service intl; + + // =attributes + + /** + * Translated new user breadcrumb + * @type {string} + */ + get breadCrumb() { + return this.intl.t('titles.new'); + } +} diff --git a/ui/core/app/controllers/scopes/scope/users/user.js b/ui/core/app/controllers/scopes/scope/users/user.js new file mode 100644 index 000000000..9f0456fa4 --- /dev/null +++ b/ui/core/app/controllers/scopes/scope/users/user.js @@ -0,0 +1,13 @@ +import Controller from '@ember/controller'; + +export default class ScopesScopeUsersUserController extends Controller { + // =attributes + + /** + * An user breadcrumb + * @type {string} + */ + get breadCrumb() { + return this.model.id; + } +} diff --git a/ui/core/mirage/config.js b/ui/core/mirage/config.js index ed6859c83..4d73b2f7c 100644 --- a/ui/core/mirage/config.js +++ b/ui/core/mirage/config.js @@ -69,7 +69,7 @@ export default function() { this.patch('/scopes/:scope_id/roles/:id'); this.del('/scopes/:scope_id/roles/:id'); - // group + // IAM: Groups this.get('/scopes/:scope_id/groups'); this.post('/scopes/:scope_id/groups'); this.get('/scopes/:scope_id/groups/:id'); diff --git a/ui/core/tests/unit/controllers/scopes/scope/groups-test.js b/ui/core/tests/unit/controllers/scopes/scope/groups-test.js new file mode 100644 index 000000000..a7baaa19e --- /dev/null +++ b/ui/core/tests/unit/controllers/scopes/scope/groups-test.js @@ -0,0 +1,12 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Controller | scopes/scope/groups', function(hooks) { + setupTest(hooks); + + // Replace this with your real tests. + test('it exists', function(assert) { + let controller = this.owner.lookup('controller:scopes/scope/groups'); + assert.ok(controller); + }); +}); diff --git a/ui/core/tests/unit/controllers/scopes/scope/groups/group-test.js b/ui/core/tests/unit/controllers/scopes/scope/groups/group-test.js new file mode 100644 index 000000000..13d2aeb1d --- /dev/null +++ b/ui/core/tests/unit/controllers/scopes/scope/groups/group-test.js @@ -0,0 +1,12 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Controller | scopes/scope/groups/group', function(hooks) { + setupTest(hooks); + + // Replace this with your real tests. + test('it exists', function(assert) { + let controller = this.owner.lookup('controller:scopes/scope/groups/group'); + assert.ok(controller); + }); +}); diff --git a/ui/core/tests/unit/controllers/scopes/scope/groups/new-test.js b/ui/core/tests/unit/controllers/scopes/scope/groups/new-test.js new file mode 100644 index 000000000..a9290297d --- /dev/null +++ b/ui/core/tests/unit/controllers/scopes/scope/groups/new-test.js @@ -0,0 +1,12 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Controller | scopes/scope/groups/new', function(hooks) { + setupTest(hooks); + + // Replace this with your real tests. + test('it exists', function(assert) { + let controller = this.owner.lookup('controller:scopes/scope/groups/new'); + assert.ok(controller); + }); +}); diff --git a/ui/core/tests/unit/controllers/scopes/scope/roles-test.js b/ui/core/tests/unit/controllers/scopes/scope/roles-test.js new file mode 100644 index 000000000..23fd61372 --- /dev/null +++ b/ui/core/tests/unit/controllers/scopes/scope/roles-test.js @@ -0,0 +1,12 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Controller | scopes/scope/roles', function(hooks) { + setupTest(hooks); + + // Replace this with your real tests. + test('it exists', function(assert) { + let controller = this.owner.lookup('controller:scopes/scope/roles'); + assert.ok(controller); + }); +}); diff --git a/ui/core/tests/unit/controllers/scopes/scope/roles/new-test.js b/ui/core/tests/unit/controllers/scopes/scope/roles/new-test.js new file mode 100644 index 000000000..74245d386 --- /dev/null +++ b/ui/core/tests/unit/controllers/scopes/scope/roles/new-test.js @@ -0,0 +1,12 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Controller | scopes/scope/roles/new', function(hooks) { + setupTest(hooks); + + // Replace this with your real tests. + test('it exists', function(assert) { + let controller = this.owner.lookup('controller:scopes/scope/roles/new'); + assert.ok(controller); + }); +}); diff --git a/ui/core/tests/unit/controllers/scopes/scope/roles/role-test.js b/ui/core/tests/unit/controllers/scopes/scope/roles/role-test.js new file mode 100644 index 000000000..b6564723f --- /dev/null +++ b/ui/core/tests/unit/controllers/scopes/scope/roles/role-test.js @@ -0,0 +1,12 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Controller | scopes/scope/roles/role', function(hooks) { + setupTest(hooks); + + // Replace this with your real tests. + test('it exists', function(assert) { + let controller = this.owner.lookup('controller:scopes/scope/roles/role'); + assert.ok(controller); + }); +}); diff --git a/ui/core/tests/unit/controllers/scopes/scope/users-test.js b/ui/core/tests/unit/controllers/scopes/scope/users-test.js new file mode 100644 index 000000000..0b5ab25d4 --- /dev/null +++ b/ui/core/tests/unit/controllers/scopes/scope/users-test.js @@ -0,0 +1,12 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Controller | scopes/scope/users', function(hooks) { + setupTest(hooks); + + // Replace this with your real tests. + test('it exists', function(assert) { + let controller = this.owner.lookup('controller:scopes/scope/users'); + assert.ok(controller); + }); +}); diff --git a/ui/core/tests/unit/controllers/scopes/scope/users/new-test.js b/ui/core/tests/unit/controllers/scopes/scope/users/new-test.js new file mode 100644 index 000000000..fd291f809 --- /dev/null +++ b/ui/core/tests/unit/controllers/scopes/scope/users/new-test.js @@ -0,0 +1,12 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Controller | scopes/scope/users/new', function(hooks) { + setupTest(hooks); + + // Replace this with your real tests. + test('it exists', function(assert) { + let controller = this.owner.lookup('controller:scopes/scope/users/new'); + assert.ok(controller); + }); +}); diff --git a/ui/core/tests/unit/controllers/scopes/scope/users/user-test.js b/ui/core/tests/unit/controllers/scopes/scope/users/user-test.js new file mode 100644 index 000000000..c4a2ef171 --- /dev/null +++ b/ui/core/tests/unit/controllers/scopes/scope/users/user-test.js @@ -0,0 +1,12 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Controller | scopes/scope/users/user', function(hooks) { + setupTest(hooks); + + // Replace this with your real tests. + test('it exists', function(assert) { + let controller = this.owner.lookup('controller:scopes/scope/users/user'); + assert.ok(controller); + }); +}); From 6f7555e279cbbfd4f99fd886436500075e86585a Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Tue, 28 Jul 2020 10:31:40 -0400 Subject: [PATCH 48/70] chore: tweak input sizing and media queries --- addons/rose/addon/styles/rose/components/form/_input.scss | 1 + addons/rose/addon/styles/rose/variables/_media.scss | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/addons/rose/addon/styles/rose/components/form/_input.scss b/addons/rose/addon/styles/rose/components/form/_input.scss index 6c0186b50..7b11a0a8e 100644 --- a/addons/rose/addon/styles/rose/components/form/_input.scss +++ b/addons/rose/addon/styles/rose/components/form/_input.scss @@ -19,6 +19,7 @@ .rose-form-input-field { @include type.type(m); + box-sizing: border-box; margin-bottom: sizing.rems(xs); padding: sizing.rems(xs); color: var(--stark); diff --git a/addons/rose/addon/styles/rose/variables/_media.scss b/addons/rose/addon/styles/rose/variables/_media.scss index 289c96ad9..95d1ab86d 100644 --- a/addons/rose/addon/styles/rose/variables/_media.scss +++ b/addons/rose/addon/styles/rose/variables/_media.scss @@ -9,7 +9,7 @@ $width-breakpoints: ( xlarge: 1200px, large: 1024px, medium: 768px, - small: 480px, + small: 540px, xsmall: 320px, ); From 0b974f122c46baec937a920578b07adba41508c5 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Tue, 28 Jul 2020 10:36:41 -0400 Subject: [PATCH 49/70] chore: hide sidebar when not authenticated --- ui/core/app/controllers/scopes/scope.js | 10 +++++ ui/core/app/templates/scopes/scope.hbs | 44 ++++++++++--------- .../unit/controllers/scopes/scope-test.js | 12 +++++ 3 files changed, 45 insertions(+), 21 deletions(-) create mode 100644 ui/core/app/controllers/scopes/scope.js create mode 100644 ui/core/tests/unit/controllers/scopes/scope-test.js diff --git a/ui/core/app/controllers/scopes/scope.js b/ui/core/app/controllers/scopes/scope.js new file mode 100644 index 000000000..5bc636752 --- /dev/null +++ b/ui/core/app/controllers/scopes/scope.js @@ -0,0 +1,10 @@ +import Controller from '@ember/controller'; +import { inject as service } from '@ember/service'; + +export default class ScopesScopeController extends Controller { + + // =services + + @service session; + +} diff --git a/ui/core/app/templates/scopes/scope.hbs b/ui/core/app/templates/scopes/scope.hbs index 35287c253..172eefc48 100644 --- a/ui/core/app/templates/scopes/scope.hbs +++ b/ui/core/app/templates/scopes/scope.hbs @@ -1,31 +1,33 @@ - - - - {{t "resources.projects"}} - - + {{#if this.session.isAuthenticated}} + + + + {{t "resources.projects"}} + + - {{outlet "sidebar"}} + {{outlet "sidebar"}} - {{!-- - - - {{t "resources.users"}} - + {{!-- + + + {{t "resources.users"}} + - - {{t "resources.groups"}} - + + {{t "resources.groups"}} + - - {{t "resources.roles"}} - - - --}} - + + {{t "resources.roles"}} + + + --}} + + {{/if}}
diff --git a/ui/core/tests/unit/controllers/scopes/scope-test.js b/ui/core/tests/unit/controllers/scopes/scope-test.js new file mode 100644 index 000000000..ec2c389bb --- /dev/null +++ b/ui/core/tests/unit/controllers/scopes/scope-test.js @@ -0,0 +1,12 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Controller | scopes/scope', function(hooks) { + setupTest(hooks); + + // Replace this with your real tests. + test('it exists', function(assert) { + let controller = this.owner.lookup('controller:scopes/scope'); + assert.ok(controller); + }); +}); From acf45fada90d904e584f7b9d32cd261b7d6bbfc6 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Tue, 28 Jul 2020 10:39:35 -0400 Subject: [PATCH 50/70] chore: add page title --- ui/core/app/templates/scopes/scope/authenticate.hbs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/ui/core/app/templates/scopes/scope/authenticate.hbs b/ui/core/app/templates/scopes/scope/authenticate.hbs index c0e8c5485..43b329188 100644 --- a/ui/core/app/templates/scopes/scope/authenticate.hbs +++ b/ui/core/app/templates/scopes/scope/authenticate.hbs @@ -1,15 +1,17 @@ +{{title (t "actions.authenticate")}} + -

{{t 'actions.authenticate'}}

-

{{t 'auth.descriptions.authenticating-with-org' name=@model.scope.displayName}}

+

{{t "actions.authenticate"}}

+

{{t "auth.descriptions.authenticating-with-org" name=@model.scope.displayName}}

{{#each @model.scopes as |scope|}} @@ -24,7 +26,7 @@ {{#each @model.authMethods as |authMethod|}} - {{t (concat 'auth.method-types.' authMethod.type)}} + {{t (concat "auth.method-types." authMethod.type)}} {{/each}} From cd0c15a41fb76a6cd24f0ff65cf57f7860d4dfb2 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Tue, 28 Jul 2020 11:06:06 -0400 Subject: [PATCH 51/70] chore: add initial host catalogs index --- ui/core/app/router.js | 7 +- .../scope/projects/project/host-catalogs.js | 73 +++++++++++++++++++ .../project/host-catalogs/host-catalog.js | 4 + .../host-catalogs/host-catalog/index.js | 4 + .../projects/project/host-catalogs/index.js | 4 + .../projects/project/host-catalogs/new.js | 4 + .../scope/projects/project/host-catalogs.hbs | 3 + .../project/host-catalogs/host-catalog.hbs | 1 + .../host-catalogs/host-catalog/index.hbs | 1 + .../projects/project/host-catalogs/index.hbs | 68 +++++++++++++++++ .../projects/project/host-catalogs/new.hbs | 1 + ui/core/mirage/scenarios/default.js | 17 +---- .../projects/project/host-catalogs-test.js | 11 +++ .../host-catalogs/host-catalog-test.js | 11 +++ .../host-catalogs/host-catalog/index-test.js | 11 +++ .../project/host-catalogs/index-test.js | 11 +++ .../project/host-catalogs/new-test.js | 11 +++ 17 files changed, 228 insertions(+), 14 deletions(-) create mode 100644 ui/core/app/routes/scopes/scope/projects/project/host-catalogs.js create mode 100644 ui/core/app/routes/scopes/scope/projects/project/host-catalogs/host-catalog.js create mode 100644 ui/core/app/routes/scopes/scope/projects/project/host-catalogs/host-catalog/index.js create mode 100644 ui/core/app/routes/scopes/scope/projects/project/host-catalogs/index.js create mode 100644 ui/core/app/routes/scopes/scope/projects/project/host-catalogs/new.js create mode 100644 ui/core/app/templates/scopes/scope/projects/project/host-catalogs.hbs create mode 100644 ui/core/app/templates/scopes/scope/projects/project/host-catalogs/host-catalog.hbs create mode 100644 ui/core/app/templates/scopes/scope/projects/project/host-catalogs/host-catalog/index.hbs create mode 100644 ui/core/app/templates/scopes/scope/projects/project/host-catalogs/index.hbs create mode 100644 ui/core/app/templates/scopes/scope/projects/project/host-catalogs/new.hbs create mode 100644 ui/core/tests/unit/routes/scopes/scope/projects/project/host-catalogs-test.js create mode 100644 ui/core/tests/unit/routes/scopes/scope/projects/project/host-catalogs/host-catalog-test.js create mode 100644 ui/core/tests/unit/routes/scopes/scope/projects/project/host-catalogs/host-catalog/index-test.js create mode 100644 ui/core/tests/unit/routes/scopes/scope/projects/project/host-catalogs/index-test.js create mode 100644 ui/core/tests/unit/routes/scopes/scope/projects/project/host-catalogs/new-test.js diff --git a/ui/core/app/router.js b/ui/core/app/router.js index ef3536c60..4385741d0 100644 --- a/ui/core/app/router.js +++ b/ui/core/app/router.js @@ -14,7 +14,12 @@ Router.map(function () { this.route('method', { path: ':auth_method_id'}); }); this.route('projects', function() { - this.route('project', { path: ':project_id' }, function() {}); + this.route('project', { path: ':project_id' }, function() { + this.route('host-catalogs', function() { + this.route('new'); + this.route('host-catalog', { path: ':host_catalog_id' }, function() {}); + }); + }); this.route('new'); }); }); diff --git a/ui/core/app/routes/scopes/scope/projects/project/host-catalogs.js b/ui/core/app/routes/scopes/scope/projects/project/host-catalogs.js new file mode 100644 index 000000000..de945f71c --- /dev/null +++ b/ui/core/app/routes/scopes/scope/projects/project/host-catalogs.js @@ -0,0 +1,73 @@ +import Route from '@ember/routing/route'; +import { inject as service } from '@ember/service'; +import { action } from '@ember/object'; + +export default class ScopesScopeProjectsProjectHostCatalogsRoute extends Route { + + // =services + + @service notify; + + // =methods + + + /** + * Loads all scopes under the current scope. + * @return {Promise{[HostCatalogModel]}} + */ + async model() { + const scopeID = this.modelFor('scopes.scope.projects.project').id; + return this.store.findAll('host-catalog', { adapterOptions: { scopeID } }); + } + + // =actions + + /** + * Rollback changes on project. + * @param {HostCatalogModel} hostCatalog + */ + @action + cancel(hostCatalog) { + const { isNew } = hostCatalog; + hostCatalog.rollbackAttributes(); + if (isNew) this.transitionTo('scopes.scope.projects.project.host-catalogs'); + } + + /** + * Handle save. + * @param {HostCatalogModel} hostCatalog + * @param {Event} e + */ + @action + async save(hostCatalog) { + try { + await hostCatalog.save(); + this.refresh(); + // TODO: replace with translated strings + this.notify.success('Save succeeded.'); + this.transitionTo('scopes.scope.projects.project.host-catalogs.host-catalog', hostCatalog); + } catch (error) { + // TODO: replace with translated strings + this.notify.error(error.message, { closeAfter: null }); + } + } + + /** + * Deletes the host catalog and redirects to index. + * @param {ProjectModel} project + */ + @action + async delete(project) { + try { + await project.destroyRecord(); + this.refresh(); + this.transitionTo('scopes.scope.projects.project.host-catalogs'); + // TODO: replace with translated strings + this.notify.success('Deleted host catalog succesfully.'); + } catch (error) { + // TODO: replace with translated strings + this.notify.error(error.message, { closeAfter: null }); + } + } + +} diff --git a/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/host-catalog.js b/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/host-catalog.js new file mode 100644 index 000000000..f54947470 --- /dev/null +++ b/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/host-catalog.js @@ -0,0 +1,4 @@ +import Route from '@ember/routing/route'; + +export default class ScopesScopeProjectsProjectHostCatalogsHostCatalogRoute extends Route { +} diff --git a/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/host-catalog/index.js b/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/host-catalog/index.js new file mode 100644 index 000000000..34bccc035 --- /dev/null +++ b/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/host-catalog/index.js @@ -0,0 +1,4 @@ +import Route from '@ember/routing/route'; + +export default class ScopesScopeProjectsProjectHostCatalogsHostCatalogIndexRoute extends Route { +} diff --git a/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/index.js b/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/index.js new file mode 100644 index 000000000..b01277c47 --- /dev/null +++ b/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/index.js @@ -0,0 +1,4 @@ +import Route from '@ember/routing/route'; + +export default class ScopesScopeProjectsProjectHostCatalogsIndexRoute extends Route { +} diff --git a/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/new.js b/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/new.js new file mode 100644 index 000000000..9e076cd1d --- /dev/null +++ b/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/new.js @@ -0,0 +1,4 @@ +import Route from '@ember/routing/route'; + +export default class ScopesScopeProjectsProjectHostCatalogsNewRoute extends Route { +} diff --git a/ui/core/app/templates/scopes/scope/projects/project/host-catalogs.hbs b/ui/core/app/templates/scopes/scope/projects/project/host-catalogs.hbs new file mode 100644 index 000000000..6ad0035fb --- /dev/null +++ b/ui/core/app/templates/scopes/scope/projects/project/host-catalogs.hbs @@ -0,0 +1,3 @@ +{{title (t "resources.host-catalogs")}} + +{{outlet}} diff --git a/ui/core/app/templates/scopes/scope/projects/project/host-catalogs/host-catalog.hbs b/ui/core/app/templates/scopes/scope/projects/project/host-catalogs/host-catalog.hbs new file mode 100644 index 000000000..e2147cab0 --- /dev/null +++ b/ui/core/app/templates/scopes/scope/projects/project/host-catalogs/host-catalog.hbs @@ -0,0 +1 @@ +{{outlet}} \ No newline at end of file diff --git a/ui/core/app/templates/scopes/scope/projects/project/host-catalogs/host-catalog/index.hbs b/ui/core/app/templates/scopes/scope/projects/project/host-catalogs/host-catalog/index.hbs new file mode 100644 index 000000000..e2147cab0 --- /dev/null +++ b/ui/core/app/templates/scopes/scope/projects/project/host-catalogs/host-catalog/index.hbs @@ -0,0 +1 @@ +{{outlet}} \ No newline at end of file diff --git a/ui/core/app/templates/scopes/scope/projects/project/host-catalogs/index.hbs b/ui/core/app/templates/scopes/scope/projects/project/host-catalogs/index.hbs new file mode 100644 index 000000000..935cf9f30 --- /dev/null +++ b/ui/core/app/templates/scopes/scope/projects/project/host-catalogs/index.hbs @@ -0,0 +1,68 @@ + + + + + + + +

{{t "resources.host-catalogs"}}

+

{{t "descriptions.host-catalogs"}}

+
+ + + {{#if @model}} + + {{t "titles.new"}} + + {{/if}} + + + + + {{#if @model}} + + + + + {{t "form.label.name"}} + {{t "form.label.type"}} + {{t "form.label.description"}} + + + + {{#each @model as |hostCatalog|}} + + + + {{hostCatalog.name}} + + + {{hostCatalog.type}} + {{hostCatalog.description}} + + {{/each}} + + + + {{else}} + + + + + {{t "descriptions.host-catalogs-welcome"}} + + + + {{t "titles.new"}} + + + + + {{/if}} + + + +
diff --git a/ui/core/app/templates/scopes/scope/projects/project/host-catalogs/new.hbs b/ui/core/app/templates/scopes/scope/projects/project/host-catalogs/new.hbs new file mode 100644 index 000000000..e2147cab0 --- /dev/null +++ b/ui/core/app/templates/scopes/scope/projects/project/host-catalogs/new.hbs @@ -0,0 +1 @@ +{{outlet}} \ No newline at end of file diff --git a/ui/core/mirage/scenarios/default.js b/ui/core/mirage/scenarios/default.js index 1bbae80d5..f5727e195 100644 --- a/ui/core/mirage/scenarios/default.js +++ b/ui/core/mirage/scenarios/default.js @@ -7,20 +7,8 @@ export default function(server) { type: 'org', scope: { id: globalScope.id, type: globalScope.type } }, 'withChildren'); - const scope = { id: orgScope.id, type: orgScope.type }; - // Create additional scopes for authentication organization selection - server.create('scope', { - type: 'org', - scope: { id: globalScope.id, type: globalScope.type } - }, 'withChildren'); - - server.create('scope', { - type: 'org', - scope: { id: globalScope.id, type: globalScope.type } - }, 'withChildren'); - // Auth // Auth methods exist both at the global scope and the org scope. @@ -38,6 +26,9 @@ export default function(server) { // Other resources - server.createList('host-catalog', 6); + server.db.scopes.where({type: 'project'}).forEach(projectScope => { + const scope = { id: projectScope.id, type: projectScope.type }; + server.createList('host-catalog', 2, { scope }); + }); } diff --git a/ui/core/tests/unit/routes/scopes/scope/projects/project/host-catalogs-test.js b/ui/core/tests/unit/routes/scopes/scope/projects/project/host-catalogs-test.js new file mode 100644 index 000000000..dde4328e4 --- /dev/null +++ b/ui/core/tests/unit/routes/scopes/scope/projects/project/host-catalogs-test.js @@ -0,0 +1,11 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Route | scopes/scope/projects/project/host-catalogs', function(hooks) { + setupTest(hooks); + + test('it exists', function(assert) { + let route = this.owner.lookup('route:scopes/scope/projects/project/host-catalogs'); + assert.ok(route); + }); +}); diff --git a/ui/core/tests/unit/routes/scopes/scope/projects/project/host-catalogs/host-catalog-test.js b/ui/core/tests/unit/routes/scopes/scope/projects/project/host-catalogs/host-catalog-test.js new file mode 100644 index 000000000..9cdc47735 --- /dev/null +++ b/ui/core/tests/unit/routes/scopes/scope/projects/project/host-catalogs/host-catalog-test.js @@ -0,0 +1,11 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Route | scopes/scope/projects/project/host-catalogs/host-catalog', function(hooks) { + setupTest(hooks); + + test('it exists', function(assert) { + let route = this.owner.lookup('route:scopes/scope/projects/project/host-catalogs/host-catalog'); + assert.ok(route); + }); +}); diff --git a/ui/core/tests/unit/routes/scopes/scope/projects/project/host-catalogs/host-catalog/index-test.js b/ui/core/tests/unit/routes/scopes/scope/projects/project/host-catalogs/host-catalog/index-test.js new file mode 100644 index 000000000..92f1aa677 --- /dev/null +++ b/ui/core/tests/unit/routes/scopes/scope/projects/project/host-catalogs/host-catalog/index-test.js @@ -0,0 +1,11 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Route | scopes/scope/projects/project/host-catalogs/host-catalog/index', function(hooks) { + setupTest(hooks); + + test('it exists', function(assert) { + let route = this.owner.lookup('route:scopes/scope/projects/project/host-catalogs/host-catalog/index'); + assert.ok(route); + }); +}); diff --git a/ui/core/tests/unit/routes/scopes/scope/projects/project/host-catalogs/index-test.js b/ui/core/tests/unit/routes/scopes/scope/projects/project/host-catalogs/index-test.js new file mode 100644 index 000000000..5cc1d842c --- /dev/null +++ b/ui/core/tests/unit/routes/scopes/scope/projects/project/host-catalogs/index-test.js @@ -0,0 +1,11 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Route | scopes/scope/projects/project/host-catalogs/index', function(hooks) { + setupTest(hooks); + + test('it exists', function(assert) { + let route = this.owner.lookup('route:scopes/scope/projects/project/host-catalogs/index'); + assert.ok(route); + }); +}); diff --git a/ui/core/tests/unit/routes/scopes/scope/projects/project/host-catalogs/new-test.js b/ui/core/tests/unit/routes/scopes/scope/projects/project/host-catalogs/new-test.js new file mode 100644 index 000000000..96ccdff8b --- /dev/null +++ b/ui/core/tests/unit/routes/scopes/scope/projects/project/host-catalogs/new-test.js @@ -0,0 +1,11 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Route | scopes/scope/projects/project/host-catalogs/new', function(hooks) { + setupTest(hooks); + + test('it exists', function(assert) { + let route = this.owner.lookup('route:scopes/scope/projects/project/host-catalogs/new'); + assert.ok(route); + }); +}); From 6fd8106d06be41882a50bcc3116f5b43145bf457 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Tue, 28 Jul 2020 12:04:55 -0400 Subject: [PATCH 52/70] chore: add projects sidebar --- .../app/routes/scopes/scope/projects/project.js | 15 +++++++++++++++ .../scopes/scope/projects/project/_sidebar.hbs | 5 +++++ ui/core/translations/en-us.yaml | 1 + 3 files changed, 21 insertions(+) create mode 100644 ui/core/app/templates/scopes/scope/projects/project/_sidebar.hbs diff --git a/ui/core/app/routes/scopes/scope/projects/project.js b/ui/core/app/routes/scopes/scope/projects/project.js index c2aee8ed3..e5d96124b 100644 --- a/ui/core/app/routes/scopes/scope/projects/project.js +++ b/ui/core/app/routes/scopes/scope/projects/project.js @@ -14,4 +14,19 @@ export default class ScopesScopeProjectsProjectRoute extends Route { return this.store.findRecord('scope', project_id); } + /** + * Renders the project-specific sidebar template. + * @override + * @param {object} controller + * @param {object} model + */ + renderTemplate(controller, model) { + this.render('scopes/scope/projects/project/_sidebar', { + into: 'scopes/scope', + outlet: 'sidebar', + model: model + }); + super.renderTemplate(...arguments); + } + } diff --git a/ui/core/app/templates/scopes/scope/projects/project/_sidebar.hbs b/ui/core/app/templates/scopes/scope/projects/project/_sidebar.hbs new file mode 100644 index 000000000..8f46f6113 --- /dev/null +++ b/ui/core/app/templates/scopes/scope/projects/project/_sidebar.hbs @@ -0,0 +1,5 @@ + + + {{t "resources.host-catalogs"}} + + diff --git a/ui/core/translations/en-us.yaml b/ui/core/translations/en-us.yaml index e4af341c7..135a2f3ea 100644 --- a/ui/core/translations/en-us.yaml +++ b/ui/core/translations/en-us.yaml @@ -33,6 +33,7 @@ resources: titles: iam: IAM new: New + resources: Resources details: Details general: General overview: Overview From f749a200e42096fe808b32c3abfcc88782c369fa Mon Sep 17 00:00:00 2001 From: Susmitha Girumala Date: Tue, 28 Jul 2020 12:10:22 -0400 Subject: [PATCH 53/70] =?UTF-8?q?test:=20=F0=9F=92=8D=20Update=20breadcrum?= =?UTF-8?q?bs=20and=20IAM=20acceptance=20tests=20under=20scope?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ui/core/tests/acceptance/bread-crumbs-test.js | 37 +++++++++----- ui/core/tests/acceptance/groups-test.js | 51 ++++++++++++------- ui/core/tests/acceptance/roles-test.js | 51 ++++++++++++------- ui/core/tests/acceptance/users-test.js | 51 ++++++++++++------- .../controllers/scopes/scope/groups-test.js | 4 +- .../scopes/scope/groups/group-test.js | 4 +- .../scopes/scope/groups/new-test.js | 4 +- .../controllers/scopes/scope/roles-test.js | 4 +- .../scopes/scope/roles/new-test.js | 4 +- .../scopes/scope/roles/role-test.js | 4 +- .../controllers/scopes/scope/users-test.js | 4 +- .../scopes/scope/users/new-test.js | 4 +- .../scopes/scope/users/user-test.js | 4 +- 13 files changed, 138 insertions(+), 88 deletions(-) diff --git a/ui/core/tests/acceptance/bread-crumbs-test.js b/ui/core/tests/acceptance/bread-crumbs-test.js index 2575f1c75..67288382c 100644 --- a/ui/core/tests/acceptance/bread-crumbs-test.js +++ b/ui/core/tests/acceptance/bread-crumbs-test.js @@ -7,7 +7,8 @@ module('Acceptance | breadcrumbs', function (hooks) { setupApplicationTest(hooks); setupMirage(hooks); - let org; + let orgScope; + let getProjectScope; let project; let hostCatalog; let user; @@ -22,33 +23,43 @@ module('Acceptance | breadcrumbs', function (hooks) { let crumbPath; hooks.beforeEach(function () { - org = this.server.create('org'); - project = this.server.create('project'); - user = this.server.create('user'); + orgScope = this.server.create( + 'scope', + { + type: 'org', + }, + 'withChildren' + ); - hostCatalog = this.server.create('host-catalog'); + getProjectScope = () => + this.server.schema.scopes.where({ type: 'project' }).models[0]; + + project = getProjectScope(); + user = this.server.create('user'); role = this.server.create('role'); group = this.server.create('group'); - projectsURL = `/orgs/${org.id}/projects`; + projectsURL = `/scopes/${orgScope.id}/projects`; newProjectURL = `${projectsURL}/new`; projectURL = `${projectsURL}/${project.id}`; - hostCatalogsURL = `${projectURL}/host-catalogs`; - newHostCatalogURL = `${hostCatalogsURL}/new`; - hostCatalogURL = `${hostCatalogsURL}/${hostCatalog.id}`; - - rolesURL = `/orgs/${org.id}/roles`; + rolesURL = `/scopes/${orgScope.id}/roles`; newRoleURL = `${rolesURL}/new`; roleURL = `${rolesURL}/${role.id}`; - groupsURL = `/orgs/${org.id}/groups`; + groupsURL = `/scopes/${orgScope.id}/groups`; newGroupURL = `${groupsURL}/new`; groupURL = `${groupsURL}/${group.id}`; - usersURL = `/orgs/${org.id}/users`; + usersURL = `/scopes/${orgScope.id}/users`; newUserURL = `${usersURL}/${user.id}`; userURL = `${usersURL}/new`; + + // TODO: Update hostcatalog to scopes + hostCatalog = this.server.create('host-catalog'); + hostCatalogsURL = `${projectURL}/host-catalogs`; + newHostCatalogURL = `${hostCatalogsURL}/new`; + hostCatalogURL = `${hostCatalogsURL}/${hostCatalog.id}`; }); test('can navigate via breadcrumbs to projects from project', async function (assert) { diff --git a/ui/core/tests/acceptance/groups-test.js b/ui/core/tests/acceptance/groups-test.js index 5564f9d62..2322f0882 100644 --- a/ui/core/tests/acceptance/groups-test.js +++ b/ui/core/tests/acceptance/groups-test.js @@ -9,24 +9,37 @@ module('Acceptance | groups', function (hooks) { setupApplicationTest(hooks); setupMirage(hooks); + let orgScope; + let groupsURL; + let newGroupURL; + hooks.beforeEach(function () { - this.server.create('org'); + orgScope = this.server.create( + 'scope', + { + type: 'org', + }, + 'withChildren' + ); + + groupsURL = `/scopes/${orgScope.id}/groups`; + newGroupURL = `${groupsURL}/new`; }); test('visiting groups', async function (assert) { assert.expect(1); - await visit('/orgs/1/groups'); + await visit(groupsURL); await a11yAudit(); - assert.equal(currentURL(), '/orgs/1/groups'); + assert.equal(currentURL(), groupsURL); }); test('can create new group', async function (assert) { assert.expect(4); assert.equal(this.server.db.groups.length, 0); - await visit('/orgs/1/groups/new'); + await visit(newGroupURL); await fillIn('[name="name"]', 'group name'); await click('[type="submit"]'); - assert.equal(currentURL(), '/orgs/1/groups/1'); + assert.equal(currentURL(), `${groupsURL}/1`); assert.equal(this.server.db.groups.length, 1); assert.equal(this.server.db.groups[0].name, 'group name'); }); @@ -34,16 +47,16 @@ module('Acceptance | groups', function (hooks) { test('can cancel new group creation', async function (assert) { assert.expect(3); assert.equal(this.server.db.groups.length, 0); - await visit('/orgs/1/groups/new'); + await visit(newGroupURL); await fillIn('[name="name"]', 'group name'); await click('.rose-form-actions [type="button"]'); - assert.equal(currentURL(), '/orgs/1/groups'); + assert.equal(currentURL(), groupsURL); assert.equal(this.server.db.groups.length, 0); }); test('saving a new group with invalid fields displays error messages', async function (assert) { assert.expect(2); - this.server.post('/orgs/:org_id/groups', () => { + this.server.post('/scopes/:scope_id/groups', () => { return new Response( 400, {}, @@ -62,7 +75,7 @@ module('Acceptance | groups', function (hooks) { } ); }); - await visit('/orgs/1/groups/new'); + await visit(newGroupURL); await fillIn('[name="name"]', 'group name'); await click('[type="submit"]'); assert.ok( @@ -80,17 +93,17 @@ module('Acceptance | groups', function (hooks) { test('can save changes to an existing group', async function (assert) { assert.expect(2); this.server.createList('group', 1, { name: 'Admin group' }); - await visit('/orgs/1/groups/1'); + await visit(`${groupsURL}/1`); await fillIn('[name="name"]', 'Updated admin group'); await click('.rose-form-actions [type="submit"]'); - assert.equal(currentURL(), '/orgs/1/groups/1'); + assert.equal(currentURL(), `${groupsURL}/1`); assert.equal(this.server.db.groups[0].name, 'Updated admin group'); }); test('can cancel changes to an existing group', async function (assert) { assert.expect(1); this.server.createList('group', 1, { name: 'Admin group' }); - await visit('/orgs/1/groups/1'); + await visit(`${groupsURL}/1`); await fillIn('[name="name"]', 'Updated admin group'); await click('.rose-form-actions [type="button"]'); assert.equal(find('[name="name"]').value, 'Admin group'); @@ -100,7 +113,7 @@ module('Acceptance | groups', function (hooks) { assert.expect(2); this.server.createList('group', 1); assert.equal(this.server.db.groups.length, 1); - await visit('/orgs/1/groups/1'); + await visit(`${groupsURL}/1`); await click('.rose-button-warning'); assert.equal(this.server.db.groups.length, 0); }); @@ -108,7 +121,7 @@ module('Acceptance | groups', function (hooks) { test('saving an existing group with invalid fields displays error messages', async function (assert) { assert.expect(2); this.server.createList('group', 1); - this.server.patch('/orgs/:org_id/groups/:id', () => { + this.server.patch('/scopes/:scope_id/groups/:id', () => { return new Response( 400, {}, @@ -127,7 +140,7 @@ module('Acceptance | groups', function (hooks) { } ); }); - await visit('/orgs/1/groups/1'); + await visit(`${groupsURL}/1`); await fillIn('[name="name"]', 'random string'); await click('[type="submit"]'); assert.ok( @@ -145,7 +158,7 @@ module('Acceptance | groups', function (hooks) { test('errors are displayed when save project fails', async function (assert) { assert.expect(1); this.server.createList('group', 1); - this.server.patch('/orgs/:org_id/groups/:id', () => { + this.server.patch('/scopes/:scope_id/groups/:id', () => { return new Response( 490, {}, @@ -156,7 +169,7 @@ module('Acceptance | groups', function (hooks) { } ); }); - await visit('/orgs/1/groups/1'); + await visit(`${groupsURL}/1`); await fillIn('[name="name"]', 'Role name'); await click('[type="submit"]'); assert.ok( @@ -169,7 +182,7 @@ module('Acceptance | groups', function (hooks) { test('errors are displayed when delete project fails', async function (assert) { assert.expect(1); this.server.createList('group', 1); - this.server.del('/orgs/:org_id/groups/:id', () => { + this.server.del('/scopes/:scope_id/groups/:id', () => { return new Response( 490, {}, @@ -180,7 +193,7 @@ module('Acceptance | groups', function (hooks) { } ); }); - await visit('/orgs/1/groups/1'); + await visit(`${groupsURL}/1`); await click('.rose-button-warning'); assert.ok( find('[role="alert"]').textContent.trim(), diff --git a/ui/core/tests/acceptance/roles-test.js b/ui/core/tests/acceptance/roles-test.js index 038d367ae..b745a0d28 100644 --- a/ui/core/tests/acceptance/roles-test.js +++ b/ui/core/tests/acceptance/roles-test.js @@ -9,24 +9,37 @@ module('Acceptance | roles', function (hooks) { setupApplicationTest(hooks); setupMirage(hooks); + let orgScope; + let rolesURL; + let newRoleURL; + hooks.beforeEach(function () { - this.server.create('org'); + orgScope = this.server.create( + 'scope', + { + type: 'org', + }, + 'withChildren' + ); + + rolesURL = `/scopes/${orgScope.id}/roles`; + newRoleURL = `${rolesURL}/new`; }); test('visiting roles', async function (assert) { assert.expect(1); - await visit('/orgs/1/roles'); + await visit(rolesURL); await a11yAudit(); - assert.equal(currentURL(), '/orgs/1/roles'); + assert.equal(currentURL(), rolesURL); }); test('can create new role', async function (assert) { assert.expect(4); assert.equal(this.server.db.roles.length, 0); - await visit('/orgs/1/roles/new'); + await visit(newRoleURL); await fillIn('[name="name"]', 'role name'); await click('[type="submit"]'); - assert.equal(currentURL(), '/orgs/1/roles/1'); + assert.equal(currentURL(), `${rolesURL}/1`); assert.equal(this.server.db.roles.length, 1); assert.equal(this.server.db.roles[0].name, 'role name'); }); @@ -34,16 +47,16 @@ module('Acceptance | roles', function (hooks) { test('can cancel new role creation', async function (assert) { assert.expect(3); assert.equal(this.server.db.roles.length, 0); - await visit('/orgs/1/roles/new'); + await visit(newRoleURL); await fillIn('[name="name"]', 'role name'); await click('.rose-form-actions [type="button"]'); - assert.equal(currentURL(), '/orgs/1/roles'); + assert.equal(currentURL(), rolesURL); assert.equal(this.server.db.roles.length, 0); }); test('saving a new role with invalid fields displays error messages', async function (assert) { assert.expect(2); - this.server.post('/orgs/:org_id/roles', () => { + this.server.post('/scopes/:scope_id/roles', () => { return new Response( 400, {}, @@ -62,7 +75,7 @@ module('Acceptance | roles', function (hooks) { } ); }); - await visit('/orgs/1/roles/new'); + await visit(newRoleURL); await fillIn('[name="name"]', 'role name'); await click('[type="submit"]'); assert.ok( @@ -80,17 +93,17 @@ module('Acceptance | roles', function (hooks) { test('can save changes to an existing role', async function (assert) { assert.expect(2); this.server.createList('role', 1, { name: 'Admin role' }); - await visit('/orgs/1/roles/1'); + await visit(`${rolesURL}/1`); await fillIn('[name="name"]', 'Updated admin role'); await click('.rose-form-actions [type="submit"]'); - assert.equal(currentURL(), '/orgs/1/roles/1'); + assert.equal(currentURL(), `${rolesURL}/1`); assert.equal(this.server.db.roles[0].name, 'Updated admin role'); }); test('can cancel changes to an existing role', async function (assert) { assert.expect(1); this.server.createList('role', 1, { name: 'Admin role' }); - await visit('/orgs/1/roles/1'); + await visit(`${rolesURL}/1`); await fillIn('[name="name"]', 'Updated admin role'); await click('.rose-form-actions [type="button"]'); assert.equal(find('[name="name"]').value, 'Admin role'); @@ -100,7 +113,7 @@ module('Acceptance | roles', function (hooks) { assert.expect(2); this.server.createList('role', 1); assert.equal(this.server.db.roles.length, 1); - await visit('/orgs/1/roles/1'); + await visit(`${rolesURL}/1`); await click('.rose-button-warning'); assert.equal(this.server.db.roles.length, 0); }); @@ -108,7 +121,7 @@ module('Acceptance | roles', function (hooks) { test('saving an existing role with invalid fields displays error messages', async function (assert) { assert.expect(2); this.server.createList('role', 1); - this.server.patch('/orgs/:org_id/roles/:id', () => { + this.server.patch('/scopes/:scope_id/roles/:id', () => { return new Response( 400, {}, @@ -127,7 +140,7 @@ module('Acceptance | roles', function (hooks) { } ); }); - await visit('/orgs/1/roles/1'); + await visit(`${rolesURL}/1`); await fillIn('[name="name"]', 'random string'); await click('[type="submit"]'); assert.ok( @@ -145,7 +158,7 @@ module('Acceptance | roles', function (hooks) { test('errors are displayed when save project fails', async function (assert) { assert.expect(1); this.server.createList('role', 1); - this.server.patch('/orgs/:org_id/roles/:id', () => { + this.server.patch('/scopes/:scope_id/roles/:id', () => { return new Response( 490, {}, @@ -156,7 +169,7 @@ module('Acceptance | roles', function (hooks) { } ); }); - await visit('/orgs/1/roles/1'); + await visit(`${rolesURL}/1`); await fillIn('[name="name"]', 'Role name'); await click('[type="submit"]'); assert.ok( @@ -169,7 +182,7 @@ module('Acceptance | roles', function (hooks) { test('errors are displayed when delete project fails', async function (assert) { assert.expect(1); this.server.createList('role', 1); - this.server.del('/orgs/:org_id/roles/:id', () => { + this.server.del('/scopes/:scope_id/roles/:id', () => { return new Response( 490, {}, @@ -180,7 +193,7 @@ module('Acceptance | roles', function (hooks) { } ); }); - await visit('/orgs/1/roles/1'); + await visit(`${rolesURL}/1`); await click('.rose-button-warning'); assert.ok( find('[role="alert"]').textContent.trim(), diff --git a/ui/core/tests/acceptance/users-test.js b/ui/core/tests/acceptance/users-test.js index af68f19bc..85b187d56 100644 --- a/ui/core/tests/acceptance/users-test.js +++ b/ui/core/tests/acceptance/users-test.js @@ -9,25 +9,38 @@ module('Acceptance | users', function (hooks) { setupApplicationTest(hooks); setupMirage(hooks); + let orgScope; + let usersURL; + let newUserURL; + hooks.beforeEach(function () { - this.server.create('org'); + orgScope = this.server.create( + 'scope', + { + type: 'org', + }, + 'withChildren' + ); + + usersURL = `/scopes/${orgScope.id}/users`; + newUserURL = `${usersURL}/new`; }); test('visiting users', async function (assert) { assert.expect(1); this.server.createList('user', 1); - await visit('/orgs/1/users'); + await visit(usersURL); await a11yAudit(); - assert.equal(currentURL(), '/orgs/1/users'); + assert.equal(currentURL(), usersURL); }); test('can create new users', async function (assert) { assert.expect(4); assert.equal(this.server.db.users.length, 0); - await visit('/orgs/1/users/new'); + await visit(newUserURL); await fillIn('[name="name"]', 'User name'); await click('[type="submit"]'); - assert.equal(currentURL(), '/orgs/1/users/1'); + assert.equal(currentURL(), `${usersURL}/1`); assert.equal(this.server.db.users.length, 1); assert.equal(this.server.db.users[0].name, 'User name'); }); @@ -35,16 +48,16 @@ module('Acceptance | users', function (hooks) { test('can cancel creation of a new user', async function (assert) { assert.expect(3); assert.equal(this.server.db.users.length, 0); - await visit('/orgs/1/users/new'); + await visit(newUserURL); await fillIn('[name="name"]', 'User name'); await click('.rose-form-actions [type="button"]'); - assert.equal(currentURL(), '/orgs/1/users'); + assert.equal(currentURL(), usersURL); assert.equal(this.server.db.users.length, 0); }); test('saving a new user with invalid fields displays error messages', async function (assert) { assert.expect(2); - this.server.post('/orgs/:org_id/users', () => { + this.server.post('scopes/:scope_id/users', () => { return new Response( 400, {}, @@ -63,7 +76,7 @@ module('Acceptance | users', function (hooks) { } ); }); - await visit('/orgs/1/users/new'); + await visit(newUserURL); await fillIn('[name="name"]', 'User name'); await click('[type="submit"]'); assert.ok( @@ -81,17 +94,17 @@ module('Acceptance | users', function (hooks) { test('can save changes to an existing user', async function (assert) { assert.expect(2); this.server.createList('user', 1, { name: 'Test User' }); - await visit('/orgs/1/users/1'); + await visit(`${usersURL}/1`); await fillIn('[name="name"]', 'Updated user name'); await click('.rose-form-actions [type="submit"]'); - assert.equal(currentURL(), '/orgs/1/users/1'); + assert.equal(currentURL(), `${usersURL}/1`); assert.equal(this.server.db.users[0].name, 'Updated user name'); }); test('can cancel changes to an existing user', async function (assert) { assert.expect(1); this.server.createList('user', 1, { name: 'Test User' }); - await visit('/orgs/1/users/1'); + await visit(`${usersURL}/1`); await fillIn('[name="name"]', 'Unsaved user name'); await click('.rose-form-actions [type="button"]'); assert.equal(find('[name="name"]').value, 'Test User'); @@ -101,7 +114,7 @@ module('Acceptance | users', function (hooks) { assert.expect(2); this.server.createList('user', 1); assert.equal(this.server.db.users.length, 1); - await visit('/orgs/1/users/1'); + await visit(`${usersURL}/1`); await click('.rose-button-warning'); assert.equal(this.server.db.users.length, 0); }); @@ -109,7 +122,7 @@ module('Acceptance | users', function (hooks) { test('saving an existing user with invalid fields displays error messages', async function (assert) { assert.expect(2); this.server.createList('user', 1); - this.server.patch('/orgs/:org_id/users/:id', () => { + this.server.patch('/scopes/:scope_id/users/:id', () => { return new Response( 400, {}, @@ -128,7 +141,7 @@ module('Acceptance | users', function (hooks) { } ); }); - await visit('/orgs/1/users/1'); + await visit(`${usersURL}/1`); await fillIn('[name="name"]', 'User name'); await click('[type="submit"]'); assert.ok( @@ -146,7 +159,7 @@ module('Acceptance | users', function (hooks) { test('errors are displayed when saving user fails', async function (assert) { assert.expect(1); this.server.createList('user', 1); - this.server.patch('/orgs/:org_id/users/:id', () => { + this.server.patch('/scopes/:scope_id/users/:id', () => { return new Response( 490, {}, @@ -157,7 +170,7 @@ module('Acceptance | users', function (hooks) { } ); }); - await visit('/orgs/1/users/1'); + await visit(`${usersURL}/1`); await fillIn('[name="name"]', 'User name'); await click('[type="submit"]'); assert.ok( @@ -170,7 +183,7 @@ module('Acceptance | users', function (hooks) { test('errors are displayed when user deletion fails', async function (assert) { assert.expect(1); this.server.createList('user', 1); - this.server.del('/orgs/:org_id/users/:id', () => { + this.server.del('/scopes/:scope_id/users/:id', () => { return new Response( 490, {}, @@ -181,7 +194,7 @@ module('Acceptance | users', function (hooks) { } ); }); - await visit('/orgs/1/users/1'); + await visit(`${usersURL}/1`); await click('.rose-button-warning'); assert.ok( find('[role="alert"]').textContent.trim(), diff --git a/ui/core/tests/unit/controllers/scopes/scope/groups-test.js b/ui/core/tests/unit/controllers/scopes/scope/groups-test.js index a7baaa19e..4f1a10f19 100644 --- a/ui/core/tests/unit/controllers/scopes/scope/groups-test.js +++ b/ui/core/tests/unit/controllers/scopes/scope/groups-test.js @@ -1,11 +1,11 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; -module('Unit | Controller | scopes/scope/groups', function(hooks) { +module('Unit | Controller | scopes/scope/groups', function (hooks) { setupTest(hooks); // Replace this with your real tests. - test('it exists', function(assert) { + test('it exists', function (assert) { let controller = this.owner.lookup('controller:scopes/scope/groups'); assert.ok(controller); }); diff --git a/ui/core/tests/unit/controllers/scopes/scope/groups/group-test.js b/ui/core/tests/unit/controllers/scopes/scope/groups/group-test.js index 13d2aeb1d..bb3e8b7cc 100644 --- a/ui/core/tests/unit/controllers/scopes/scope/groups/group-test.js +++ b/ui/core/tests/unit/controllers/scopes/scope/groups/group-test.js @@ -1,11 +1,11 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; -module('Unit | Controller | scopes/scope/groups/group', function(hooks) { +module('Unit | Controller | scopes/scope/groups/group', function (hooks) { setupTest(hooks); // Replace this with your real tests. - test('it exists', function(assert) { + test('it exists', function (assert) { let controller = this.owner.lookup('controller:scopes/scope/groups/group'); assert.ok(controller); }); diff --git a/ui/core/tests/unit/controllers/scopes/scope/groups/new-test.js b/ui/core/tests/unit/controllers/scopes/scope/groups/new-test.js index a9290297d..56eaa6c76 100644 --- a/ui/core/tests/unit/controllers/scopes/scope/groups/new-test.js +++ b/ui/core/tests/unit/controllers/scopes/scope/groups/new-test.js @@ -1,11 +1,11 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; -module('Unit | Controller | scopes/scope/groups/new', function(hooks) { +module('Unit | Controller | scopes/scope/groups/new', function (hooks) { setupTest(hooks); // Replace this with your real tests. - test('it exists', function(assert) { + test('it exists', function (assert) { let controller = this.owner.lookup('controller:scopes/scope/groups/new'); assert.ok(controller); }); diff --git a/ui/core/tests/unit/controllers/scopes/scope/roles-test.js b/ui/core/tests/unit/controllers/scopes/scope/roles-test.js index 23fd61372..1567c0d37 100644 --- a/ui/core/tests/unit/controllers/scopes/scope/roles-test.js +++ b/ui/core/tests/unit/controllers/scopes/scope/roles-test.js @@ -1,11 +1,11 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; -module('Unit | Controller | scopes/scope/roles', function(hooks) { +module('Unit | Controller | scopes/scope/roles', function (hooks) { setupTest(hooks); // Replace this with your real tests. - test('it exists', function(assert) { + test('it exists', function (assert) { let controller = this.owner.lookup('controller:scopes/scope/roles'); assert.ok(controller); }); diff --git a/ui/core/tests/unit/controllers/scopes/scope/roles/new-test.js b/ui/core/tests/unit/controllers/scopes/scope/roles/new-test.js index 74245d386..ee2595d36 100644 --- a/ui/core/tests/unit/controllers/scopes/scope/roles/new-test.js +++ b/ui/core/tests/unit/controllers/scopes/scope/roles/new-test.js @@ -1,11 +1,11 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; -module('Unit | Controller | scopes/scope/roles/new', function(hooks) { +module('Unit | Controller | scopes/scope/roles/new', function (hooks) { setupTest(hooks); // Replace this with your real tests. - test('it exists', function(assert) { + test('it exists', function (assert) { let controller = this.owner.lookup('controller:scopes/scope/roles/new'); assert.ok(controller); }); diff --git a/ui/core/tests/unit/controllers/scopes/scope/roles/role-test.js b/ui/core/tests/unit/controllers/scopes/scope/roles/role-test.js index b6564723f..4edaa508f 100644 --- a/ui/core/tests/unit/controllers/scopes/scope/roles/role-test.js +++ b/ui/core/tests/unit/controllers/scopes/scope/roles/role-test.js @@ -1,11 +1,11 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; -module('Unit | Controller | scopes/scope/roles/role', function(hooks) { +module('Unit | Controller | scopes/scope/roles/role', function (hooks) { setupTest(hooks); // Replace this with your real tests. - test('it exists', function(assert) { + test('it exists', function (assert) { let controller = this.owner.lookup('controller:scopes/scope/roles/role'); assert.ok(controller); }); diff --git a/ui/core/tests/unit/controllers/scopes/scope/users-test.js b/ui/core/tests/unit/controllers/scopes/scope/users-test.js index 0b5ab25d4..25ced1669 100644 --- a/ui/core/tests/unit/controllers/scopes/scope/users-test.js +++ b/ui/core/tests/unit/controllers/scopes/scope/users-test.js @@ -1,11 +1,11 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; -module('Unit | Controller | scopes/scope/users', function(hooks) { +module('Unit | Controller | scopes/scope/users', function (hooks) { setupTest(hooks); // Replace this with your real tests. - test('it exists', function(assert) { + test('it exists', function (assert) { let controller = this.owner.lookup('controller:scopes/scope/users'); assert.ok(controller); }); diff --git a/ui/core/tests/unit/controllers/scopes/scope/users/new-test.js b/ui/core/tests/unit/controllers/scopes/scope/users/new-test.js index fd291f809..440b4523c 100644 --- a/ui/core/tests/unit/controllers/scopes/scope/users/new-test.js +++ b/ui/core/tests/unit/controllers/scopes/scope/users/new-test.js @@ -1,11 +1,11 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; -module('Unit | Controller | scopes/scope/users/new', function(hooks) { +module('Unit | Controller | scopes/scope/users/new', function (hooks) { setupTest(hooks); // Replace this with your real tests. - test('it exists', function(assert) { + test('it exists', function (assert) { let controller = this.owner.lookup('controller:scopes/scope/users/new'); assert.ok(controller); }); diff --git a/ui/core/tests/unit/controllers/scopes/scope/users/user-test.js b/ui/core/tests/unit/controllers/scopes/scope/users/user-test.js index c4a2ef171..17d292de0 100644 --- a/ui/core/tests/unit/controllers/scopes/scope/users/user-test.js +++ b/ui/core/tests/unit/controllers/scopes/scope/users/user-test.js @@ -1,11 +1,11 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; -module('Unit | Controller | scopes/scope/users/user', function(hooks) { +module('Unit | Controller | scopes/scope/users/user', function (hooks) { setupTest(hooks); // Replace this with your real tests. - test('it exists', function(assert) { + test('it exists', function (assert) { let controller = this.owner.lookup('controller:scopes/scope/users/user'); assert.ok(controller); }); From 234dc2a746d4ffd90674d0eed95824336144369a Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Tue, 28 Jul 2020 12:17:30 -0400 Subject: [PATCH 54/70] chore: add host catalog edit --- .../scope/projects/project/host-catalogs.js | 19 ++++++++++ .../project/host-catalogs/host-catalog.js | 14 ++++++++ .../project/host-catalogs/host-catalog.hbs | 4 ++- .../host-catalogs/host-catalog/index.hbs | 36 ++++++++++++++++++- .../projects/project/host-catalogs/index.hbs | 9 +++-- .../projects/project/host-catalogs-test.js | 12 +++++++ .../host-catalogs/host-catalog-test.js | 12 +++++++ ui/core/translations/en-us.yaml | 1 + 8 files changed, 102 insertions(+), 5 deletions(-) create mode 100644 ui/core/app/controllers/scopes/scope/projects/project/host-catalogs.js create mode 100644 ui/core/app/controllers/scopes/scope/projects/project/host-catalogs/host-catalog.js create mode 100644 ui/core/tests/unit/controllers/scopes/scope/projects/project/host-catalogs-test.js create mode 100644 ui/core/tests/unit/controllers/scopes/scope/projects/project/host-catalogs/host-catalog-test.js diff --git a/ui/core/app/controllers/scopes/scope/projects/project/host-catalogs.js b/ui/core/app/controllers/scopes/scope/projects/project/host-catalogs.js new file mode 100644 index 000000000..854de55e2 --- /dev/null +++ b/ui/core/app/controllers/scopes/scope/projects/project/host-catalogs.js @@ -0,0 +1,19 @@ +import Controller from '@ember/controller'; +import { inject as service } from '@ember/service'; + +export default class ScopesScopeProjectsProjectHostCatalogsController extends Controller { + + // =services + + @service intl; + + // =attributes + + /** + * @type {string} + */ + get breadCrumb() { + return this.intl.t('resources.host-catalogs'); + } + +} diff --git a/ui/core/app/controllers/scopes/scope/projects/project/host-catalogs/host-catalog.js b/ui/core/app/controllers/scopes/scope/projects/project/host-catalogs/host-catalog.js new file mode 100644 index 000000000..97fa6533b --- /dev/null +++ b/ui/core/app/controllers/scopes/scope/projects/project/host-catalogs/host-catalog.js @@ -0,0 +1,14 @@ +import Controller from '@ember/controller'; + +export default class ScopesScopeProjectsProjectHostCatalogsHostCatalogController extends Controller { + + // =attributes + + /** + * @type {string} + */ + get breadCrumb() { + return this.model.displayName; + } + +} diff --git a/ui/core/app/templates/scopes/scope/projects/project/host-catalogs/host-catalog.hbs b/ui/core/app/templates/scopes/scope/projects/project/host-catalogs/host-catalog.hbs index e2147cab0..d66e83390 100644 --- a/ui/core/app/templates/scopes/scope/projects/project/host-catalogs/host-catalog.hbs +++ b/ui/core/app/templates/scopes/scope/projects/project/host-catalogs/host-catalog.hbs @@ -1 +1,3 @@ -{{outlet}} \ No newline at end of file +{{title @model.displayName}} + +{{outlet}} diff --git a/ui/core/app/templates/scopes/scope/projects/project/host-catalogs/host-catalog/index.hbs b/ui/core/app/templates/scopes/scope/projects/project/host-catalogs/host-catalog/index.hbs index e2147cab0..cb0a4a0cf 100644 --- a/ui/core/app/templates/scopes/scope/projects/project/host-catalogs/host-catalog/index.hbs +++ b/ui/core/app/templates/scopes/scope/projects/project/host-catalogs/host-catalog/index.hbs @@ -1 +1,35 @@ -{{outlet}} \ No newline at end of file + + + + + + + +

{{t "resources.host-catalog"}}

+
+ + + + + {{t 'titles.details'}} + + + + + + + + + {{t 'actions.delete'}} + + + +
diff --git a/ui/core/app/templates/scopes/scope/projects/project/host-catalogs/index.hbs b/ui/core/app/templates/scopes/scope/projects/project/host-catalogs/index.hbs index 935cf9f30..8cc81e3db 100644 --- a/ui/core/app/templates/scopes/scope/projects/project/host-catalogs/index.hbs +++ b/ui/core/app/templates/scopes/scope/projects/project/host-catalogs/index.hbs @@ -24,24 +24,27 @@ + {{t "form.label.id"}} {{t "form.label.name"}} {{t "form.label.type"}} - {{t "form.label.description"}} {{#each @model as |hostCatalog|}} + + {{hostCatalog.id}} + - {{hostCatalog.name}} + {{hostCatalog.displayName}} +

{{hostCatalog.description}}

{{hostCatalog.type}} - {{hostCatalog.description}}
{{/each}}
diff --git a/ui/core/tests/unit/controllers/scopes/scope/projects/project/host-catalogs-test.js b/ui/core/tests/unit/controllers/scopes/scope/projects/project/host-catalogs-test.js new file mode 100644 index 000000000..ce6e91cd3 --- /dev/null +++ b/ui/core/tests/unit/controllers/scopes/scope/projects/project/host-catalogs-test.js @@ -0,0 +1,12 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Controller | scopes/scope/projects/project/host-catalogs', function(hooks) { + setupTest(hooks); + + // Replace this with your real tests. + test('it exists', function(assert) { + let controller = this.owner.lookup('controller:scopes/scope/projects/project/host-catalogs'); + assert.ok(controller); + }); +}); diff --git a/ui/core/tests/unit/controllers/scopes/scope/projects/project/host-catalogs/host-catalog-test.js b/ui/core/tests/unit/controllers/scopes/scope/projects/project/host-catalogs/host-catalog-test.js new file mode 100644 index 000000000..b93e2d1ce --- /dev/null +++ b/ui/core/tests/unit/controllers/scopes/scope/projects/project/host-catalogs/host-catalog-test.js @@ -0,0 +1,12 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Controller | scopes/scope/projects/project/host-catalogs/host-catalog', function(hooks) { + setupTest(hooks); + + // Replace this with your real tests. + test('it exists', function(assert) { + let controller = this.owner.lookup('controller:scopes/scope/projects/project/host-catalogs/host-catalog'); + assert.ok(controller); + }); +}); diff --git a/ui/core/translations/en-us.yaml b/ui/core/translations/en-us.yaml index 135a2f3ea..af1322a87 100644 --- a/ui/core/translations/en-us.yaml +++ b/ui/core/translations/en-us.yaml @@ -59,6 +59,7 @@ states: error: Error form: label: + id: ID name: Name description: Description type: Type From ee6c6626c990bbb235aa493dc79b3ff7350306f4 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Tue, 28 Jul 2020 12:21:45 -0400 Subject: [PATCH 55/70] chore: add new host catalog logic --- .../projects/project/host-catalogs/new.js | 12 ++++++++++ .../projects/project/host-catalogs/new.hbs | 22 ++++++++++++++++++- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/new.js b/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/new.js index 9e076cd1d..923eb7ac3 100644 --- a/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/new.js +++ b/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/new.js @@ -1,4 +1,16 @@ import Route from '@ember/routing/route'; export default class ScopesScopeProjectsProjectHostCatalogsNewRoute extends Route { + + // =methods + + /** + * Creates a new unsaved host catalog belonging to the current project scope. + * @return {HostCatalogModel} + */ + model() { + const scopeID = this.modelFor('scopes.scope.projects.project').id; + return this.store.createRecord('host-catalog', { scopeID }); + } + } diff --git a/ui/core/app/templates/scopes/scope/projects/project/host-catalogs/new.hbs b/ui/core/app/templates/scopes/scope/projects/project/host-catalogs/new.hbs index e2147cab0..0d969f874 100644 --- a/ui/core/app/templates/scopes/scope/projects/project/host-catalogs/new.hbs +++ b/ui/core/app/templates/scopes/scope/projects/project/host-catalogs/new.hbs @@ -1 +1,21 @@ -{{outlet}} \ No newline at end of file +{{title (t 'titles.new')}} + + + + + + + + +

{{t "titles.new"}}

+
+ + + + + +
From 830e6b4116b6410566b732639a00c33aed5e65ac Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Tue, 28 Jul 2020 13:20:48 -0400 Subject: [PATCH 56/70] chore: simplify new model instantiation --- .../routes/scopes/scope/projects/project/host-catalogs/new.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/new.js b/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/new.js index 923eb7ac3..badecca3b 100644 --- a/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/new.js +++ b/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/new.js @@ -9,8 +9,8 @@ export default class ScopesScopeProjectsProjectHostCatalogsNewRoute extends Rout * @return {HostCatalogModel} */ model() { - const scopeID = this.modelFor('scopes.scope.projects.project').id; - return this.store.createRecord('host-catalog', { scopeID }); + const scopeModel = this.modelFor('scopes.scope.projects.project'); + return this.store.createRecord('host-catalog', { scopeModel }); } } From 65e0a73305a0a9fd31934ecfc11869c4e47a52b3 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Tue, 28 Jul 2020 15:16:55 -0400 Subject: [PATCH 57/70] chore: ensure host catalog type is saved --- .../scopes/scope/projects/project/host-catalogs/new.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/new.js b/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/new.js index badecca3b..22097c23d 100644 --- a/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/new.js +++ b/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/new.js @@ -10,7 +10,10 @@ export default class ScopesScopeProjectsProjectHostCatalogsNewRoute extends Rout */ model() { const scopeModel = this.modelFor('scopes.scope.projects.project'); - return this.store.createRecord('host-catalog', { scopeModel }); + return this.store.createRecord('host-catalog', { + type: 'static', + scopeModel + }); } } From 5bcbdafd82eb3a37bce84cd5467a27a191f0aa51 Mon Sep 17 00:00:00 2001 From: Susmitha Girumala Date: Tue, 28 Jul 2020 16:55:40 -0400 Subject: [PATCH 58/70] =?UTF-8?q?chore:=20=F0=9F=A4=96=20Update=20IAM=20br?= =?UTF-8?q?eadcrumbs=20to=20displayname=20instead=20of=20id?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ui/core/app/controllers/scopes/scope/groups/group.js | 2 +- ui/core/app/controllers/scopes/scope/roles/role.js | 2 +- ui/core/app/controllers/scopes/scope/users/user.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ui/core/app/controllers/scopes/scope/groups/group.js b/ui/core/app/controllers/scopes/scope/groups/group.js index 121559b29..dcd1e5296 100644 --- a/ui/core/app/controllers/scopes/scope/groups/group.js +++ b/ui/core/app/controllers/scopes/scope/groups/group.js @@ -8,6 +8,6 @@ export default class ScopesScopeGroupsGroupController extends Controller { * @type {string} */ get breadCrumb() { - return this.model.id; + return this.model.displayName; } } diff --git a/ui/core/app/controllers/scopes/scope/roles/role.js b/ui/core/app/controllers/scopes/scope/roles/role.js index f18b1df9a..39ba35717 100644 --- a/ui/core/app/controllers/scopes/scope/roles/role.js +++ b/ui/core/app/controllers/scopes/scope/roles/role.js @@ -8,6 +8,6 @@ export default class ScopesScopeRolesRoleController extends Controller { * @type {string} */ get breadCrumb() { - return this.model.id; + return this.model.displayName; } } diff --git a/ui/core/app/controllers/scopes/scope/users/user.js b/ui/core/app/controllers/scopes/scope/users/user.js index 9f0456fa4..6f7d42c7a 100644 --- a/ui/core/app/controllers/scopes/scope/users/user.js +++ b/ui/core/app/controllers/scopes/scope/users/user.js @@ -8,6 +8,6 @@ export default class ScopesScopeUsersUserController extends Controller { * @type {string} */ get breadCrumb() { - return this.model.id; + return this.model.displayName; } } From a62630f57277ae4dbdd1baf88e6c13bab43fa8bd Mon Sep 17 00:00:00 2001 From: Susmitha Girumala Date: Tue, 28 Jul 2020 17:09:29 -0400 Subject: [PATCH 59/70] =?UTF-8?q?docs:=20=E2=9C=8F=EF=B8=8F=20Update=20cre?= =?UTF-8?q?ate,=20save,=20and=20delete=20i18n=20notify=20strings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In addition, notify on create, save, and delete before route changes for consistency --- ui/core/app/routes/scopes/scope/groups.js | 7 +++++-- ui/core/app/routes/scopes/scope/projects.js | 10 ++++++---- ui/core/app/routes/scopes/scope/roles.js | 7 +++++-- ui/core/app/routes/scopes/scope/users.js | 7 +++++-- ui/core/translations/en-us.yaml | 13 +++---------- 5 files changed, 24 insertions(+), 20 deletions(-) diff --git a/ui/core/app/routes/scopes/scope/groups.js b/ui/core/app/routes/scopes/scope/groups.js index 0344e53f1..8c872a9c2 100644 --- a/ui/core/app/routes/scopes/scope/groups.js +++ b/ui/core/app/routes/scopes/scope/groups.js @@ -36,10 +36,13 @@ export default class ScopesScopeGroupsRoute extends Route { */ @action async save(group) { + const { isNew } = group; try { await group.save(this.scopeAdapterOptions()); this.refresh(); - this.notify.success(this.intl.t('notify.save-success')); + this.notify.success(this.intl.t( + isNew ? 'notify.create-success' : 'notify.save-success' + )); this.transitionTo('scopes.scope.groups.group', group); } catch (error) { //TODO: replace with translated strings @@ -56,8 +59,8 @@ export default class ScopesScopeGroupsRoute extends Route { try { await group.destroyRecord(this.scopeAdapterOptions()); this.refresh(); + this.notify.success(this.intl.t('notify.delete-success')); this.transitionTo('scopes.scope.groups'); - this.notify.success(this.intl.t('notify.group.delete-success')); } catch (error) { //TODO: replace with translated strings this.notify.error(error.message, { closeAfter: null }); diff --git a/ui/core/app/routes/scopes/scope/projects.js b/ui/core/app/routes/scopes/scope/projects.js index 775dc5ad6..09a305150 100644 --- a/ui/core/app/routes/scopes/scope/projects.js +++ b/ui/core/app/routes/scopes/scope/projects.js @@ -5,6 +5,7 @@ import { action } from '@ember/object'; export default class ScopesScopeProjectsRoute extends Route { // =services + @service intl; @service notify; // =methods @@ -52,11 +53,13 @@ export default class ScopesScopeProjectsRoute extends Route { */ @action async save(project) { + const { isNew } = project; try { await project.save(); this.refresh(); - // TODO: replace with translated strings - this.notify.success('Save succeeded.'); + this.notify.success(this.intl.t( + isNew ? 'notify.create-success' : 'notify.save-success' + )); this.transitionTo('scopes.scope.projects.project', project); } catch (error) { // TODO: replace with translated strings @@ -73,9 +76,8 @@ export default class ScopesScopeProjectsRoute extends Route { try { await project.destroyRecord(); this.refresh(); + this.notify.success('notify.delete-success'); this.transitionTo('scopes.scope.projects'); - // TODO: replace with translated strings - this.notify.success('Deleted project succesfully.'); } catch (error) { // TODO: replace with translated strings this.notify.error(error.message, { closeAfter: null }); diff --git a/ui/core/app/routes/scopes/scope/roles.js b/ui/core/app/routes/scopes/scope/roles.js index fd451c719..5b6b45106 100644 --- a/ui/core/app/routes/scopes/scope/roles.js +++ b/ui/core/app/routes/scopes/scope/roles.js @@ -36,10 +36,13 @@ export default class ScopesScopeRolesRoute extends Route { */ @action async save(role) { + const { isNew } = role; try { await role.save(this.scopeAdapterOptions()); this.refresh(); - this.notify.success(this.intl.t('notify.save-success')); + this.notify.success(this.intl.t( + isNew ? 'notify.create-success' : 'notify.save-success' + )); this.transitionTo('scopes.scope.roles.role', role); } catch (error) { // TODO: replace with translated strings @@ -56,7 +59,7 @@ export default class ScopesScopeRolesRoute extends Route { try { await role.destroyRecord(this.scopeAdapterOptions()); this.refresh(); - this.notify.success(this.intl.t('notify.role.delete-success')); + this.notify.success(this.intl.t('notify.delete-success')); this.transitionTo('scopes.scope.roles'); } catch (error) { // TODO: replace with translated strings diff --git a/ui/core/app/routes/scopes/scope/users.js b/ui/core/app/routes/scopes/scope/users.js index 8f4f65905..e70c25f2f 100644 --- a/ui/core/app/routes/scopes/scope/users.js +++ b/ui/core/app/routes/scopes/scope/users.js @@ -37,10 +37,13 @@ export default class ScopesScopeUsersRoute extends Route { */ @action async save(user) { + const { isNew } = user; try { await user.save(this.scopeAdapterOptions()); this.refresh(); - this.notify.success(this.intl.t('notify.save-success')); + this.notify.success(this.intl.t( + isNew ? 'notify.create-success' : 'notify.save-success' + )); this.transitionTo('scopes.scope.users.user', user); } catch (error) { // TODO: replace with translated strings @@ -57,8 +60,8 @@ export default class ScopesScopeUsersRoute extends Route { try { await user.destroyRecord(this.scopeAdapterOptions()); this.refresh(); + this.notify.success(this.intl.t('notify.delete-success')); this.transitionTo('scopes.scope.users'); - this.notify.success(this.intl.t('notify.user.delete-success')); } catch (error) { // TODO: replace with translated strings this.notify.error(error.message, { closeAfter: null }); diff --git a/ui/core/translations/en-us.yaml b/ui/core/translations/en-us.yaml index e770de25c..22e522072 100644 --- a/ui/core/translations/en-us.yaml +++ b/ui/core/translations/en-us.yaml @@ -60,16 +60,9 @@ errors: server-error: We ran into a problem and could not continue. You can ask your administrator or try again later. unknown-error: Please contact your administrator or try again later. notify: - save-success: Save succeeded. - user: - create-success: User created succesfully. - delete-success: Deleted user successfully. - role: - create-success: Role created succesfully. - delete-success: Deleted role successfully. - group: - create-success: Group created succesfully. - delete-success: Deleted group successfully. + create-success: Created succesfully. + save-success: Saved succesfully. + delete-success: Deleted successfully. dev: default-org: Default Org menu-item: Menu item From f3e5978e2b780cc31d52bcdbd755d1589da08d4e Mon Sep 17 00:00:00 2001 From: Susmitha Girumala Date: Tue, 28 Jul 2020 17:56:49 -0400 Subject: [PATCH 60/70] =?UTF-8?q?chore:=20=F0=9F=A4=96=20Update=20IAM=20sc?= =?UTF-8?q?ope=20adapteroptions=20for=20find,=20save,=20&=20del?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ui/core/app/routes/scopes/scope/groups.js | 7 +++---- ui/core/app/routes/scopes/scope/groups/group.js | 5 ++--- ui/core/app/routes/scopes/scope/groups/new.js | 3 ++- ui/core/app/routes/scopes/scope/roles.js | 7 +++---- ui/core/app/routes/scopes/scope/roles/new.js | 3 ++- ui/core/app/routes/scopes/scope/roles/role.js | 5 ++--- ui/core/app/routes/scopes/scope/users.js | 7 +++---- ui/core/app/routes/scopes/scope/users/new.js | 3 ++- ui/core/app/routes/scopes/scope/users/user.js | 5 ++--- 9 files changed, 21 insertions(+), 24 deletions(-) diff --git a/ui/core/app/routes/scopes/scope/groups.js b/ui/core/app/routes/scopes/scope/groups.js index 8c872a9c2..94441ca1e 100644 --- a/ui/core/app/routes/scopes/scope/groups.js +++ b/ui/core/app/routes/scopes/scope/groups.js @@ -38,7 +38,7 @@ export default class ScopesScopeGroupsRoute extends Route { async save(group) { const { isNew } = group; try { - await group.save(this.scopeAdapterOptions()); + await group.save(); this.refresh(); this.notify.success(this.intl.t( isNew ? 'notify.create-success' : 'notify.save-success' @@ -68,8 +68,7 @@ export default class ScopesScopeGroupsRoute extends Route { } scopeAdapterOptions() { - const { id: scope_id } = this.modelFor('scopes.scope'); - const adapterOptions = { scopeID: scope_id }; - return { adapterOptions }; + const { id: scopeID } = this.modelFor('scopes.scope'); + return { adapterOptions: { scopeID } }; } } diff --git a/ui/core/app/routes/scopes/scope/groups/group.js b/ui/core/app/routes/scopes/scope/groups/group.js index da536ed07..ce0585e8d 100644 --- a/ui/core/app/routes/scopes/scope/groups/group.js +++ b/ui/core/app/routes/scopes/scope/groups/group.js @@ -10,8 +10,7 @@ export default class ScopesScopeGroupsGroupRoute extends Route { * @return {groupModel} */ async model({ group_id }) { - const { id: scope_id } = this.modelFor('scopes.scope'); - const adapterOptions = { scopeID: scope_id }; - return this.store.findRecord('group', group_id, { adapterOptions }); + const { id: scopeID } = this.modelFor('scopes.scope'); + return this.store.findRecord('group', group_id, { adapterOptions: { scopeID } }); } } diff --git a/ui/core/app/routes/scopes/scope/groups/new.js b/ui/core/app/routes/scopes/scope/groups/new.js index e33233f5f..3326dc7e4 100644 --- a/ui/core/app/routes/scopes/scope/groups/new.js +++ b/ui/core/app/routes/scopes/scope/groups/new.js @@ -8,6 +8,7 @@ export default class ScopesScopeGroupsNewRoute extends Route { * @return {GroupModel} */ model() { - return this.store.createRecord('group'); + const { id: scopeID } = this.modelFor('scopes.scope'); + return this.store.createRecord('group', { scopeID }); } } diff --git a/ui/core/app/routes/scopes/scope/roles.js b/ui/core/app/routes/scopes/scope/roles.js index 5b6b45106..a4d9d2c5b 100644 --- a/ui/core/app/routes/scopes/scope/roles.js +++ b/ui/core/app/routes/scopes/scope/roles.js @@ -38,7 +38,7 @@ export default class ScopesScopeRolesRoute extends Route { async save(role) { const { isNew } = role; try { - await role.save(this.scopeAdapterOptions()); + await role.save(); this.refresh(); this.notify.success(this.intl.t( isNew ? 'notify.create-success' : 'notify.save-success' @@ -68,8 +68,7 @@ export default class ScopesScopeRolesRoute extends Route { } scopeAdapterOptions() { - const { id: scope_id } = this.modelFor('scopes.scope'); - const adapterOptions = { scopeID: scope_id }; - return { adapterOptions }; + const { id: scopeID } = this.modelFor('scopes.scope'); + return { adapterOptions: { scopeID } }; } } diff --git a/ui/core/app/routes/scopes/scope/roles/new.js b/ui/core/app/routes/scopes/scope/roles/new.js index f39f6e6c2..edc239e21 100644 --- a/ui/core/app/routes/scopes/scope/roles/new.js +++ b/ui/core/app/routes/scopes/scope/roles/new.js @@ -8,6 +8,7 @@ export default class ScopesScopeRolesNewRoute extends Route { * @return {RoleModel} */ model() { - return this.store.createRecord('role'); + const { id: scopeID } = this.modelFor('scopes.scope'); + return this.store.createRecord('role', { scopeID }); } } diff --git a/ui/core/app/routes/scopes/scope/roles/role.js b/ui/core/app/routes/scopes/scope/roles/role.js index 781bb4e47..98cbffcd0 100644 --- a/ui/core/app/routes/scopes/scope/roles/role.js +++ b/ui/core/app/routes/scopes/scope/roles/role.js @@ -10,8 +10,7 @@ export default class ScopesScopeRolesRoleRoute extends Route { * @return {RoleModel} */ async model({ role_id }) { - const { id: scope_id } = this.modelFor('scopes.scope'); - const adapterOptions = { scopeID: scope_id }; - return this.store.findRecord('role', role_id, { adapterOptions }); + const { id: scopeID } = this.modelFor('scopes.scope'); + return this.store.findRecord('role', role_id, { adapterOptions: { scopeID } }); } } diff --git a/ui/core/app/routes/scopes/scope/users.js b/ui/core/app/routes/scopes/scope/users.js index e70c25f2f..8beb4f146 100644 --- a/ui/core/app/routes/scopes/scope/users.js +++ b/ui/core/app/routes/scopes/scope/users.js @@ -39,7 +39,7 @@ export default class ScopesScopeUsersRoute extends Route { async save(user) { const { isNew } = user; try { - await user.save(this.scopeAdapterOptions()); + await user.save(); this.refresh(); this.notify.success(this.intl.t( isNew ? 'notify.create-success' : 'notify.save-success' @@ -69,8 +69,7 @@ export default class ScopesScopeUsersRoute extends Route { } scopeAdapterOptions() { - const { id: scope_id } = this.modelFor('scopes.scope'); - const adapterOptions = { scopeID: scope_id }; - return { adapterOptions }; + const { id: scopeID } = this.modelFor('scopes.scope'); + return { adapterOptions: { scopeID } }; } } diff --git a/ui/core/app/routes/scopes/scope/users/new.js b/ui/core/app/routes/scopes/scope/users/new.js index 9b486a48b..25c4a0b52 100644 --- a/ui/core/app/routes/scopes/scope/users/new.js +++ b/ui/core/app/routes/scopes/scope/users/new.js @@ -8,6 +8,7 @@ export default class ScopesScopeUsersNewRoute extends Route { * @return {UserModel} */ model() { - return this.store.createRecord('user'); + const { id: scopeID } = this.modelFor('scopes.scope'); + return this.store.createRecord('user', { scopeID }); } } diff --git a/ui/core/app/routes/scopes/scope/users/user.js b/ui/core/app/routes/scopes/scope/users/user.js index f28ff4d56..b2bcc0911 100644 --- a/ui/core/app/routes/scopes/scope/users/user.js +++ b/ui/core/app/routes/scopes/scope/users/user.js @@ -10,8 +10,7 @@ export default class ScopesScopeUsersUserRoute extends Route { * @return {UserModel} */ async model({ user_id }) { - const { id: scope_id } = this.modelFor('scopes.scope'); - const adapterOptions = { scopeID: scope_id }; - return this.store.findRecord('user', user_id, { adapterOptions }); + const { id: scopeID } = this.modelFor('scopes.scope'); + return this.store.findRecord('user', user_id, { adapterOptions: { scopeID } }); } } From 3fe86614f120e92a66795c98e39764d2ec513a51 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Wed, 29 Jul 2020 11:20:37 -0400 Subject: [PATCH 61/70] test: add host catalogs acceptance tests --- .../tests/acceptance/host-catalogs-test.js | 130 +++++++++++------- 1 file changed, 83 insertions(+), 47 deletions(-) diff --git a/ui/core/tests/acceptance/host-catalogs-test.js b/ui/core/tests/acceptance/host-catalogs-test.js index 4be43309f..682f9c163 100644 --- a/ui/core/tests/acceptance/host-catalogs-test.js +++ b/ui/core/tests/acceptance/host-catalogs-test.js @@ -1,52 +1,92 @@ import { module, test } from 'qunit'; -import { visit, currentURL, click, fillIn, find } from '@ember/test-helpers'; +import { visit, currentURL, fillIn, click, find } from '@ember/test-helpers'; import { setupApplicationTest } from 'ember-qunit'; import setupMirage from 'ember-cli-mirage/test-support/setup-mirage'; import a11yAudit from 'ember-a11y-testing/test-support/audit'; import { Response } from 'miragejs'; +import { +// currentSession, + authenticateSession, +// invalidateSession, +} from 'ember-simple-auth/test-support'; module('Acceptance | host catalogs', function(hooks) { setupApplicationTest(hooks); setupMirage(hooks); + const instances = { + scopes: { + global: null, + org: null, + project: null + }, + hostCatalog: null + }; + const urls = { + orgScope: null, + projects: null, + project: null, + hostCatalogs: null, + newHostCatalog: null, + hostCatalog: null + }; + hooks.beforeEach(function () { - this.server.create('org'); + // Setup Mirage mock resources for this test + instances.scopes.global = this.server.create('scope', { id: 'global' }); + instances.scopes.org = this.server.create('scope', { type: 'org', + scope: { id: instances.scopes.global.id, type: instances.scopes.global.type } + }); + instances.scopes.project = this.server.create('scope', { type: 'project', + scope: { id: instances.scopes.org.id, type: instances.scopes.org.type } + }); + instances.hostCatalog = this.server.create('host-catalog', { + scope: { + id: instances.scopes.project.id, + type: instances.scopes.project.type + } + }); + + // Generate route URLs for resources + urls.orgScope = `/scopes/${instances.scopes.org.id}`; + urls.projects = `${urls.orgScope}/projects`; + urls.project = `${urls.projects}/${instances.hostCatalog.scope.id}`; + urls.hostCatalogs = `${urls.project}/host-catalogs`; + urls.newHostCatalog = `${urls.hostCatalogs}/new`; + urls.hostCatalog = `${urls.hostCatalogs}/${instances.hostCatalog.id}`; }); test('visiting host catalogs', async function (assert) { assert.expect(1); - this.server.createList('project', 1); - this.server.createList('host-catalog', 1); - await visit('/orgs/1/projects/1/host-catalogs'); + authenticateSession(); + await visit(urls.hostCatalogs); await a11yAudit(); - assert.equal(currentURL(), '/orgs/1/projects/1/host-catalogs'); + assert.equal(currentURL(), urls.hostCatalogs); }); test('can navigate to host catalog form', async function(assert) { assert.expect(1); - this.server.createList('project', 1); - this.server.createList('host-catalog', 1); - await visit('/orgs/1/projects/1/host-catalogs'); - await click('main .rose-table-cell:first-child a'); + authenticateSession({}); + await visit(urls.hostCatalogs); + await click('main tbody .rose-table-cell:nth-child(2) a'); await a11yAudit(); - assert.equal(currentURL(), '/orgs/1/projects/1/host-catalogs/1'); + assert.equal(currentURL(), urls.hostCatalog); }); test('can delete host catalog', async function(assert) { assert.expect(2); - this.server.createList('project', 1); - this.server.createList('host-catalog', 1); - assert.equal(this.server.db.hostCatalogs.length, 1); - await visit('/orgs/1/projects/1/host-catalogs/1'); + const hostCatalogsCount = this.server.db.hostCatalogs.length; + authenticateSession({}); + await visit(urls.hostCatalog); + assert.equal(currentURL(), urls.hostCatalog); await click('.rose-button-warning'); - assert.equal(this.server.db.hostCatalogs.length, 0); + assert.equal(this.server.db.hostCatalogs.length, hostCatalogsCount - 1); }); test('can update host catalog and save changes', async function(assert) { assert.expect(1); - this.server.createList('project', 1); - this.server.createList('host-catalog', 1); - await visit('/orgs/1/projects/1/host-catalogs/1'); + authenticateSession({}); + await visit(urls.hostCatalog); await fillIn('[name="name"]', 'Test Name'); await click('form [type="submit"]:not(:disabled)'); assert.equal(this.server.db.hostCatalogs[0].name, 'Test Name'); @@ -54,39 +94,36 @@ module('Acceptance | host catalogs', function(hooks) { test('can update host catalog and cancel changes', async function(assert) { assert.expect(1); - this.server.createList('project', 1); - this.server.createList('host-catalog', 1); - await visit('/orgs/1/projects/1/host-catalogs/1'); + authenticateSession({}); + await visit(urls.hostCatalog); await fillIn('[name="name"]', 'Test Name'); await click('form button:not([type="submit"])'); assert.notEqual(this.server.db.hostCatalogs[0].name, 'Test Name'); }); test('can create host catalog and save changes', async function(assert) { - assert.expect(2); - this.server.createList('project', 1); - await visit('/orgs/1/projects/1/host-catalogs/new'); + assert.expect(1); + const hostCatalogsCount = this.server.db.hostCatalogs.length; + authenticateSession({}); + await visit(urls.newHostCatalog); await fillIn('[name="name"]', 'Test Name'); await click('form [type="submit"]:not(:disabled)'); - assert.equal(this.server.db.hostCatalogs.length, 1); - assert.equal(currentURL(), '/orgs/1/projects/1/host-catalogs/1'); + assert.equal(this.server.db.hostCatalogs.length, hostCatalogsCount + 1); }); test('can create host catalog and cancel changes', async function(assert) { - assert.expect(2); - this.server.createList('project', 1); - await visit('/orgs/1/projects/1/host-catalogs/new'); + assert.expect(1); + const hostCatalogsCount = this.server.db.hostCatalogs.length; + authenticateSession({}); + await visit(urls.newHostCatalog); await fillIn('[name="name"]', 'Test Name'); await click('form button:not([type="submit"])'); - assert.equal(this.server.db.hostCatalogs.length, 0); - assert.equal(currentURL(), '/orgs/1/projects/1/host-catalogs'); + assert.equal(this.server.db.hostCatalogs.length, hostCatalogsCount); }); test('errors are displayed when save host catalog fails', async function (assert) { assert.expect(1); - this.server.createList('project', 1); - this.server.createList('host-catalog', 1); - this.server.patch('/orgs/:org_id/projects/:project_id/host-catalogs/:id', () => { + this.server.patch('/scopes/:scope_id/host-catalogs/:id', () => { return new Response( 490, {}, @@ -97,7 +134,8 @@ module('Acceptance | host catalogs', function(hooks) { } ); }); - await visit('/orgs/1/projects/1/host-catalogs/1'); + authenticateSession({}); + await visit(urls.hostCatalog); await fillIn('[name="name"]', 'random string'); await click('form [type="submit"]'); await a11yAudit(); @@ -110,9 +148,7 @@ module('Acceptance | host catalogs', function(hooks) { test('errors are displayed when delete host catalog fails', async function (assert) { assert.expect(1); - this.server.createList('project', 1); - this.server.createList('host-catalog', 1); - this.server.del('/orgs/:org_id/projects/:project_id/host-catalogs/:id', () => { + this.server.del('/scopes/:scope_id/host-catalogs/:id', () => { return new Response( 490, {}, @@ -123,7 +159,8 @@ module('Acceptance | host catalogs', function(hooks) { } ); }); - await visit('/orgs/1/projects/1/host-catalogs/1'); + authenticateSession({}); + await visit(urls.hostCatalog); await click('.rose-button-warning'); await a11yAudit(); assert.ok( @@ -135,7 +172,7 @@ module('Acceptance | host catalogs', function(hooks) { test('saving a new host catalog with invalid fields displays error messages', async function (assert) { assert.expect(2); - this.server.post('/orgs/:org_id/projects/:project_id/host-catalogs', () => { + this.server.post('/scopes/:scope_id/host-catalogs', () => { return new Response( 400, {}, @@ -154,8 +191,8 @@ module('Acceptance | host catalogs', function(hooks) { } ); }); - this.server.createList('project', 1); - await visit('/orgs/1/projects/1/host-catalogs/new'); + authenticateSession({}); + await visit(urls.newHostCatalog); await fillIn('[name="name"]', 'random string'); await click('form [type="submit"]'); await a11yAudit(); @@ -173,9 +210,7 @@ module('Acceptance | host catalogs', function(hooks) { test('saving an existing host catalog with invalid fields displays error messages', async function (assert) { assert.expect(2); - this.server.createList('project', 1); - this.server.createList('host-catalog', 1); - this.server.patch('/orgs/:org_id/projects/:project_id/host-catalogs/:id', () => { + this.server.patch('/scopes/:scope_id/host-catalogs/:id', () => { return new Response( 400, {}, @@ -194,7 +229,8 @@ module('Acceptance | host catalogs', function(hooks) { } ); }); - await visit('/orgs/1/projects/1/host-catalogs/1'); + authenticateSession({}); + await visit(urls.hostCatalog); await fillIn('[name="name"]', 'random string'); await click('form [type="submit"]'); await a11yAudit(); From deca73ba5f322671537ce60db1944704c200def0 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Wed, 29 Jul 2020 13:13:43 -0400 Subject: [PATCH 62/70] chore: generate multiple org scopes in mocks and add import comment --- ui/core/mirage/scenarios/default.js | 4 ++-- ui/core/tests/acceptance/host-catalogs-test.js | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/ui/core/mirage/scenarios/default.js b/ui/core/mirage/scenarios/default.js index f5727e195..7bc409465 100644 --- a/ui/core/mirage/scenarios/default.js +++ b/ui/core/mirage/scenarios/default.js @@ -3,10 +3,10 @@ export default function(server) { // Scope resources const globalScope = server.create('scope', { id: 'global' }); // creates a global scope - const orgScope = server.create('scope', { + const orgScope = server.createList('scope', 3, { type: 'org', scope: { id: globalScope.id, type: globalScope.type } - }, 'withChildren'); + }, 'withChildren')[0]; const scope = { id: orgScope.id, type: orgScope.type }; // Auth diff --git a/ui/core/tests/acceptance/host-catalogs-test.js b/ui/core/tests/acceptance/host-catalogs-test.js index 682f9c163..96b49cd8c 100644 --- a/ui/core/tests/acceptance/host-catalogs-test.js +++ b/ui/core/tests/acceptance/host-catalogs-test.js @@ -5,9 +5,10 @@ import setupMirage from 'ember-cli-mirage/test-support/setup-mirage'; import a11yAudit from 'ember-a11y-testing/test-support/audit'; import { Response } from 'miragejs'; import { -// currentSession, authenticateSession, -// invalidateSession, + // These are left here intentionally for future reference. + //currentSession, + //invalidateSession, } from 'ember-simple-auth/test-support'; module('Acceptance | host catalogs', function(hooks) { From aa5bfb71555e97ce513f0ceb990b6825044d9a77 Mon Sep 17 00:00:00 2001 From: Susmitha Girumala Date: Wed, 29 Jul 2020 14:30:19 -0400 Subject: [PATCH 63/70] =?UTF-8?q?chore:=20=F0=9F=A4=96=20Update=20IAM=20cr?= =?UTF-8?q?eation=20to=20use=20scopeModel=20and=20type?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ui/core/app/routes/scopes/scope/groups.js | 10 +++------- ui/core/app/routes/scopes/scope/groups/new.js | 4 ++-- ui/core/app/routes/scopes/scope/roles.js | 10 +++------- ui/core/app/routes/scopes/scope/roles/new.js | 4 ++-- ui/core/app/routes/scopes/scope/users.js | 10 +++------- ui/core/app/routes/scopes/scope/users/new.js | 4 ++-- 6 files changed, 15 insertions(+), 27 deletions(-) diff --git a/ui/core/app/routes/scopes/scope/groups.js b/ui/core/app/routes/scopes/scope/groups.js index 94441ca1e..ea24aa58e 100644 --- a/ui/core/app/routes/scopes/scope/groups.js +++ b/ui/core/app/routes/scopes/scope/groups.js @@ -15,7 +15,8 @@ export default class ScopesScopeGroupsRoute extends Route { * @return {Promise[GroupModel]} */ async model() { - return this.store.findAll('group', this.scopeAdapterOptions()); + const { id: scopeID } = this.modelFor('scopes.scope'); + return this.store.findAll('group', { adapterOptions: { scopeID } }); } // =actions @@ -57,7 +58,7 @@ export default class ScopesScopeGroupsRoute extends Route { @action async delete(group) { try { - await group.destroyRecord(this.scopeAdapterOptions()); + await group.destroyRecord(); this.refresh(); this.notify.success(this.intl.t('notify.delete-success')); this.transitionTo('scopes.scope.groups'); @@ -66,9 +67,4 @@ export default class ScopesScopeGroupsRoute extends Route { this.notify.error(error.message, { closeAfter: null }); } } - - scopeAdapterOptions() { - const { id: scopeID } = this.modelFor('scopes.scope'); - return { adapterOptions: { scopeID } }; - } } diff --git a/ui/core/app/routes/scopes/scope/groups/new.js b/ui/core/app/routes/scopes/scope/groups/new.js index 3326dc7e4..fe281afb2 100644 --- a/ui/core/app/routes/scopes/scope/groups/new.js +++ b/ui/core/app/routes/scopes/scope/groups/new.js @@ -8,7 +8,7 @@ export default class ScopesScopeGroupsNewRoute extends Route { * @return {GroupModel} */ model() { - const { id: scopeID } = this.modelFor('scopes.scope'); - return this.store.createRecord('group', { scopeID }); + const scopeModel = this.modelFor('scopes.scope'); + return this.store.createRecord('group', { type: 'group', scopeModel }); } } diff --git a/ui/core/app/routes/scopes/scope/roles.js b/ui/core/app/routes/scopes/scope/roles.js index a4d9d2c5b..f5e27a9f5 100644 --- a/ui/core/app/routes/scopes/scope/roles.js +++ b/ui/core/app/routes/scopes/scope/roles.js @@ -15,7 +15,8 @@ export default class ScopesScopeRolesRoute extends Route { * @return {Promise{[RoleModel]}} */ async model() { - return this.store.findAll('role', this.scopeAdapterOptions()); + const { id: scopeID } = this.modelFor('scopes.scope'); + return this.store.findAll('role', { adapterOptions: { scopeID } }); } // =actions @@ -57,7 +58,7 @@ export default class ScopesScopeRolesRoute extends Route { @action async delete(role) { try { - await role.destroyRecord(this.scopeAdapterOptions()); + await role.destroyRecord(); this.refresh(); this.notify.success(this.intl.t('notify.delete-success')); this.transitionTo('scopes.scope.roles'); @@ -66,9 +67,4 @@ export default class ScopesScopeRolesRoute extends Route { this.notify.error(error.message, { closeAfter: null }); } } - - scopeAdapterOptions() { - const { id: scopeID } = this.modelFor('scopes.scope'); - return { adapterOptions: { scopeID } }; - } } diff --git a/ui/core/app/routes/scopes/scope/roles/new.js b/ui/core/app/routes/scopes/scope/roles/new.js index edc239e21..bcde1bb0f 100644 --- a/ui/core/app/routes/scopes/scope/roles/new.js +++ b/ui/core/app/routes/scopes/scope/roles/new.js @@ -8,7 +8,7 @@ export default class ScopesScopeRolesNewRoute extends Route { * @return {RoleModel} */ model() { - const { id: scopeID } = this.modelFor('scopes.scope'); - return this.store.createRecord('role', { scopeID }); + const scopeModel = this.modelFor('scopes.scope'); + return this.store.createRecord('role', { type: 'role', scopeModel }); } } diff --git a/ui/core/app/routes/scopes/scope/users.js b/ui/core/app/routes/scopes/scope/users.js index 8beb4f146..6519086fd 100644 --- a/ui/core/app/routes/scopes/scope/users.js +++ b/ui/core/app/routes/scopes/scope/users.js @@ -15,7 +15,8 @@ export default class ScopesScopeUsersRoute extends Route { * @return {Promise{[UserModel]}} */ async model() { - return this.store.findAll('user', this.scopeAdapterOptions()); + const { id: scopeID } = this.modelFor('scopes.scope'); + return this.store.findAll('user', { adapterOptions: { scopeID } }); } // =actions @@ -58,7 +59,7 @@ export default class ScopesScopeUsersRoute extends Route { @action async delete(user) { try { - await user.destroyRecord(this.scopeAdapterOptions()); + await user.destroyRecord(); this.refresh(); this.notify.success(this.intl.t('notify.delete-success')); this.transitionTo('scopes.scope.users'); @@ -67,9 +68,4 @@ export default class ScopesScopeUsersRoute extends Route { this.notify.error(error.message, { closeAfter: null }); } } - - scopeAdapterOptions() { - const { id: scopeID } = this.modelFor('scopes.scope'); - return { adapterOptions: { scopeID } }; - } } diff --git a/ui/core/app/routes/scopes/scope/users/new.js b/ui/core/app/routes/scopes/scope/users/new.js index 25c4a0b52..89cca81d9 100644 --- a/ui/core/app/routes/scopes/scope/users/new.js +++ b/ui/core/app/routes/scopes/scope/users/new.js @@ -8,7 +8,7 @@ export default class ScopesScopeUsersNewRoute extends Route { * @return {UserModel} */ model() { - const { id: scopeID } = this.modelFor('scopes.scope'); - return this.store.createRecord('user', { scopeID }); + const scopeModel = this.modelFor('scopes.scope'); + return this.store.createRecord('user', { type: 'user', scopeModel }); } } From bb30bb4b5f3576d95f36f659b89a5a315145be18 Mon Sep 17 00:00:00 2001 From: Susmitha Girumala Date: Wed, 29 Jul 2020 15:16:11 -0400 Subject: [PATCH 64/70] =?UTF-8?q?chore:=20=F0=9F=A4=96=20Assign=20scope=20?= =?UTF-8?q?to=20IAM=20mirage=20resources?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ui/core/mirage/scenarios/default.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ui/core/mirage/scenarios/default.js b/ui/core/mirage/scenarios/default.js index 1bbae80d5..5d7b72800 100644 --- a/ui/core/mirage/scenarios/default.js +++ b/ui/core/mirage/scenarios/default.js @@ -29,12 +29,12 @@ export default function(server) { server.createList('auth-method', 3, { scope }); // User - server.createList('user', 5); + server.createList('user', 5, { scope }); // Role - server.createList('role', 5); + server.createList('role', 5, { scope }); // Groups - server.createList('group', 5); + server.createList('group', 5, { scope }); // Other resources From c9fa433663cb9aa3ba16c6d8c8e3b4ccc20cad4b Mon Sep 17 00:00:00 2001 From: Susmitha Girumala Date: Wed, 29 Jul 2020 15:43:09 -0400 Subject: [PATCH 65/70] =?UTF-8?q?test:=20=F0=9F=92=8D=20Refactor=20IAM=20a?= =?UTF-8?q?cceptance=20tests=20to=20use=20predefined=20ids?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ui/core/tests/acceptance/groups-test.js | 66 +++++++++++++++--------- ui/core/tests/acceptance/roles-test.js | 66 +++++++++++++++--------- ui/core/tests/acceptance/users-test.js | 67 +++++++++++++++---------- 3 files changed, 123 insertions(+), 76 deletions(-) diff --git a/ui/core/tests/acceptance/groups-test.js b/ui/core/tests/acceptance/groups-test.js index 2322f0882..778a71969 100644 --- a/ui/core/tests/acceptance/groups-test.js +++ b/ui/core/tests/acceptance/groups-test.js @@ -4,6 +4,12 @@ import { setupApplicationTest } from 'ember-qunit'; import setupMirage from 'ember-cli-mirage/test-support/setup-mirage'; import a11yAudit from 'ember-a11y-testing/test-support/audit'; import { Response } from 'miragejs'; +import { + authenticateSession, + // These are left here intentionally for future reference. + //currentSession, + //invalidateSession, +} from 'ember-simple-auth/test-support'; module('Acceptance | groups', function (hooks) { setupApplicationTest(hooks); @@ -11,6 +17,7 @@ module('Acceptance | groups', function (hooks) { let orgScope; let groupsURL; + let groupURL; let newGroupURL; hooks.beforeEach(function () { @@ -22,8 +29,18 @@ module('Acceptance | groups', function (hooks) { 'withChildren' ); + const group = this.server.create('group', { + scope: { + id: orgScope.id, + type: orgScope.type + } + }); + groupsURL = `/scopes/${orgScope.id}/groups`; + groupURL = `${groupsURL}/${group.id}`; newGroupURL = `${groupsURL}/new`; + + authenticateSession({}); }); test('visiting groups', async function (assert) { @@ -33,25 +50,30 @@ module('Acceptance | groups', function (hooks) { assert.equal(currentURL(), groupsURL); }); + test('visiting a group', async function(assert) { + assert.expect(1); + await visit(newGroupURL); + await a11yAudit(); + assert.equal(currentURL(), newGroupURL); + }); + test('can create new group', async function (assert) { - assert.expect(4); - assert.equal(this.server.db.groups.length, 0); + assert.expect(1); + const groupsCount = this.server.db.groups.length; await visit(newGroupURL); await fillIn('[name="name"]', 'group name'); await click('[type="submit"]'); - assert.equal(currentURL(), `${groupsURL}/1`); - assert.equal(this.server.db.groups.length, 1); - assert.equal(this.server.db.groups[0].name, 'group name'); + assert.equal(this.server.db.groups.length, groupsCount + 1); }); test('can cancel new group creation', async function (assert) { - assert.expect(3); - assert.equal(this.server.db.groups.length, 0); + assert.expect(2); + const groupsCount = this.server.db.groups.length; await visit(newGroupURL); await fillIn('[name="name"]', 'group name'); await click('.rose-form-actions [type="button"]'); assert.equal(currentURL(), groupsURL); - assert.equal(this.server.db.groups.length, 0); + assert.equal(this.server.db.groups.length, groupsCount); }); test('saving a new group with invalid fields displays error messages', async function (assert) { @@ -92,35 +114,31 @@ module('Acceptance | groups', function (hooks) { test('can save changes to an existing group', async function (assert) { assert.expect(2); - this.server.createList('group', 1, { name: 'Admin group' }); - await visit(`${groupsURL}/1`); + await visit(groupURL); await fillIn('[name="name"]', 'Updated admin group'); await click('.rose-form-actions [type="submit"]'); - assert.equal(currentURL(), `${groupsURL}/1`); + assert.equal(currentURL(), groupURL); assert.equal(this.server.db.groups[0].name, 'Updated admin group'); }); test('can cancel changes to an existing group', async function (assert) { assert.expect(1); - this.server.createList('group', 1, { name: 'Admin group' }); - await visit(`${groupsURL}/1`); + await visit(groupURL); await fillIn('[name="name"]', 'Updated admin group'); await click('.rose-form-actions [type="button"]'); - assert.equal(find('[name="name"]').value, 'Admin group'); + assert.notEqual(find('[name="name"]').value, 'Updated admin group'); }); test('can delete a group', async function (assert) { - assert.expect(2); - this.server.createList('group', 1); - assert.equal(this.server.db.groups.length, 1); - await visit(`${groupsURL}/1`); + assert.expect(1); + const groupsCount = this.server.db.groups.length; + await visit(groupURL); await click('.rose-button-warning'); - assert.equal(this.server.db.groups.length, 0); + assert.equal(this.server.db.groups.length, groupsCount - 1); }); test('saving an existing group with invalid fields displays error messages', async function (assert) { assert.expect(2); - this.server.createList('group', 1); this.server.patch('/scopes/:scope_id/groups/:id', () => { return new Response( 400, @@ -140,7 +158,7 @@ module('Acceptance | groups', function (hooks) { } ); }); - await visit(`${groupsURL}/1`); + await visit(groupURL); await fillIn('[name="name"]', 'random string'); await click('[type="submit"]'); assert.ok( @@ -157,7 +175,6 @@ module('Acceptance | groups', function (hooks) { test('errors are displayed when save project fails', async function (assert) { assert.expect(1); - this.server.createList('group', 1); this.server.patch('/scopes/:scope_id/groups/:id', () => { return new Response( 490, @@ -169,7 +186,7 @@ module('Acceptance | groups', function (hooks) { } ); }); - await visit(`${groupsURL}/1`); + await visit(groupURL); await fillIn('[name="name"]', 'Role name'); await click('[type="submit"]'); assert.ok( @@ -181,7 +198,6 @@ module('Acceptance | groups', function (hooks) { test('errors are displayed when delete project fails', async function (assert) { assert.expect(1); - this.server.createList('group', 1); this.server.del('/scopes/:scope_id/groups/:id', () => { return new Response( 490, @@ -193,7 +209,7 @@ module('Acceptance | groups', function (hooks) { } ); }); - await visit(`${groupsURL}/1`); + await visit(groupURL); await click('.rose-button-warning'); assert.ok( find('[role="alert"]').textContent.trim(), diff --git a/ui/core/tests/acceptance/roles-test.js b/ui/core/tests/acceptance/roles-test.js index b745a0d28..ba8c85ed3 100644 --- a/ui/core/tests/acceptance/roles-test.js +++ b/ui/core/tests/acceptance/roles-test.js @@ -4,6 +4,12 @@ import { setupApplicationTest } from 'ember-qunit'; import setupMirage from 'ember-cli-mirage/test-support/setup-mirage'; import a11yAudit from 'ember-a11y-testing/test-support/audit'; import { Response } from 'miragejs'; +import { + authenticateSession, + // These are left here intentionally for future reference. + //currentSession, + //invalidateSession, +} from 'ember-simple-auth/test-support'; module('Acceptance | roles', function (hooks) { setupApplicationTest(hooks); @@ -11,6 +17,7 @@ module('Acceptance | roles', function (hooks) { let orgScope; let rolesURL; + let roleURL; let newRoleURL; hooks.beforeEach(function () { @@ -22,8 +29,18 @@ module('Acceptance | roles', function (hooks) { 'withChildren' ); + const role = this.server.create('role', { + scope: { + id: orgScope.id, + type: orgScope.type + } + }) + rolesURL = `/scopes/${orgScope.id}/roles`; + roleURL = `${rolesURL}/${role.id}`; newRoleURL = `${rolesURL}/new`; + + authenticateSession({}); }); test('visiting roles', async function (assert) { @@ -33,25 +50,30 @@ module('Acceptance | roles', function (hooks) { assert.equal(currentURL(), rolesURL); }); + test('visiting a role', async function(assert) { + assert.expect(1); + await visit(newRoleURL); + await a11yAudit(); + assert.equal(currentURL(), newRoleURL); + }); + test('can create new role', async function (assert) { - assert.expect(4); - assert.equal(this.server.db.roles.length, 0); + assert.expect(1); + const rolesCount = this.server.db.roles.length; await visit(newRoleURL); await fillIn('[name="name"]', 'role name'); await click('[type="submit"]'); - assert.equal(currentURL(), `${rolesURL}/1`); - assert.equal(this.server.db.roles.length, 1); - assert.equal(this.server.db.roles[0].name, 'role name'); + assert.equal(this.server.db.roles.length, rolesCount + 1); }); test('can cancel new role creation', async function (assert) { - assert.expect(3); - assert.equal(this.server.db.roles.length, 0); + assert.expect(2); + const rolesCount = this.server.db.roles.length; await visit(newRoleURL); await fillIn('[name="name"]', 'role name'); await click('.rose-form-actions [type="button"]'); assert.equal(currentURL(), rolesURL); - assert.equal(this.server.db.roles.length, 0); + assert.equal(this.server.db.roles.length, rolesCount); }); test('saving a new role with invalid fields displays error messages', async function (assert) { @@ -92,35 +114,31 @@ module('Acceptance | roles', function (hooks) { test('can save changes to an existing role', async function (assert) { assert.expect(2); - this.server.createList('role', 1, { name: 'Admin role' }); - await visit(`${rolesURL}/1`); + await visit(roleURL); await fillIn('[name="name"]', 'Updated admin role'); await click('.rose-form-actions [type="submit"]'); - assert.equal(currentURL(), `${rolesURL}/1`); + assert.equal(currentURL(), roleURL); assert.equal(this.server.db.roles[0].name, 'Updated admin role'); }); test('can cancel changes to an existing role', async function (assert) { assert.expect(1); - this.server.createList('role', 1, { name: 'Admin role' }); - await visit(`${rolesURL}/1`); + await visit(roleURL); await fillIn('[name="name"]', 'Updated admin role'); await click('.rose-form-actions [type="button"]'); - assert.equal(find('[name="name"]').value, 'Admin role'); + assert.notEqual(find('[name="name"]').value, 'Updated admin role'); }); test('can delete a role', async function (assert) { - assert.expect(2); - this.server.createList('role', 1); - assert.equal(this.server.db.roles.length, 1); - await visit(`${rolesURL}/1`); + assert.expect(1); + const rolesCount = this.server.db.roles.length; + await visit(roleURL); await click('.rose-button-warning'); - assert.equal(this.server.db.roles.length, 0); + assert.equal(this.server.db.roles.length, rolesCount - 1); }); test('saving an existing role with invalid fields displays error messages', async function (assert) { assert.expect(2); - this.server.createList('role', 1); this.server.patch('/scopes/:scope_id/roles/:id', () => { return new Response( 400, @@ -140,7 +158,7 @@ module('Acceptance | roles', function (hooks) { } ); }); - await visit(`${rolesURL}/1`); + await visit(roleURL); await fillIn('[name="name"]', 'random string'); await click('[type="submit"]'); assert.ok( @@ -157,7 +175,6 @@ module('Acceptance | roles', function (hooks) { test('errors are displayed when save project fails', async function (assert) { assert.expect(1); - this.server.createList('role', 1); this.server.patch('/scopes/:scope_id/roles/:id', () => { return new Response( 490, @@ -169,7 +186,7 @@ module('Acceptance | roles', function (hooks) { } ); }); - await visit(`${rolesURL}/1`); + await visit(roleURL); await fillIn('[name="name"]', 'Role name'); await click('[type="submit"]'); assert.ok( @@ -181,7 +198,6 @@ module('Acceptance | roles', function (hooks) { test('errors are displayed when delete project fails', async function (assert) { assert.expect(1); - this.server.createList('role', 1); this.server.del('/scopes/:scope_id/roles/:id', () => { return new Response( 490, @@ -193,7 +209,7 @@ module('Acceptance | roles', function (hooks) { } ); }); - await visit(`${rolesURL}/1`); + await visit(roleURL); await click('.rose-button-warning'); assert.ok( find('[role="alert"]').textContent.trim(), diff --git a/ui/core/tests/acceptance/users-test.js b/ui/core/tests/acceptance/users-test.js index 85b187d56..eac32425c 100644 --- a/ui/core/tests/acceptance/users-test.js +++ b/ui/core/tests/acceptance/users-test.js @@ -4,6 +4,12 @@ import { setupApplicationTest } from 'ember-qunit'; import setupMirage from 'ember-cli-mirage/test-support/setup-mirage'; import a11yAudit from 'ember-a11y-testing/test-support/audit'; import { Response } from 'miragejs'; +import { + authenticateSession, + // These are left here intentionally for future reference. + //currentSession, + //invalidateSession, +} from 'ember-simple-auth/test-support'; module('Acceptance | users', function (hooks) { setupApplicationTest(hooks); @@ -11,6 +17,7 @@ module('Acceptance | users', function (hooks) { let orgScope; let usersURL; + let userURL; let newUserURL; hooks.beforeEach(function () { @@ -22,37 +29,51 @@ module('Acceptance | users', function (hooks) { 'withChildren' ); + const user = this.server.create('user', { + scope: { + id: orgScope.id, + type: orgScope.type + } + }); + usersURL = `/scopes/${orgScope.id}/users`; + userURL = `${usersURL}/${user.id}`; newUserURL = `${usersURL}/new`; + + authenticateSession({}); }); test('visiting users', async function (assert) { assert.expect(1); - this.server.createList('user', 1); await visit(usersURL); await a11yAudit(); assert.equal(currentURL(), usersURL); }); + test('visiting a user', async function(assert) { + assert.expect(1); + await visit(newUserURL); + await a11yAudit(); + assert.equal(currentURL(), newUserURL); + }); + test('can create new users', async function (assert) { - assert.expect(4); - assert.equal(this.server.db.users.length, 0); + assert.expect(1); + const usersCount = this.server.db.users.length; await visit(newUserURL); await fillIn('[name="name"]', 'User name'); await click('[type="submit"]'); - assert.equal(currentURL(), `${usersURL}/1`); - assert.equal(this.server.db.users.length, 1); - assert.equal(this.server.db.users[0].name, 'User name'); + assert.equal(this.server.db.users.length, usersCount + 1); }); test('can cancel creation of a new user', async function (assert) { - assert.expect(3); - assert.equal(this.server.db.users.length, 0); + assert.expect(2); + const usersCount = this.server.db.users.length; await visit(newUserURL); await fillIn('[name="name"]', 'User name'); await click('.rose-form-actions [type="button"]'); assert.equal(currentURL(), usersURL); - assert.equal(this.server.db.users.length, 0); + assert.equal(this.server.db.users.length, usersCount); }); test('saving a new user with invalid fields displays error messages', async function (assert) { @@ -93,35 +114,31 @@ module('Acceptance | users', function (hooks) { test('can save changes to an existing user', async function (assert) { assert.expect(2); - this.server.createList('user', 1, { name: 'Test User' }); - await visit(`${usersURL}/1`); + await visit(userURL); await fillIn('[name="name"]', 'Updated user name'); await click('.rose-form-actions [type="submit"]'); - assert.equal(currentURL(), `${usersURL}/1`); + assert.equal(currentURL(), userURL); assert.equal(this.server.db.users[0].name, 'Updated user name'); }); test('can cancel changes to an existing user', async function (assert) { assert.expect(1); - this.server.createList('user', 1, { name: 'Test User' }); - await visit(`${usersURL}/1`); + await visit(userURL); await fillIn('[name="name"]', 'Unsaved user name'); await click('.rose-form-actions [type="button"]'); - assert.equal(find('[name="name"]').value, 'Test User'); + assert.notEqual(find('[name="name"]').value, 'Unsaved user name'); }); test('can delete an user', async function (assert) { - assert.expect(2); - this.server.createList('user', 1); - assert.equal(this.server.db.users.length, 1); - await visit(`${usersURL}/1`); + assert.expect(1); + const usersCount = this.server.db.users.length; + await visit(userURL); await click('.rose-button-warning'); - assert.equal(this.server.db.users.length, 0); + assert.equal(this.server.db.users.length, usersCount - 1); }); test('saving an existing user with invalid fields displays error messages', async function (assert) { assert.expect(2); - this.server.createList('user', 1); this.server.patch('/scopes/:scope_id/users/:id', () => { return new Response( 400, @@ -141,7 +158,7 @@ module('Acceptance | users', function (hooks) { } ); }); - await visit(`${usersURL}/1`); + await visit(userURL); await fillIn('[name="name"]', 'User name'); await click('[type="submit"]'); assert.ok( @@ -158,7 +175,6 @@ module('Acceptance | users', function (hooks) { test('errors are displayed when saving user fails', async function (assert) { assert.expect(1); - this.server.createList('user', 1); this.server.patch('/scopes/:scope_id/users/:id', () => { return new Response( 490, @@ -170,7 +186,7 @@ module('Acceptance | users', function (hooks) { } ); }); - await visit(`${usersURL}/1`); + await visit(userURL); await fillIn('[name="name"]', 'User name'); await click('[type="submit"]'); assert.ok( @@ -182,7 +198,6 @@ module('Acceptance | users', function (hooks) { test('errors are displayed when user deletion fails', async function (assert) { assert.expect(1); - this.server.createList('user', 1); this.server.del('/scopes/:scope_id/users/:id', () => { return new Response( 490, @@ -194,7 +209,7 @@ module('Acceptance | users', function (hooks) { } ); }); - await visit(`${usersURL}/1`); + await visit(userURL); await click('.rose-button-warning'); assert.ok( find('[role="alert"]').textContent.trim(), From 656e11bb527d55720f0d1c9f9cb43ba9904655d7 Mon Sep 17 00:00:00 2001 From: Susmitha Girumala Date: Wed, 29 Jul 2020 15:47:23 -0400 Subject: [PATCH 66/70] =?UTF-8?q?refactor:=20=F0=9F=92=A1=20"yarn=20format?= =?UTF-8?q?"=20touched=20files?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ui/core/app/authenticators/password.js | 4 +-- ui/core/app/controllers/application.js | 2 -- ui/core/app/controllers/scopes/scope.js | 2 -- ui/core/app/router.js | 4 +-- ui/core/app/routes/application.js | 1 - ui/core/app/routes/index.js | 2 -- ui/core/app/routes/scopes.js | 4 +-- ui/core/app/routes/scopes/index.js | 8 ++--- ui/core/app/routes/scopes/scope.js | 13 ++++---- .../app/routes/scopes/scope/authenticate.js | 2 -- .../routes/scopes/scope/authenticate/index.js | 2 -- .../scopes/scope/authenticate/method.js | 2 -- ui/core/app/routes/scopes/scope/groups.js | 6 ++-- .../app/routes/scopes/scope/groups/group.js | 4 ++- ui/core/app/routes/scopes/scope/index.js | 2 -- ui/core/app/routes/scopes/scope/projects.js | 6 ++-- ui/core/app/routes/scopes/scope/roles.js | 6 ++-- ui/core/app/routes/scopes/scope/roles/role.js | 4 ++- ui/core/app/routes/scopes/scope/users.js | 6 ++-- ui/core/app/routes/scopes/scope/users/user.js | 4 ++- ui/core/app/session-stores/application.js | 2 -- .../tests/acceptance/authentication-test.js | 31 ++++++++++--------- ui/core/tests/acceptance/groups-test.js | 6 ++-- ui/core/tests/acceptance/roles-test.js | 8 ++--- ui/core/tests/acceptance/users-test.js | 6 ++-- .../unit/controllers/application-test.js | 4 +-- .../unit/controllers/scopes/scope-test.js | 4 +-- ui/core/tests/unit/routes/index-test.js | 4 +-- .../tests/unit/routes/scopes/index-test.js | 4 +-- .../routes/scopes/scope/authenticate-test.js | 4 +-- .../scopes/scope/authenticate/index-test.js | 4 +-- .../scopes/scope/authenticate/method-test.js | 4 +-- .../unit/routes/scopes/scope/index-test.js | 4 +-- 33 files changed, 78 insertions(+), 91 deletions(-) diff --git a/ui/core/app/authenticators/password.js b/ui/core/app/authenticators/password.js index 41e75d078..a70c89ac7 100644 --- a/ui/core/app/authenticators/password.js +++ b/ui/core/app/authenticators/password.js @@ -7,7 +7,6 @@ import { inject as service } from '@ember/service'; * endpoint, while deauthentication occurs on a scope endpoint. */ export default class PasswordAuthenticator extends BasePasswordAuthenticator { - // =services @service store; @@ -24,7 +23,7 @@ export default class PasswordAuthenticator extends BasePasswordAuthenticator { */ buildAuthEndpointURL({ scope: { id: scopeID }, - authMethod: { id: authMethodID } + authMethod: { id: authMethodID }, }) { const adapter = this.store.adapterFor('application'); const options = { adapterOptions: { scopeID, method: 'authenticate' } }; @@ -43,5 +42,4 @@ export default class PasswordAuthenticator extends BasePasswordAuthenticator { const options = { adapterOptions: { method: 'deauthenticate' } }; return adapter.buildURL('scope', scopeID, options, 'findRecord'); } - } diff --git a/ui/core/app/controllers/application.js b/ui/core/app/controllers/application.js index 3fd811d62..565eb7d7a 100644 --- a/ui/core/app/controllers/application.js +++ b/ui/core/app/controllers/application.js @@ -2,9 +2,7 @@ import Controller from '@ember/controller'; import { inject as service } from '@ember/service'; export default class ApplicationController extends Controller { - // =services @service session; - } diff --git a/ui/core/app/controllers/scopes/scope.js b/ui/core/app/controllers/scopes/scope.js index 5bc636752..de850e223 100644 --- a/ui/core/app/controllers/scopes/scope.js +++ b/ui/core/app/controllers/scopes/scope.js @@ -2,9 +2,7 @@ import Controller from '@ember/controller'; import { inject as service } from '@ember/service'; export default class ScopesScopeController extends Controller { - // =services @service session; - } diff --git a/ui/core/app/router.js b/ui/core/app/router.js index 7bac5667e..a00ddbad3 100644 --- a/ui/core/app/router.js +++ b/ui/core/app/router.js @@ -9,8 +9,8 @@ export default class Router extends EmberRouter { Router.map(function () { this.route('scopes', function () { this.route('scope', { path: ':scope_id' }, function () { - this.route('authenticate', function() { - this.route('method', { path: ':auth_method_id'}); + this.route('authenticate', function () { + this.route('method', { path: ':auth_method_id' }); }); this.route('projects', function () { this.route('project', { path: ':project_id' }, function () {}); diff --git a/ui/core/app/routes/application.js b/ui/core/app/routes/application.js index 024a0b887..e67f524e1 100644 --- a/ui/core/app/routes/application.js +++ b/ui/core/app/routes/application.js @@ -12,7 +12,6 @@ import { later } from '@ember/runloop'; export default class ApplicationRoute extends Route.extend( ApplicationRouteMixin ) { - // =services @service session; diff --git a/ui/core/app/routes/index.js b/ui/core/app/routes/index.js index 4ec1a62b0..c8728d037 100644 --- a/ui/core/app/routes/index.js +++ b/ui/core/app/routes/index.js @@ -1,7 +1,6 @@ import Route from '@ember/routing/route'; export default class IndexRoute extends Route { - // =methods /** @@ -10,5 +9,4 @@ export default class IndexRoute extends Route { redirect() { this.transitionTo('scopes'); } - } diff --git a/ui/core/app/routes/scopes.js b/ui/core/app/routes/scopes.js index 86a9452fd..337b319c9 100644 --- a/ui/core/app/routes/scopes.js +++ b/ui/core/app/routes/scopes.js @@ -2,7 +2,6 @@ import Route from '@ember/routing/route'; import { A } from '@ember/array'; export default class ScopesRoute extends Route { - // =methods /** @@ -20,5 +19,4 @@ export default class ScopesRoute extends Route { // expected to default to the global scope, thus returning org scopes. return this.store.findAll('scope').catch(() => A([])); } - -} \ No newline at end of file +} diff --git a/ui/core/app/routes/scopes/index.js b/ui/core/app/routes/scopes/index.js index 06a263171..562a27a15 100644 --- a/ui/core/app/routes/scopes/index.js +++ b/ui/core/app/routes/scopes/index.js @@ -3,7 +3,6 @@ import { inject as service } from '@ember/service'; import { get } from '@ember/object'; export default class ScopesIndexRoute extends Route { - // =services @service session; @@ -17,13 +16,14 @@ export default class ScopesIndexRoute extends Route { * @param {?ScopeModel} model.firstObject */ redirect({ firstObject }) { - const authenticatedScopeID = - get(this.session, 'data.authenticated.scope.id'); + const authenticatedScopeID = get( + this.session, + 'data.authenticated.scope.id' + ); if (authenticatedScopeID) { this.transitionTo('scopes.scope', authenticatedScopeID); } else if (firstObject) { this.transitionTo('scopes.scope', firstObject.id); } } - } diff --git a/ui/core/app/routes/scopes/scope.js b/ui/core/app/routes/scopes/scope.js index e9f2c191c..2707a238b 100644 --- a/ui/core/app/routes/scopes/scope.js +++ b/ui/core/app/routes/scopes/scope.js @@ -14,12 +14,11 @@ export default class ScopesScopeRoute extends Route { * @return {Promise{ScopeModel}} */ model({ scope_id: id }) { - return this.store.findRecord('scope', id) - .catch(() => { - const maybeExistingScope = this.store.peekRecord('scope', id); - // TODO it's unclear if creating a temporary unsaved scope is reliable - // we may need to revert to a POJO. - return maybeExistingScope || this.store.createRecord('scope', { id }); - }); + return this.store.findRecord('scope', id).catch(() => { + const maybeExistingScope = this.store.peekRecord('scope', id); + // TODO it's unclear if creating a temporary unsaved scope is reliable + // we may need to revert to a POJO. + return maybeExistingScope || this.store.createRecord('scope', { id }); + }); } } diff --git a/ui/core/app/routes/scopes/scope/authenticate.js b/ui/core/app/routes/scopes/scope/authenticate.js index 93f565baa..d7dae560d 100644 --- a/ui/core/app/routes/scopes/scope/authenticate.js +++ b/ui/core/app/routes/scopes/scope/authenticate.js @@ -3,7 +3,6 @@ import { inject as service } from '@ember/service'; import { hash } from 'rsvp'; export default class ScopesScopeAuthenticateRoute extends Route { - // =services @service session; @@ -41,5 +40,4 @@ export default class ScopesScopeAuthenticateRoute extends Route { this.transitionTo(this.routeIfAlreadyAuthenticated); } } - } diff --git a/ui/core/app/routes/scopes/scope/authenticate/index.js b/ui/core/app/routes/scopes/scope/authenticate/index.js index 5ee523b46..9d6ce9d61 100644 --- a/ui/core/app/routes/scopes/scope/authenticate/index.js +++ b/ui/core/app/routes/scopes/scope/authenticate/index.js @@ -1,7 +1,6 @@ import Route from '@ember/routing/route'; export default class ScopesScopeAuthenticateIndexRoute extends Route { - // =methods /** @@ -16,5 +15,4 @@ export default class ScopesScopeAuthenticateIndexRoute extends Route { this.transitionTo('scopes.scope.authenticate.method', firstAuthMethod); } } - } diff --git a/ui/core/app/routes/scopes/scope/authenticate/method.js b/ui/core/app/routes/scopes/scope/authenticate/method.js index b52406722..5208b152d 100644 --- a/ui/core/app/routes/scopes/scope/authenticate/method.js +++ b/ui/core/app/routes/scopes/scope/authenticate/method.js @@ -3,7 +3,6 @@ import { inject as service } from '@ember/service'; import { action } from '@ember/object'; export default class ScopesScopeAuthenticateMethodRoute extends Route { - // =services @service session; @@ -62,5 +61,4 @@ export default class ScopesScopeAuthenticateMethodRoute extends Route { this.notify.error(errorMessage, { closeAfter: null }); } } - } diff --git a/ui/core/app/routes/scopes/scope/groups.js b/ui/core/app/routes/scopes/scope/groups.js index ea24aa58e..7eed4406e 100644 --- a/ui/core/app/routes/scopes/scope/groups.js +++ b/ui/core/app/routes/scopes/scope/groups.js @@ -41,9 +41,9 @@ export default class ScopesScopeGroupsRoute extends Route { try { await group.save(); this.refresh(); - this.notify.success(this.intl.t( - isNew ? 'notify.create-success' : 'notify.save-success' - )); + this.notify.success( + this.intl.t(isNew ? 'notify.create-success' : 'notify.save-success') + ); this.transitionTo('scopes.scope.groups.group', group); } catch (error) { //TODO: replace with translated strings diff --git a/ui/core/app/routes/scopes/scope/groups/group.js b/ui/core/app/routes/scopes/scope/groups/group.js index ce0585e8d..6231ab13f 100644 --- a/ui/core/app/routes/scopes/scope/groups/group.js +++ b/ui/core/app/routes/scopes/scope/groups/group.js @@ -11,6 +11,8 @@ export default class ScopesScopeGroupsGroupRoute extends Route { */ async model({ group_id }) { const { id: scopeID } = this.modelFor('scopes.scope'); - return this.store.findRecord('group', group_id, { adapterOptions: { scopeID } }); + return this.store.findRecord('group', group_id, { + adapterOptions: { scopeID }, + }); } } diff --git a/ui/core/app/routes/scopes/scope/index.js b/ui/core/app/routes/scopes/scope/index.js index 0215ea9da..36261b82d 100644 --- a/ui/core/app/routes/scopes/scope/index.js +++ b/ui/core/app/routes/scopes/scope/index.js @@ -1,7 +1,6 @@ import Route from '@ember/routing/route'; export default class ScopesScopeIndexRoute extends Route { - // =methods /** @@ -10,5 +9,4 @@ export default class ScopesScopeIndexRoute extends Route { redirect() { this.transitionTo('scopes.scope.authenticate'); } - } diff --git a/ui/core/app/routes/scopes/scope/projects.js b/ui/core/app/routes/scopes/scope/projects.js index 2e5835d63..8d9ef9c00 100644 --- a/ui/core/app/routes/scopes/scope/projects.js +++ b/ui/core/app/routes/scopes/scope/projects.js @@ -65,9 +65,9 @@ export default class ScopesScopeProjectsRoute extends Route { try { await project.save(); this.refresh(); - this.notify.success(this.intl.t( - isNew ? 'notify.create-success' : 'notify.save-success' - )); + this.notify.success( + this.intl.t(isNew ? 'notify.create-success' : 'notify.save-success') + ); this.transitionTo('scopes.scope.projects.project', project); } catch (error) { // TODO: replace with translated strings diff --git a/ui/core/app/routes/scopes/scope/roles.js b/ui/core/app/routes/scopes/scope/roles.js index f5e27a9f5..84cf2ae0f 100644 --- a/ui/core/app/routes/scopes/scope/roles.js +++ b/ui/core/app/routes/scopes/scope/roles.js @@ -41,9 +41,9 @@ export default class ScopesScopeRolesRoute extends Route { try { await role.save(); this.refresh(); - this.notify.success(this.intl.t( - isNew ? 'notify.create-success' : 'notify.save-success' - )); + this.notify.success( + this.intl.t(isNew ? 'notify.create-success' : 'notify.save-success') + ); this.transitionTo('scopes.scope.roles.role', role); } catch (error) { // TODO: replace with translated strings diff --git a/ui/core/app/routes/scopes/scope/roles/role.js b/ui/core/app/routes/scopes/scope/roles/role.js index 98cbffcd0..7fb8d3db7 100644 --- a/ui/core/app/routes/scopes/scope/roles/role.js +++ b/ui/core/app/routes/scopes/scope/roles/role.js @@ -11,6 +11,8 @@ export default class ScopesScopeRolesRoleRoute extends Route { */ async model({ role_id }) { const { id: scopeID } = this.modelFor('scopes.scope'); - return this.store.findRecord('role', role_id, { adapterOptions: { scopeID } }); + return this.store.findRecord('role', role_id, { + adapterOptions: { scopeID }, + }); } } diff --git a/ui/core/app/routes/scopes/scope/users.js b/ui/core/app/routes/scopes/scope/users.js index 6519086fd..f1982b769 100644 --- a/ui/core/app/routes/scopes/scope/users.js +++ b/ui/core/app/routes/scopes/scope/users.js @@ -42,9 +42,9 @@ export default class ScopesScopeUsersRoute extends Route { try { await user.save(); this.refresh(); - this.notify.success(this.intl.t( - isNew ? 'notify.create-success' : 'notify.save-success' - )); + this.notify.success( + this.intl.t(isNew ? 'notify.create-success' : 'notify.save-success') + ); this.transitionTo('scopes.scope.users.user', user); } catch (error) { // TODO: replace with translated strings diff --git a/ui/core/app/routes/scopes/scope/users/user.js b/ui/core/app/routes/scopes/scope/users/user.js index b2bcc0911..ffbf19e7e 100644 --- a/ui/core/app/routes/scopes/scope/users/user.js +++ b/ui/core/app/routes/scopes/scope/users/user.js @@ -11,6 +11,8 @@ export default class ScopesScopeUsersUserRoute extends Route { */ async model({ user_id }) { const { id: scopeID } = this.modelFor('scopes.scope'); - return this.store.findRecord('user', user_id, { adapterOptions: { scopeID } }); + return this.store.findRecord('user', user_id, { + adapterOptions: { scopeID }, + }); } } diff --git a/ui/core/app/session-stores/application.js b/ui/core/app/session-stores/application.js index 80d0603db..48b3bc4be 100644 --- a/ui/core/app/session-stores/application.js +++ b/ui/core/app/session-stores/application.js @@ -4,7 +4,6 @@ import { get } from '@ember/object'; //import config from '../config/environment'; export default class ApplicationSessionStore extends LocalStorageSessionStore { - @service store; /** @@ -41,7 +40,6 @@ export default class ApplicationSessionStore extends LocalStorageSessionStore { const authenticated = get(sessionData, 'authenticated'); if (scope && authenticated) authenticated.scope = scope; } - } // import CookieSessionStore from 'auth/session-stores/cookie'; diff --git a/ui/core/tests/acceptance/authentication-test.js b/ui/core/tests/acceptance/authentication-test.js index f6fa1c8ab..3481337bb 100644 --- a/ui/core/tests/acceptance/authentication-test.js +++ b/ui/core/tests/acceptance/authentication-test.js @@ -9,7 +9,7 @@ import { invalidateSession, } from 'ember-simple-auth/test-support'; -module('Acceptance | authentication', function(hooks) { +module('Acceptance | authentication', function (hooks) { setupApplicationTest(hooks); setupMirage(hooks); @@ -30,10 +30,14 @@ module('Acceptance | authentication', function(hooks) { invalidateSession(); indexURL = '/'; globalScope = this.server.create('scope', { id: 'global' }); - orgScope = this.server.create('scope', { - type: 'org', - scope: { id: globalScope.id, type: globalScope.type } - }, 'withChildren'); + orgScope = this.server.create( + 'scope', + { + type: 'org', + scope: { id: globalScope.id, type: globalScope.type }, + }, + 'withChildren' + ); scope = { id: orgScope.id, type: orgScope.type }; authMethod = this.server.create('auth-method', { scope }); orgScopeID = orgScope.id; @@ -45,14 +49,14 @@ module('Acceptance | authentication', function(hooks) { projectsURL = `/scopes/${orgScopeID}/projects`; }); - test('visiting auth methods authenticate route redirects to first auth method', async function(assert) { + test('visiting auth methods authenticate route redirects to first auth method', async function (assert) { assert.expect(1); await visit(authenticateURL); await a11yAudit(); assert.equal(currentURL(), authMethodAuthenticateURL); }); - test('visiting any authentication parent route while unauthenticated redirects to first authenticate method', async function(assert) { + test('visiting any authentication parent route while unauthenticated redirects to first authenticate method', async function (assert) { assert.expect(3); await visit(indexURL); assert.equal(currentURL(), authMethodAuthenticateURL); @@ -61,21 +65,21 @@ module('Acceptance | authentication', function(hooks) { assert.notOk(currentSession().isAuthenticated); }); - test('visiting projects while unauthenticated redirects to first authenticate method', async function(assert) { + test('visiting projects while unauthenticated redirects to first authenticate method', async function (assert) { assert.expect(2); await visit(projectsURL); assert.equal(currentURL(), authMethodAuthenticateURL); assert.notOk(currentSession().isAuthenticated); }); - test('visiting auth method authenticate route', async function(assert) { + test('visiting auth method authenticate route', async function (assert) { assert.expect(1); await visit(authMethodAuthenticateURL); await a11yAudit(); assert.equal(currentURL(), authMethodAuthenticateURL); }); - test('visiting any authentication parent route while already authenticated redirects to projects', async function(assert) { + test('visiting any authentication parent route while already authenticated redirects to projects', async function (assert) { assert.expect(6); authenticateSession({ scope }); await visit(indexURL); @@ -91,7 +95,7 @@ module('Acceptance | authentication', function(hooks) { assert.ok(currentSession().isAuthenticated); }); - test('failed authentication shows a notification message', async function(assert) { + test('failed authentication shows a notification message', async function (assert) { assert.expect(3); await visit(authMethodAuthenticateURL); assert.notOk(currentSession().isAuthenticated); @@ -101,7 +105,7 @@ module('Acceptance | authentication', function(hooks) { assert.notOk(currentSession().isAuthenticated); }); - test('successful authentication redirects to projects', async function(assert) { + test('successful authentication redirects to projects', async function (assert) { assert.expect(4); await visit(authMethodAuthenticateURL); assert.notOk(currentSession().isAuthenticated); @@ -113,7 +117,7 @@ module('Acceptance | authentication', function(hooks) { assert.ok(currentSession().isAuthenticated); }); - test('deauthentication redirects to first authenticate method', async function(assert) { + test('deauthentication redirects to first authenticate method', async function (assert) { assert.expect(3); await visit(authMethodAuthenticateURL); await fillIn('[name="identification"]', 'test'); @@ -124,5 +128,4 @@ module('Acceptance | authentication', function(hooks) { assert.notOk(currentSession().isAuthenticated); assert.equal(currentURL(), authMethodAuthenticateURL); }); - }); diff --git a/ui/core/tests/acceptance/groups-test.js b/ui/core/tests/acceptance/groups-test.js index 778a71969..b619d09ce 100644 --- a/ui/core/tests/acceptance/groups-test.js +++ b/ui/core/tests/acceptance/groups-test.js @@ -32,8 +32,8 @@ module('Acceptance | groups', function (hooks) { const group = this.server.create('group', { scope: { id: orgScope.id, - type: orgScope.type - } + type: orgScope.type, + }, }); groupsURL = `/scopes/${orgScope.id}/groups`; @@ -50,7 +50,7 @@ module('Acceptance | groups', function (hooks) { assert.equal(currentURL(), groupsURL); }); - test('visiting a group', async function(assert) { + test('visiting a group', async function (assert) { assert.expect(1); await visit(newGroupURL); await a11yAudit(); diff --git a/ui/core/tests/acceptance/roles-test.js b/ui/core/tests/acceptance/roles-test.js index ba8c85ed3..dd8483574 100644 --- a/ui/core/tests/acceptance/roles-test.js +++ b/ui/core/tests/acceptance/roles-test.js @@ -32,9 +32,9 @@ module('Acceptance | roles', function (hooks) { const role = this.server.create('role', { scope: { id: orgScope.id, - type: orgScope.type - } - }) + type: orgScope.type, + }, + }); rolesURL = `/scopes/${orgScope.id}/roles`; roleURL = `${rolesURL}/${role.id}`; @@ -50,7 +50,7 @@ module('Acceptance | roles', function (hooks) { assert.equal(currentURL(), rolesURL); }); - test('visiting a role', async function(assert) { + test('visiting a role', async function (assert) { assert.expect(1); await visit(newRoleURL); await a11yAudit(); diff --git a/ui/core/tests/acceptance/users-test.js b/ui/core/tests/acceptance/users-test.js index eac32425c..31fa5c98e 100644 --- a/ui/core/tests/acceptance/users-test.js +++ b/ui/core/tests/acceptance/users-test.js @@ -32,8 +32,8 @@ module('Acceptance | users', function (hooks) { const user = this.server.create('user', { scope: { id: orgScope.id, - type: orgScope.type - } + type: orgScope.type, + }, }); usersURL = `/scopes/${orgScope.id}/users`; @@ -50,7 +50,7 @@ module('Acceptance | users', function (hooks) { assert.equal(currentURL(), usersURL); }); - test('visiting a user', async function(assert) { + test('visiting a user', async function (assert) { assert.expect(1); await visit(newUserURL); await a11yAudit(); diff --git a/ui/core/tests/unit/controllers/application-test.js b/ui/core/tests/unit/controllers/application-test.js index 93d19cebe..eb120e471 100644 --- a/ui/core/tests/unit/controllers/application-test.js +++ b/ui/core/tests/unit/controllers/application-test.js @@ -1,11 +1,11 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; -module('Unit | Controller | application', function(hooks) { +module('Unit | Controller | application', function (hooks) { setupTest(hooks); // Replace this with your real tests. - test('it exists', function(assert) { + test('it exists', function (assert) { let controller = this.owner.lookup('controller:application'); assert.ok(controller); }); diff --git a/ui/core/tests/unit/controllers/scopes/scope-test.js b/ui/core/tests/unit/controllers/scopes/scope-test.js index ec2c389bb..1ac27e62b 100644 --- a/ui/core/tests/unit/controllers/scopes/scope-test.js +++ b/ui/core/tests/unit/controllers/scopes/scope-test.js @@ -1,11 +1,11 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; -module('Unit | Controller | scopes/scope', function(hooks) { +module('Unit | Controller | scopes/scope', function (hooks) { setupTest(hooks); // Replace this with your real tests. - test('it exists', function(assert) { + test('it exists', function (assert) { let controller = this.owner.lookup('controller:scopes/scope'); assert.ok(controller); }); diff --git a/ui/core/tests/unit/routes/index-test.js b/ui/core/tests/unit/routes/index-test.js index eaaac17bc..01131fb9a 100644 --- a/ui/core/tests/unit/routes/index-test.js +++ b/ui/core/tests/unit/routes/index-test.js @@ -1,10 +1,10 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; -module('Unit | Route | index', function(hooks) { +module('Unit | Route | index', function (hooks) { setupTest(hooks); - test('it exists', function(assert) { + test('it exists', function (assert) { let route = this.owner.lookup('route:index'); assert.ok(route); }); diff --git a/ui/core/tests/unit/routes/scopes/index-test.js b/ui/core/tests/unit/routes/scopes/index-test.js index c2fb98357..22a5c073a 100644 --- a/ui/core/tests/unit/routes/scopes/index-test.js +++ b/ui/core/tests/unit/routes/scopes/index-test.js @@ -1,10 +1,10 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; -module('Unit | Route | scopes/index', function(hooks) { +module('Unit | Route | scopes/index', function (hooks) { setupTest(hooks); - test('it exists', function(assert) { + test('it exists', function (assert) { let route = this.owner.lookup('route:scopes/index'); assert.ok(route); }); diff --git a/ui/core/tests/unit/routes/scopes/scope/authenticate-test.js b/ui/core/tests/unit/routes/scopes/scope/authenticate-test.js index 2ee818453..5a1db715d 100644 --- a/ui/core/tests/unit/routes/scopes/scope/authenticate-test.js +++ b/ui/core/tests/unit/routes/scopes/scope/authenticate-test.js @@ -1,10 +1,10 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; -module('Unit | Route | scopes/scope/authenticate', function(hooks) { +module('Unit | Route | scopes/scope/authenticate', function (hooks) { setupTest(hooks); - test('it exists', function(assert) { + test('it exists', function (assert) { let route = this.owner.lookup('route:scopes/scope/authenticate'); assert.ok(route); }); diff --git a/ui/core/tests/unit/routes/scopes/scope/authenticate/index-test.js b/ui/core/tests/unit/routes/scopes/scope/authenticate/index-test.js index 88d5a1121..d9e86f2da 100644 --- a/ui/core/tests/unit/routes/scopes/scope/authenticate/index-test.js +++ b/ui/core/tests/unit/routes/scopes/scope/authenticate/index-test.js @@ -1,10 +1,10 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; -module('Unit | Route | scopes/scope/authenticate/index', function(hooks) { +module('Unit | Route | scopes/scope/authenticate/index', function (hooks) { setupTest(hooks); - test('it exists', function(assert) { + test('it exists', function (assert) { let route = this.owner.lookup('route:scopes/scope/authenticate/index'); assert.ok(route); }); diff --git a/ui/core/tests/unit/routes/scopes/scope/authenticate/method-test.js b/ui/core/tests/unit/routes/scopes/scope/authenticate/method-test.js index 5aed2aefe..f077f994b 100644 --- a/ui/core/tests/unit/routes/scopes/scope/authenticate/method-test.js +++ b/ui/core/tests/unit/routes/scopes/scope/authenticate/method-test.js @@ -1,10 +1,10 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; -module('Unit | Route | scopes/scope/authenticate/method', function(hooks) { +module('Unit | Route | scopes/scope/authenticate/method', function (hooks) { setupTest(hooks); - test('it exists', function(assert) { + test('it exists', function (assert) { let route = this.owner.lookup('route:scopes/scope/authenticate/method'); assert.ok(route); }); diff --git a/ui/core/tests/unit/routes/scopes/scope/index-test.js b/ui/core/tests/unit/routes/scopes/scope/index-test.js index ba0a5bdba..c623e801b 100644 --- a/ui/core/tests/unit/routes/scopes/scope/index-test.js +++ b/ui/core/tests/unit/routes/scopes/scope/index-test.js @@ -1,10 +1,10 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; -module('Unit | Route | scopes/scope/index', function(hooks) { +module('Unit | Route | scopes/scope/index', function (hooks) { setupTest(hooks); - test('it exists', function(assert) { + test('it exists', function (assert) { let route = this.owner.lookup('route:scopes/scope/index'); assert.ok(route); }); From b8c2770745a03cc1de624308b0456930e5cd52b7 Mon Sep 17 00:00:00 2001 From: Susmitha Girumala Date: Thu, 30 Jul 2020 11:32:34 -0400 Subject: [PATCH 67/70] =?UTF-8?q?chore:=20=F0=9F=A4=96=20Do=20not=20set=20?= =?UTF-8?q?"type"=20for=20IAM=20when=20using=20scopeModel?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ui/core/app/routes/scopes/scope/groups/new.js | 2 +- ui/core/app/routes/scopes/scope/roles/new.js | 2 +- ui/core/app/routes/scopes/scope/users/new.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ui/core/app/routes/scopes/scope/groups/new.js b/ui/core/app/routes/scopes/scope/groups/new.js index fe281afb2..baf98da65 100644 --- a/ui/core/app/routes/scopes/scope/groups/new.js +++ b/ui/core/app/routes/scopes/scope/groups/new.js @@ -9,6 +9,6 @@ export default class ScopesScopeGroupsNewRoute extends Route { */ model() { const scopeModel = this.modelFor('scopes.scope'); - return this.store.createRecord('group', { type: 'group', scopeModel }); + return this.store.createRecord('group', { scopeModel }); } } diff --git a/ui/core/app/routes/scopes/scope/roles/new.js b/ui/core/app/routes/scopes/scope/roles/new.js index bcde1bb0f..6b5e27688 100644 --- a/ui/core/app/routes/scopes/scope/roles/new.js +++ b/ui/core/app/routes/scopes/scope/roles/new.js @@ -9,6 +9,6 @@ export default class ScopesScopeRolesNewRoute extends Route { */ model() { const scopeModel = this.modelFor('scopes.scope'); - return this.store.createRecord('role', { type: 'role', scopeModel }); + return this.store.createRecord('role', { scopeModel }); } } diff --git a/ui/core/app/routes/scopes/scope/users/new.js b/ui/core/app/routes/scopes/scope/users/new.js index 89cca81d9..469680a4e 100644 --- a/ui/core/app/routes/scopes/scope/users/new.js +++ b/ui/core/app/routes/scopes/scope/users/new.js @@ -9,6 +9,6 @@ export default class ScopesScopeUsersNewRoute extends Route { */ model() { const scopeModel = this.modelFor('scopes.scope'); - return this.store.createRecord('user', { type: 'user', scopeModel }); + return this.store.createRecord('user', { scopeModel }); } } From c8b90921d2c30f3702b17764a29a368d3349592c Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Thu, 30 Jul 2020 12:54:28 -0400 Subject: [PATCH 68/70] chore: nuke old orgs stuff and fix tests --- ui/core/app/controllers/orgs/org/groups.js | 17 ---- .../app/controllers/orgs/org/groups/group.js | 11 --- .../app/controllers/orgs/org/groups/new.js | 17 ---- ui/core/app/controllers/orgs/org/projects.js | 17 ---- .../app/controllers/orgs/org/projects/new.js | 17 ---- .../controllers/orgs/org/projects/project.js | 11 --- .../org/projects/project/host-catalogs.js | 17 ---- .../project/host-catalogs/host-catalog.js | 11 --- ui/core/app/controllers/orgs/org/roles.js | 17 ---- ui/core/app/controllers/orgs/org/roles/new.js | 17 ---- .../app/controllers/orgs/org/roles/role.js | 11 --- ui/core/app/controllers/orgs/org/users.js | 17 ---- ui/core/app/controllers/orgs/org/users/new.js | 17 ---- .../app/controllers/orgs/org/users/user.js | 11 --- .../projects/project/host-catalogs/new.js | 5 +- ui/core/app/router.js | 33 ------- ui/core/app/routes/orgs.js | 13 --- ui/core/app/routes/orgs/org.js | 28 ------ ui/core/app/routes/orgs/org/groups.js | 13 --- ui/core/app/routes/orgs/org/groups/group.js | 64 ------------- .../app/routes/orgs/org/groups/group/index.js | 3 - ui/core/app/routes/orgs/org/groups/index.js | 3 - ui/core/app/routes/orgs/org/groups/new.js | 50 ----------- ui/core/app/routes/orgs/org/projects.js | 13 --- ui/core/app/routes/orgs/org/projects/index.js | 3 - ui/core/app/routes/orgs/org/projects/new.js | 50 ----------- .../app/routes/orgs/org/projects/project.js | 90 ------------------- .../org/projects/project/host-catalogs.js | 13 --- .../project/host-catalogs/host-catalog.js | 65 -------------- .../host-catalogs/host-catalog/index.js | 3 - .../projects/project/host-catalogs/index.js | 3 - .../org/projects/project/host-catalogs/new.js | 53 ----------- .../routes/orgs/org/projects/project/index.js | 3 - ui/core/app/routes/orgs/org/roles.js | 13 --- ui/core/app/routes/orgs/org/roles/index.js | 3 - ui/core/app/routes/orgs/org/roles/new.js | 50 ----------- ui/core/app/routes/orgs/org/roles/role.js | 64 ------------- ui/core/app/routes/orgs/org/users.js | 13 --- ui/core/app/routes/orgs/org/users/index.js | 3 - ui/core/app/routes/orgs/org/users/new.js | 50 ----------- ui/core/app/routes/orgs/org/users/user.js | 64 ------------- ui/core/app/templates/orgs.hbs | 1 - ui/core/app/templates/orgs/org.hbs | 35 -------- ui/core/app/templates/orgs/org/error.hbs | 58 ------------ ui/core/app/templates/orgs/org/groups.hbs | 3 - .../app/templates/orgs/org/groups/group.hbs | 3 - .../templates/orgs/org/groups/group/index.hbs | 27 ------ .../app/templates/orgs/org/groups/index.hbs | 63 ------------- ui/core/app/templates/orgs/org/groups/new.hbs | 18 ---- ui/core/app/templates/orgs/org/projects.hbs | 3 - .../app/templates/orgs/org/projects/index.hbs | 51 ----------- .../app/templates/orgs/org/projects/new.hbs | 17 ---- .../templates/orgs/org/projects/project.hbs | 3 - .../orgs/org/projects/project/_sidebar.hbs | 15 ---- .../org/projects/project/host-catalogs.hbs | 3 - .../project/host-catalogs/host-catalog.hbs | 3 - .../host-catalogs/host-catalog/index.hbs | 27 ------ .../projects/project/host-catalogs/index.hbs | 68 -------------- .../projects/project/host-catalogs/new.hbs | 18 ---- .../orgs/org/projects/project/index.hbs | 35 -------- ui/core/app/templates/orgs/org/roles.hbs | 3 - .../app/templates/orgs/org/roles/index.hbs | 63 ------------- ui/core/app/templates/orgs/org/roles/new.hbs | 17 ---- ui/core/app/templates/orgs/org/roles/role.hbs | 3 - .../templates/orgs/org/roles/role/index.hbs | 27 ------ ui/core/app/templates/orgs/org/users.hbs | 3 - .../app/templates/orgs/org/users/index.hbs | 63 ------------- ui/core/app/templates/orgs/org/users/new.hbs | 17 ---- ui/core/app/templates/orgs/org/users/user.hbs | 3 - .../templates/orgs/org/users/user/index.hbs | 27 ------ ui/core/app/templates/scopes/scope.hbs | 20 +---- ui/core/tests/acceptance/bread-crumbs-test.js | 7 ++ ui/core/tests/acceptance/index-test.js | 15 ---- ui/core/tests/acceptance/projects-test.js | 7 ++ .../components/bread-crumbs-test.js | 2 +- .../unit/controllers/orgs/org/groups-test.js | 12 --- .../controllers/orgs/org/groups/group-test.js | 12 --- .../controllers/orgs/org/groups/new-test.js | 12 --- .../controllers/orgs/org/projects-test.js | 12 --- .../controllers/orgs/org/projects/new-test.js | 12 --- .../orgs/org/projects/project-test.js | 12 --- .../projects/project/host-catalogs-test.js | 16 ---- .../host-catalogs/host-catalog-test.js | 17 ---- .../project/host-catalogs/new-test.js | 17 ---- .../unit/controllers/orgs/org/roles-test.js | 11 --- .../controllers/orgs/org/roles/new-test.js | 11 --- .../controllers/orgs/org/roles/role-test.js | 11 --- .../unit/controllers/orgs/org/users-test.js | 12 --- .../controllers/orgs/org/users/new-test.js | 12 --- .../controllers/orgs/org/users/user-test.js | 12 --- .../project/host-catalogs/new-test.js | 12 +++ ui/core/tests/unit/routes/orgs-test.js | 11 --- ui/core/tests/unit/routes/orgs/org-test.js | 11 --- .../tests/unit/routes/orgs/org/groups-test.js | 11 --- .../unit/routes/orgs/org/groups/group-test.js | 11 --- .../orgs/org/groups/group/index-test.js | 11 --- .../unit/routes/orgs/org/groups/index-test.js | 11 --- .../unit/routes/orgs/org/groups/new-test.js | 11 --- .../unit/routes/orgs/org/projects-test.js | 11 --- .../routes/orgs/org/projects/index-test.js | 11 --- .../unit/routes/orgs/org/projects/new-test.js | 11 --- .../routes/orgs/org/projects/project-test.js | 11 --- .../projects/project/host-catalogs-test.js | 15 ---- .../host-catalogs/host-catalog-test.js | 16 ---- .../host-catalogs/host-catalog/index-test.js | 16 ---- .../project/host-catalogs/index-test.js | 16 ---- .../project/host-catalogs/new-test.js | 15 ---- .../orgs/org/projects/project/index-test.js | 11 --- .../tests/unit/routes/orgs/org/roles-test.js | 11 --- .../unit/routes/orgs/org/roles/index-test.js | 11 --- .../unit/routes/orgs/org/roles/new-test.js | 11 --- .../unit/routes/orgs/org/roles/role-test.js | 11 --- .../tests/unit/routes/orgs/org/users-test.js | 11 --- .../unit/routes/orgs/org/users/index-test.js | 11 --- .../unit/routes/orgs/org/users/new-test.js | 11 --- .../unit/routes/orgs/org/users/user-test.js | 11 --- 116 files changed, 33 insertions(+), 2172 deletions(-) delete mode 100644 ui/core/app/controllers/orgs/org/groups.js delete mode 100644 ui/core/app/controllers/orgs/org/groups/group.js delete mode 100644 ui/core/app/controllers/orgs/org/groups/new.js delete mode 100644 ui/core/app/controllers/orgs/org/projects.js delete mode 100644 ui/core/app/controllers/orgs/org/projects/new.js delete mode 100644 ui/core/app/controllers/orgs/org/projects/project.js delete mode 100644 ui/core/app/controllers/orgs/org/projects/project/host-catalogs.js delete mode 100644 ui/core/app/controllers/orgs/org/projects/project/host-catalogs/host-catalog.js delete mode 100644 ui/core/app/controllers/orgs/org/roles.js delete mode 100644 ui/core/app/controllers/orgs/org/roles/new.js delete mode 100644 ui/core/app/controllers/orgs/org/roles/role.js delete mode 100644 ui/core/app/controllers/orgs/org/users.js delete mode 100644 ui/core/app/controllers/orgs/org/users/new.js delete mode 100644 ui/core/app/controllers/orgs/org/users/user.js rename ui/core/app/controllers/{orgs/org => scopes/scope}/projects/project/host-catalogs/new.js (65%) delete mode 100644 ui/core/app/routes/orgs.js delete mode 100644 ui/core/app/routes/orgs/org.js delete mode 100644 ui/core/app/routes/orgs/org/groups.js delete mode 100644 ui/core/app/routes/orgs/org/groups/group.js delete mode 100644 ui/core/app/routes/orgs/org/groups/group/index.js delete mode 100644 ui/core/app/routes/orgs/org/groups/index.js delete mode 100644 ui/core/app/routes/orgs/org/groups/new.js delete mode 100644 ui/core/app/routes/orgs/org/projects.js delete mode 100644 ui/core/app/routes/orgs/org/projects/index.js delete mode 100644 ui/core/app/routes/orgs/org/projects/new.js delete mode 100644 ui/core/app/routes/orgs/org/projects/project.js delete mode 100644 ui/core/app/routes/orgs/org/projects/project/host-catalogs.js delete mode 100644 ui/core/app/routes/orgs/org/projects/project/host-catalogs/host-catalog.js delete mode 100644 ui/core/app/routes/orgs/org/projects/project/host-catalogs/host-catalog/index.js delete mode 100644 ui/core/app/routes/orgs/org/projects/project/host-catalogs/index.js delete mode 100644 ui/core/app/routes/orgs/org/projects/project/host-catalogs/new.js delete mode 100644 ui/core/app/routes/orgs/org/projects/project/index.js delete mode 100644 ui/core/app/routes/orgs/org/roles.js delete mode 100644 ui/core/app/routes/orgs/org/roles/index.js delete mode 100644 ui/core/app/routes/orgs/org/roles/new.js delete mode 100644 ui/core/app/routes/orgs/org/roles/role.js delete mode 100644 ui/core/app/routes/orgs/org/users.js delete mode 100644 ui/core/app/routes/orgs/org/users/index.js delete mode 100644 ui/core/app/routes/orgs/org/users/new.js delete mode 100644 ui/core/app/routes/orgs/org/users/user.js delete mode 100644 ui/core/app/templates/orgs.hbs delete mode 100644 ui/core/app/templates/orgs/org.hbs delete mode 100644 ui/core/app/templates/orgs/org/error.hbs delete mode 100644 ui/core/app/templates/orgs/org/groups.hbs delete mode 100644 ui/core/app/templates/orgs/org/groups/group.hbs delete mode 100644 ui/core/app/templates/orgs/org/groups/group/index.hbs delete mode 100644 ui/core/app/templates/orgs/org/groups/index.hbs delete mode 100644 ui/core/app/templates/orgs/org/groups/new.hbs delete mode 100644 ui/core/app/templates/orgs/org/projects.hbs delete mode 100644 ui/core/app/templates/orgs/org/projects/index.hbs delete mode 100644 ui/core/app/templates/orgs/org/projects/new.hbs delete mode 100644 ui/core/app/templates/orgs/org/projects/project.hbs delete mode 100644 ui/core/app/templates/orgs/org/projects/project/_sidebar.hbs delete mode 100644 ui/core/app/templates/orgs/org/projects/project/host-catalogs.hbs delete mode 100644 ui/core/app/templates/orgs/org/projects/project/host-catalogs/host-catalog.hbs delete mode 100644 ui/core/app/templates/orgs/org/projects/project/host-catalogs/host-catalog/index.hbs delete mode 100644 ui/core/app/templates/orgs/org/projects/project/host-catalogs/index.hbs delete mode 100644 ui/core/app/templates/orgs/org/projects/project/host-catalogs/new.hbs delete mode 100644 ui/core/app/templates/orgs/org/projects/project/index.hbs delete mode 100644 ui/core/app/templates/orgs/org/roles.hbs delete mode 100644 ui/core/app/templates/orgs/org/roles/index.hbs delete mode 100644 ui/core/app/templates/orgs/org/roles/new.hbs delete mode 100644 ui/core/app/templates/orgs/org/roles/role.hbs delete mode 100644 ui/core/app/templates/orgs/org/roles/role/index.hbs delete mode 100644 ui/core/app/templates/orgs/org/users.hbs delete mode 100644 ui/core/app/templates/orgs/org/users/index.hbs delete mode 100644 ui/core/app/templates/orgs/org/users/new.hbs delete mode 100644 ui/core/app/templates/orgs/org/users/user.hbs delete mode 100644 ui/core/app/templates/orgs/org/users/user/index.hbs delete mode 100644 ui/core/tests/acceptance/index-test.js delete mode 100644 ui/core/tests/unit/controllers/orgs/org/groups-test.js delete mode 100644 ui/core/tests/unit/controllers/orgs/org/groups/group-test.js delete mode 100644 ui/core/tests/unit/controllers/orgs/org/groups/new-test.js delete mode 100644 ui/core/tests/unit/controllers/orgs/org/projects-test.js delete mode 100644 ui/core/tests/unit/controllers/orgs/org/projects/new-test.js delete mode 100644 ui/core/tests/unit/controllers/orgs/org/projects/project-test.js delete mode 100644 ui/core/tests/unit/controllers/orgs/org/projects/project/host-catalogs-test.js delete mode 100644 ui/core/tests/unit/controllers/orgs/org/projects/project/host-catalogs/host-catalog-test.js delete mode 100644 ui/core/tests/unit/controllers/orgs/org/projects/project/host-catalogs/new-test.js delete mode 100644 ui/core/tests/unit/controllers/orgs/org/roles-test.js delete mode 100644 ui/core/tests/unit/controllers/orgs/org/roles/new-test.js delete mode 100644 ui/core/tests/unit/controllers/orgs/org/roles/role-test.js delete mode 100644 ui/core/tests/unit/controllers/orgs/org/users-test.js delete mode 100644 ui/core/tests/unit/controllers/orgs/org/users/new-test.js delete mode 100644 ui/core/tests/unit/controllers/orgs/org/users/user-test.js create mode 100644 ui/core/tests/unit/controllers/scopes/scope/projects/project/host-catalogs/new-test.js delete mode 100644 ui/core/tests/unit/routes/orgs-test.js delete mode 100644 ui/core/tests/unit/routes/orgs/org-test.js delete mode 100644 ui/core/tests/unit/routes/orgs/org/groups-test.js delete mode 100644 ui/core/tests/unit/routes/orgs/org/groups/group-test.js delete mode 100644 ui/core/tests/unit/routes/orgs/org/groups/group/index-test.js delete mode 100644 ui/core/tests/unit/routes/orgs/org/groups/index-test.js delete mode 100644 ui/core/tests/unit/routes/orgs/org/groups/new-test.js delete mode 100644 ui/core/tests/unit/routes/orgs/org/projects-test.js delete mode 100644 ui/core/tests/unit/routes/orgs/org/projects/index-test.js delete mode 100644 ui/core/tests/unit/routes/orgs/org/projects/new-test.js delete mode 100644 ui/core/tests/unit/routes/orgs/org/projects/project-test.js delete mode 100644 ui/core/tests/unit/routes/orgs/org/projects/project/host-catalogs-test.js delete mode 100644 ui/core/tests/unit/routes/orgs/org/projects/project/host-catalogs/host-catalog-test.js delete mode 100644 ui/core/tests/unit/routes/orgs/org/projects/project/host-catalogs/host-catalog/index-test.js delete mode 100644 ui/core/tests/unit/routes/orgs/org/projects/project/host-catalogs/index-test.js delete mode 100644 ui/core/tests/unit/routes/orgs/org/projects/project/host-catalogs/new-test.js delete mode 100644 ui/core/tests/unit/routes/orgs/org/projects/project/index-test.js delete mode 100644 ui/core/tests/unit/routes/orgs/org/roles-test.js delete mode 100644 ui/core/tests/unit/routes/orgs/org/roles/index-test.js delete mode 100644 ui/core/tests/unit/routes/orgs/org/roles/new-test.js delete mode 100644 ui/core/tests/unit/routes/orgs/org/roles/role-test.js delete mode 100644 ui/core/tests/unit/routes/orgs/org/users-test.js delete mode 100644 ui/core/tests/unit/routes/orgs/org/users/index-test.js delete mode 100644 ui/core/tests/unit/routes/orgs/org/users/new-test.js delete mode 100644 ui/core/tests/unit/routes/orgs/org/users/user-test.js diff --git a/ui/core/app/controllers/orgs/org/groups.js b/ui/core/app/controllers/orgs/org/groups.js deleted file mode 100644 index b6c1716e9..000000000 --- a/ui/core/app/controllers/orgs/org/groups.js +++ /dev/null @@ -1,17 +0,0 @@ -import Controller from '@ember/controller'; -import { inject as service } from '@ember/service'; - -export default class OrgsOrgGroupsController extends Controller { - // =services - - @service intl; - - // =attributes - - /** - * @type {string} - */ - get breadCrumb() { - return this.intl.t('resources.groups'); - } -} diff --git a/ui/core/app/controllers/orgs/org/groups/group.js b/ui/core/app/controllers/orgs/org/groups/group.js deleted file mode 100644 index 0d12865f7..000000000 --- a/ui/core/app/controllers/orgs/org/groups/group.js +++ /dev/null @@ -1,11 +0,0 @@ -import Controller from '@ember/controller'; -import { alias } from '@ember/object/computed'; - -export default class OrgsOrgGroupsGroupController extends Controller { - // =attributes - - /** - * @type {string} - */ - @alias('model.displayName') breadCrumb; -} diff --git a/ui/core/app/controllers/orgs/org/groups/new.js b/ui/core/app/controllers/orgs/org/groups/new.js deleted file mode 100644 index 1c5eeb7c9..000000000 --- a/ui/core/app/controllers/orgs/org/groups/new.js +++ /dev/null @@ -1,17 +0,0 @@ -import Controller from '@ember/controller'; -import { inject as service } from '@ember/service'; - -export default class OrgsOrgGroupsNewController extends Controller { - // =services - - @service intl; - - // =attributes - - /** - * @type {string} - */ - get breadCrumb() { - return this.intl.t('titles.new'); - } -} diff --git a/ui/core/app/controllers/orgs/org/projects.js b/ui/core/app/controllers/orgs/org/projects.js deleted file mode 100644 index 60c1441a7..000000000 --- a/ui/core/app/controllers/orgs/org/projects.js +++ /dev/null @@ -1,17 +0,0 @@ -import Controller from '@ember/controller'; -import { inject as service } from '@ember/service'; - -export default class OrgsOrgProjectsController extends Controller { - // =services - - @service intl; - - // =attributes - - /** - * @type {string} - */ - get breadCrumb() { - return this.intl.t('resources.projects'); - } -} diff --git a/ui/core/app/controllers/orgs/org/projects/new.js b/ui/core/app/controllers/orgs/org/projects/new.js deleted file mode 100644 index 769d6e195..000000000 --- a/ui/core/app/controllers/orgs/org/projects/new.js +++ /dev/null @@ -1,17 +0,0 @@ -import Controller from '@ember/controller'; -import { inject as service } from '@ember/service'; - -export default class OrgsOrgProjectsNewController extends Controller { - // =services - - @service intl; - - // =attributes - - /** - * @type {string} - */ - get breadCrumb() { - return this.intl.t('titles.new'); - } -} diff --git a/ui/core/app/controllers/orgs/org/projects/project.js b/ui/core/app/controllers/orgs/org/projects/project.js deleted file mode 100644 index c449ef523..000000000 --- a/ui/core/app/controllers/orgs/org/projects/project.js +++ /dev/null @@ -1,11 +0,0 @@ -import Controller from '@ember/controller'; -import { alias } from '@ember/object/computed'; - -export default class OrgsOrgProjectsProjectController extends Controller { - // =attributes - - /** - * @type {string} - */ - @alias('model.displayName') breadCrumb; -} diff --git a/ui/core/app/controllers/orgs/org/projects/project/host-catalogs.js b/ui/core/app/controllers/orgs/org/projects/project/host-catalogs.js deleted file mode 100644 index ed10e744b..000000000 --- a/ui/core/app/controllers/orgs/org/projects/project/host-catalogs.js +++ /dev/null @@ -1,17 +0,0 @@ -import Controller from '@ember/controller'; -import { inject as service } from '@ember/service'; - -export default class OrgsOrgProjectsProjectHostCatalogsController extends Controller { - // =services - - @service intl; - - // =attributes - - /** - * @type {string} - */ - get breadCrumb() { - return this.intl.t('resources.host-catalogs'); - } -} diff --git a/ui/core/app/controllers/orgs/org/projects/project/host-catalogs/host-catalog.js b/ui/core/app/controllers/orgs/org/projects/project/host-catalogs/host-catalog.js deleted file mode 100644 index 1e733a5aa..000000000 --- a/ui/core/app/controllers/orgs/org/projects/project/host-catalogs/host-catalog.js +++ /dev/null @@ -1,11 +0,0 @@ -import Controller from '@ember/controller'; -import { alias } from '@ember/object/computed'; - -export default class OrgsOrgProjectsProjectHostCatalogsHostCatalogController extends Controller { - // =attributes - - /** - * @type {string} - */ - @alias('model.displayName') breadCrumb; -} diff --git a/ui/core/app/controllers/orgs/org/roles.js b/ui/core/app/controllers/orgs/org/roles.js deleted file mode 100644 index 16f84ae44..000000000 --- a/ui/core/app/controllers/orgs/org/roles.js +++ /dev/null @@ -1,17 +0,0 @@ -import Controller from '@ember/controller'; -import { inject as service } from '@ember/service'; - -export default class OrgsOrgRolesController extends Controller { - // =services - - @service intl; - - // =attributes - - /** - * @type {string} - */ - get breadCrumb() { - return this.intl.t('resources.roles'); - } -} diff --git a/ui/core/app/controllers/orgs/org/roles/new.js b/ui/core/app/controllers/orgs/org/roles/new.js deleted file mode 100644 index 126c6a1b8..000000000 --- a/ui/core/app/controllers/orgs/org/roles/new.js +++ /dev/null @@ -1,17 +0,0 @@ -import Controller from '@ember/controller'; -import { inject as service } from '@ember/service'; - -export default class OrgsOrgRolesNewController extends Controller { - // =services - - @service intl; - - // =attributes - - /** - * @type {string} - */ - get breadCrumb() { - return this.intl.t('titles.new'); - } -} diff --git a/ui/core/app/controllers/orgs/org/roles/role.js b/ui/core/app/controllers/orgs/org/roles/role.js deleted file mode 100644 index 90425d113..000000000 --- a/ui/core/app/controllers/orgs/org/roles/role.js +++ /dev/null @@ -1,11 +0,0 @@ -import Controller from '@ember/controller'; -import { alias } from '@ember/object/computed'; - -export default class OrgsOrgRolesRoleController extends Controller { - // =attributes - - /** - * @type {string} - */ - @alias('model.displayName') breadCrumb; -} diff --git a/ui/core/app/controllers/orgs/org/users.js b/ui/core/app/controllers/orgs/org/users.js deleted file mode 100644 index fe28ecf0f..000000000 --- a/ui/core/app/controllers/orgs/org/users.js +++ /dev/null @@ -1,17 +0,0 @@ -import Controller from '@ember/controller'; -import { inject as service } from '@ember/service'; - -export default class OrgsOrgUsersController extends Controller { - // =services - - @service intl; - - // =attributes - - /** - * @type {string} - */ - get breadCrumb() { - return this.intl.t('resources.users'); - } -} diff --git a/ui/core/app/controllers/orgs/org/users/new.js b/ui/core/app/controllers/orgs/org/users/new.js deleted file mode 100644 index 5e047015a..000000000 --- a/ui/core/app/controllers/orgs/org/users/new.js +++ /dev/null @@ -1,17 +0,0 @@ -import Controller from '@ember/controller'; -import { inject as service } from '@ember/service'; - -export default class OrgsOrgUsersNewController extends Controller { - // =services - - @service intl; - - // =attributes - - /** - * @type {string} - */ - get breadCrumb() { - return this.intl.t('titles.new'); - } -} diff --git a/ui/core/app/controllers/orgs/org/users/user.js b/ui/core/app/controllers/orgs/org/users/user.js deleted file mode 100644 index 3461021b1..000000000 --- a/ui/core/app/controllers/orgs/org/users/user.js +++ /dev/null @@ -1,11 +0,0 @@ -import Controller from '@ember/controller'; -import { alias } from '@ember/object/computed'; - -export default class OrgsOrgUsersUserController extends Controller { - // =attributes - - /** - * @type {string} - */ - @alias('model.displayName') breadCrumb; -} diff --git a/ui/core/app/controllers/orgs/org/projects/project/host-catalogs/new.js b/ui/core/app/controllers/scopes/scope/projects/project/host-catalogs/new.js similarity index 65% rename from ui/core/app/controllers/orgs/org/projects/project/host-catalogs/new.js rename to ui/core/app/controllers/scopes/scope/projects/project/host-catalogs/new.js index 02b865cd0..549965fb3 100644 --- a/ui/core/app/controllers/orgs/org/projects/project/host-catalogs/new.js +++ b/ui/core/app/controllers/scopes/scope/projects/project/host-catalogs/new.js @@ -1,7 +1,8 @@ import Controller from '@ember/controller'; import { inject as service } from '@ember/service'; -export default class OrgsOrgProjectsProjectHostCatalogsNewController extends Controller { +export default class ScopesScopeProjectsProjectHostCatalogsNewController extends Controller { + // =services @service intl; @@ -9,9 +10,11 @@ export default class OrgsOrgProjectsProjectHostCatalogsNewController extends Con // =attributes /** + * Translated new role breadcrumb * @type {string} */ get breadCrumb() { return this.intl.t('titles.new'); } + } diff --git a/ui/core/app/router.js b/ui/core/app/router.js index 5275ca1a7..a4f121c19 100644 --- a/ui/core/app/router.js +++ b/ui/core/app/router.js @@ -35,37 +35,4 @@ Router.map(function () { }); }); }); - - this.route('orgs', function () { - this.route('org', { path: ':org_id' }, function () { - this.route('projects', function () { - this.route('project', { path: ':project_id' }, function () { - this.route('host-catalogs', function () { - this.route( - 'host-catalog', - { path: ':host_catalog_id' }, - function () {} - ); - this.route('new'); - }); - }); - this.route('new'); - }); - - this.route('users', function () { - this.route('user', { path: ':user_id' }, function () {}); - this.route('new'); - }); - - this.route('roles', function () { - this.route('role', { path: ':role_id' }, function () {}); - this.route('new'); - }); - - this.route('groups', function () { - this.route('group', { path: ':group_id' }, function () {}); - this.route('new'); - }); - }); - }); }); diff --git a/ui/core/app/routes/orgs.js b/ui/core/app/routes/orgs.js deleted file mode 100644 index f41f51d52..000000000 --- a/ui/core/app/routes/orgs.js +++ /dev/null @@ -1,13 +0,0 @@ -import Route from '@ember/routing/route'; - -export default class OrgsRoute extends Route { - // =methods - - /** - * Returns all orgs from the store. - * @return {Promise[OrgModel]} - */ - model() { - return this.store.findAll('org'); - } -} diff --git a/ui/core/app/routes/orgs/org.js b/ui/core/app/routes/orgs/org.js deleted file mode 100644 index 59055906e..000000000 --- a/ui/core/app/routes/orgs/org.js +++ /dev/null @@ -1,28 +0,0 @@ -import Route from '@ember/routing/route'; -import { inject as service } from '@ember/service'; - -export default class OrgsOrgRoute extends Route { - // =services - - @service scope; - - // =methods - - /** - * Returns an org by id. - * @param {object} params - * @return {Promise{OrgModel}} - */ - model({ org_id: id }) { - return this.store.findRecord('org', id); - } - - /** - * Adds the org to the scope. - * @param {object} org - */ - afterModel(org) { - this.scope.org = org; - return super.afterModel(...arguments); - } -} diff --git a/ui/core/app/routes/orgs/org/groups.js b/ui/core/app/routes/orgs/org/groups.js deleted file mode 100644 index 80bb0af1a..000000000 --- a/ui/core/app/routes/orgs/org/groups.js +++ /dev/null @@ -1,13 +0,0 @@ -import Route from '@ember/routing/route'; - -export default class OrgsOrgGroupsRoute extends Route { - // =methods - - /** - * Returns all groups from store. - * @return {Promise[GroupModel]} - */ - model() { - return this.store.findAll('group'); - } -} diff --git a/ui/core/app/routes/orgs/org/groups/group.js b/ui/core/app/routes/orgs/org/groups/group.js deleted file mode 100644 index 98c6b65c9..000000000 --- a/ui/core/app/routes/orgs/org/groups/group.js +++ /dev/null @@ -1,64 +0,0 @@ -import Route from '@ember/routing/route'; -import { inject as service } from '@ember/service'; -import { action } from '@ember/object'; - -export default class OrgsOrgGroupsGroupRoute extends Route { - // =services - - @service intl; - @service notify; - - // =methods - - /** - * Return a group with id - * @param {object} params - * @return {Promise{GroupModel}} - */ - model({ group_id: id }) { - return this.store.findRecord('group', id); - } - - // =actions - - /** - * Rollback changes on group. - * @param {GroupModel} group - */ - @action - cancel(group) { - group.rollbackAttributes(); - } - - /** - * Handle saving a group. - * @param {GroupModel} group - * @param {Event} e - */ - @action - async save(group) { - try { - await group.save(); - this.notify.success(this.intl.t('notify.save-success')); - } catch (error) { - // TODO: replace with translated strings - this.notify.error(error.message, { closeAfter: null }); - } - } - - /** - * Delete group and redirect to groups index. - * @param {GroupModel} group - */ - @action - async delete(group) { - try { - await group.destroyRecord(); - this.transitionTo('orgs.org.groups'); - this.notify.success(this.intl.t('notify.group.delete-success')); - } catch (error) { - // TODO: replace with translated strings - this.notify.error(error.message, { closeAfter: null }); - } - } -} diff --git a/ui/core/app/routes/orgs/org/groups/group/index.js b/ui/core/app/routes/orgs/org/groups/group/index.js deleted file mode 100644 index 9bd8f2675..000000000 --- a/ui/core/app/routes/orgs/org/groups/group/index.js +++ /dev/null @@ -1,3 +0,0 @@ -import Route from '@ember/routing/route'; - -export default class OrgsOrgGroupsGroupIndexRoute extends Route {} diff --git a/ui/core/app/routes/orgs/org/groups/index.js b/ui/core/app/routes/orgs/org/groups/index.js deleted file mode 100644 index 9c566dfa0..000000000 --- a/ui/core/app/routes/orgs/org/groups/index.js +++ /dev/null @@ -1,3 +0,0 @@ -import Route from '@ember/routing/route'; - -export default class OrgsOrgGroupsIndexRoute extends Route {} diff --git a/ui/core/app/routes/orgs/org/groups/new.js b/ui/core/app/routes/orgs/org/groups/new.js deleted file mode 100644 index 6f353d368..000000000 --- a/ui/core/app/routes/orgs/org/groups/new.js +++ /dev/null @@ -1,50 +0,0 @@ -import Route from '@ember/routing/route'; -import { inject as service } from '@ember/service'; -import { action } from '@ember/object'; - -export default class OrgsOrgGroupsNewRoute extends Route { - // =services - - @service intl; - @service notify; - - // =methods - - /** - * Returns a new unsaved group. - * @return {Promise{GroupModel}} - */ - model() { - return this.store.createRecord('group'); - } - - // =actions - - /** - * Rollback changes on group by destroying unsaved instance - * and redirect to group index. - * @param {GroupModel} group - */ - @action - cancel(group) { - group.rollbackAttributes(); - this.transitionTo('orgs.org.groups'); - } - - /** - * Handle saving a group. - * @param {GroupModel} group - * @param {Event} e - */ - @action - async save(group) { - try { - await group.save(); - this.transitionTo('orgs.org.groups.group', group); - this.notify.success(this.intl.t('notify.group.create-success')); - } catch (error) { - // TODO: replace with translated strings - this.notify.error(error.message, { closeAfter: null }); - } - } -} diff --git a/ui/core/app/routes/orgs/org/projects.js b/ui/core/app/routes/orgs/org/projects.js deleted file mode 100644 index ee50e8f8a..000000000 --- a/ui/core/app/routes/orgs/org/projects.js +++ /dev/null @@ -1,13 +0,0 @@ -import Route from '@ember/routing/route'; - -export default class OrgsOrgProjectsRoute extends Route { - // =methods - - /** - * Returns all projects from the store. - * @return {Promise[ProjectModel]} - */ - model() { - return this.store.findAll('project'); - } -} diff --git a/ui/core/app/routes/orgs/org/projects/index.js b/ui/core/app/routes/orgs/org/projects/index.js deleted file mode 100644 index be2068f6d..000000000 --- a/ui/core/app/routes/orgs/org/projects/index.js +++ /dev/null @@ -1,3 +0,0 @@ -import Route from '@ember/routing/route'; - -export default class OrgsOrgProjectsIndexRoute extends Route {} diff --git a/ui/core/app/routes/orgs/org/projects/new.js b/ui/core/app/routes/orgs/org/projects/new.js deleted file mode 100644 index 1314716cd..000000000 --- a/ui/core/app/routes/orgs/org/projects/new.js +++ /dev/null @@ -1,50 +0,0 @@ -import Route from '@ember/routing/route'; -import { inject as service } from '@ember/service'; -import { action } from '@ember/object'; - -export default class OrgsOrgProjectsNewRoute extends Route { - // =services - - @service notify; - - // =methods - - /** - * Returns a new unsaved project. - * @return {Promise{ProjectModel}} - */ - model() { - return this.store.createRecord('project'); - } - - // =actions - - /** - * Rollback changes on project (which destroys unsaved instances) - * and redirects to projects index. - * @param {ProjectModel} project - */ - @action - cancel(project) { - project.rollbackAttributes(); - this.transitionTo('orgs.org.projects'); - } - - /** - * Handle save project. - * @param {ProjectModel} project - * @param {Event} e - */ - @action - async save(project) { - try { - await project.save(); - this.transitionTo('orgs.org.projects.project', project); - // TODO: replace with translated strings - this.notify.success('Project created.'); - } catch (error) { - // TODO: replace with translated strings - this.notify.error(error.message, { closeAfter: null }); - } - } -} diff --git a/ui/core/app/routes/orgs/org/projects/project.js b/ui/core/app/routes/orgs/org/projects/project.js deleted file mode 100644 index 3171733ff..000000000 --- a/ui/core/app/routes/orgs/org/projects/project.js +++ /dev/null @@ -1,90 +0,0 @@ -import Route from '@ember/routing/route'; -import { inject as service } from '@ember/service'; -import { action } from '@ember/object'; - -export default class OrgsOrgProjectsProjectRoute extends Route { - // =services - - @service scope; - @service notify; - - // =methods - - /** - * Returns a project by id. - * @param {object} params - * @return {Promise{ProjectModel}} - */ - model({ project_id: id }) { - return this.store.findRecord('project', id); - } - - /** - * Adds the project to the scope. - * @param {ProjectModel} project - */ - afterModel(project) { - this.scope.project = project; - return super.afterModel(...arguments); - } - - /** - * Renders the project-specific sidebar template. - * @override - * @param {object} controller - * @param {object} model - */ - renderTemplate(controller, model) { - this.render('orgs/org/projects/project/_sidebar', { - into: 'orgs/org', - outlet: 'sidebar', - model: model, - }); - super.renderTemplate(...arguments); - } - - // =actions - - /** - * Rollback changes on project. - * @param {ProjectModel} project - */ - @action - cancel(project) { - project.rollbackAttributes(); - } - - /** - * Handle save project. - * @param {ProjectModel} project - * @param {Event} e - */ - @action - async save(project) { - try { - await project.save(); - // TODO: replace with translated strings - this.notify.success('Save succeeded.'); - } catch (error) { - // TODO: replace with translated strings - this.notify.error(error.message, { closeAfter: null }); - } - } - - /** - * Deletes the project and redirects to index. - * @param {ProjectModel} project - */ - @action - async delete(project) { - try { - await project.destroyRecord(); - this.transitionTo('orgs.org.projects'); - // TODO: replace with translated strings - this.notify.success('Deleted project succesfully.'); - } catch (error) { - // TODO: replace with translated strings - this.notify.error(error.message, { closeAfter: null }); - } - } -} diff --git a/ui/core/app/routes/orgs/org/projects/project/host-catalogs.js b/ui/core/app/routes/orgs/org/projects/project/host-catalogs.js deleted file mode 100644 index 685266cb4..000000000 --- a/ui/core/app/routes/orgs/org/projects/project/host-catalogs.js +++ /dev/null @@ -1,13 +0,0 @@ -import Route from '@ember/routing/route'; - -export default class OrgsOrgProjectsProjectHostCatalogsRoute extends Route { - // =methods - - /** - * Returns host catalogs for the current scope. - * @return {Promise[HostCatalogModel]} - */ - model() { - return this.store.findAll('host-catalog'); - } -} diff --git a/ui/core/app/routes/orgs/org/projects/project/host-catalogs/host-catalog.js b/ui/core/app/routes/orgs/org/projects/project/host-catalogs/host-catalog.js deleted file mode 100644 index 47d79f7bb..000000000 --- a/ui/core/app/routes/orgs/org/projects/project/host-catalogs/host-catalog.js +++ /dev/null @@ -1,65 +0,0 @@ -import Route from '@ember/routing/route'; -import { inject as service } from '@ember/service'; -import { action } from '@ember/object'; - -export default class OrgsOrgProjectsProjectHostCatalogsHostCatalogRoute extends Route { - // =services - - @service notify; - - // =methods - - /** - * Returns a host catalog by id. - * @param {object} params - * @return {Promise{HostCatalogModel}} - */ - model({ host_catalog_id: id }) { - return this.store.findRecord('host-catalog', id); - } - - // =actions - - /** - * Rollback changes on host catalog. - * @param {HostCatalogModel} hostCatalog - */ - @action - cancel(hostCatalog) { - hostCatalog.rollbackAttributes(); - } - - /** - * Handle save host catalog. - * @param {HostCatalogModel} host catalog - * @param {Event} e - */ - @action - async save(hostCatalog) { - try { - await hostCatalog.save(); - // TODO: replace with translated strings - this.notify.success('Save succeeded.'); - } catch (error) { - // TODO: replace with translated strings - this.notify.error(error.message, { closeAfter: null }); - } - } - - /** - * Deletes the host catalog and redirects to index. - * @param {HostCatalogModel} hostCatalog - */ - @action - async delete(hostCatalog) { - try { - await hostCatalog.destroyRecord(); - this.transitionTo('orgs.org.projects.project.host-catalogs'); - // TODO: replace with translated strings - this.notify.success('Deleted host catalog succesfully.'); - } catch (error) { - // TODO: replace with translated strings - this.notify.error(error.message, { closeAfter: null }); - } - } -} diff --git a/ui/core/app/routes/orgs/org/projects/project/host-catalogs/host-catalog/index.js b/ui/core/app/routes/orgs/org/projects/project/host-catalogs/host-catalog/index.js deleted file mode 100644 index a4ba3aa0e..000000000 --- a/ui/core/app/routes/orgs/org/projects/project/host-catalogs/host-catalog/index.js +++ /dev/null @@ -1,3 +0,0 @@ -import Route from '@ember/routing/route'; - -export default class OrgsOrgProjectsProjectHostCatalogsHostCatalogIndexRoute extends Route {} diff --git a/ui/core/app/routes/orgs/org/projects/project/host-catalogs/index.js b/ui/core/app/routes/orgs/org/projects/project/host-catalogs/index.js deleted file mode 100644 index b760aed44..000000000 --- a/ui/core/app/routes/orgs/org/projects/project/host-catalogs/index.js +++ /dev/null @@ -1,3 +0,0 @@ -import Route from '@ember/routing/route'; - -export default class OrgsOrgProjectsProjectHostCatalogsIndexRoute extends Route {} diff --git a/ui/core/app/routes/orgs/org/projects/project/host-catalogs/new.js b/ui/core/app/routes/orgs/org/projects/project/host-catalogs/new.js deleted file mode 100644 index c2c224e88..000000000 --- a/ui/core/app/routes/orgs/org/projects/project/host-catalogs/new.js +++ /dev/null @@ -1,53 +0,0 @@ -import Route from '@ember/routing/route'; -import { inject as service } from '@ember/service'; -import { action } from '@ember/object'; - -export default class OrgsOrgProjectsProjectHostCatalogsNewRoute extends Route { - // =services - - @service notify; - - // =methods - - /** - * Returns a new unsaved host catalog. - * @return {Promise{HostCatalogModel}} - */ - model() { - return this.store.createRecord('hostCatalog'); - } - - // =actions - - /** - * Rollback changes on host catalog (which destroys unsaved instances) - * and redirects to host catalogs index. - * @param {HostCatalogModel} hostCatalog - */ - @action - cancel(hostCatalog) { - hostCatalog.rollbackAttributes(); - this.transitionTo('orgs.org.projects.project.host-catalogs'); - } - - /** - * Handle save host catalog. - * @param {HostCatalogModel} hostCatalog - * @param {Event} e - */ - @action - async save(hostCatalog) { - try { - await hostCatalog.save(); - this.transitionTo( - 'orgs.org.projects.project.host-catalogs.host-catalog', - hostCatalog - ); - // TODO: replace with translated strings - this.notify.success('Host catalog created.'); - } catch (error) { - // TODO: replace with translated strings - this.notify.error(error.message, { closeAfter: null }); - } - } -} diff --git a/ui/core/app/routes/orgs/org/projects/project/index.js b/ui/core/app/routes/orgs/org/projects/project/index.js deleted file mode 100644 index 0fc6b90b7..000000000 --- a/ui/core/app/routes/orgs/org/projects/project/index.js +++ /dev/null @@ -1,3 +0,0 @@ -import Route from '@ember/routing/route'; - -export default class OrgsOrgProjectsProjectIndexRoute extends Route {} diff --git a/ui/core/app/routes/orgs/org/roles.js b/ui/core/app/routes/orgs/org/roles.js deleted file mode 100644 index 972aaf816..000000000 --- a/ui/core/app/routes/orgs/org/roles.js +++ /dev/null @@ -1,13 +0,0 @@ -import Route from '@ember/routing/route'; - -export default class OrgsOrgRolesRoute extends Route { - // =methods - - /** - * Returns all roles from store. - * @return {Promise[RoleModel]} - */ - model() { - return this.store.findAll('role'); - } -} diff --git a/ui/core/app/routes/orgs/org/roles/index.js b/ui/core/app/routes/orgs/org/roles/index.js deleted file mode 100644 index 92a715a56..000000000 --- a/ui/core/app/routes/orgs/org/roles/index.js +++ /dev/null @@ -1,3 +0,0 @@ -import Route from '@ember/routing/route'; - -export default class OrgsOrgRolesIndexRoute extends Route {} diff --git a/ui/core/app/routes/orgs/org/roles/new.js b/ui/core/app/routes/orgs/org/roles/new.js deleted file mode 100644 index cad253804..000000000 --- a/ui/core/app/routes/orgs/org/roles/new.js +++ /dev/null @@ -1,50 +0,0 @@ -import Route from '@ember/routing/route'; -import { inject as service } from '@ember/service'; -import { action } from '@ember/object'; - -export default class OrgsOrgRolesNewRoute extends Route { - // =services - - @service intl; - @service notify; - - // =methods - - /** - * Returns a new unsaved role. - * @return {Promise{RoleModel}} - */ - model() { - return this.store.createRecord('role'); - } - - // =actions - - /** - * Rollback changes on role by destroying unsaved instance - * and redirect to role index. - * @param {RoleModel} role - */ - @action - cancel(role) { - role.rollbackAttributes(); - this.transitionTo('orgs.org.roles'); - } - - /** - * Handle saving a role. - * @param {RoleModel} role - * @param {Event} e - */ - @action - async save(role) { - try { - await role.save(); - this.transitionTo('orgs.org.roles.role', role); - this.notify.success(this.intl.t('notify.role.create-success')); - } catch (error) { - // TODO: replace with translated strings - this.notify.error(error.message, { closeAfter: null }); - } - } -} diff --git a/ui/core/app/routes/orgs/org/roles/role.js b/ui/core/app/routes/orgs/org/roles/role.js deleted file mode 100644 index 1820f2ba1..000000000 --- a/ui/core/app/routes/orgs/org/roles/role.js +++ /dev/null @@ -1,64 +0,0 @@ -import Route from '@ember/routing/route'; -import { inject as service } from '@ember/service'; -import { action } from '@ember/object'; - -export default class OrgsOrgRolesRoleRoute extends Route { - // =services - - @service intl; - @service notify; - - // =methods - - /** - * Return a role with id - * @param {object} params - * @return {Promise{RoleModel}} - */ - model({ role_id: id }) { - return this.store.findRecord('role', id); - } - - // =actions - - /** - * Rollback changes on role. - * @param {RoleModel} role - */ - @action - cancel(role) { - role.rollbackAttributes(); - } - - /** - * Handle saving a role. - * @param {RoleModel} role - * @param {Event} e - */ - @action - async save(role) { - try { - await role.save(); - this.notify.success(this.intl.t('notify.save-success')); - } catch (error) { - // TODO: replace with translated strings - this.notify.error(error.message, { closeAfter: null }); - } - } - - /** - * Delete role and redirect to roles index. - * @param {RoleModel} role - */ - @action - async delete(role) { - try { - await role.destroyRecord(); - this.transitionTo('orgs.org.roles'); - this.notify.success(this.intl.t('notify.role.delete-success')); - } catch (error) { - // TODO: replace with translated strings - this.notify.error(error.message, { closeAfter: null }); - } - } -} diff --git a/ui/core/app/routes/orgs/org/users.js b/ui/core/app/routes/orgs/org/users.js deleted file mode 100644 index 7833312e3..000000000 --- a/ui/core/app/routes/orgs/org/users.js +++ /dev/null @@ -1,13 +0,0 @@ -import Route from '@ember/routing/route'; - -export default class OrgsOrgUsersRoute extends Route { - // =methods - - /** - * Returns all users from the store. - * @return {Promise[UserModel]} - */ - model() { - return this.store.findAll('user'); - } -} diff --git a/ui/core/app/routes/orgs/org/users/index.js b/ui/core/app/routes/orgs/org/users/index.js deleted file mode 100644 index b3047d27e..000000000 --- a/ui/core/app/routes/orgs/org/users/index.js +++ /dev/null @@ -1,3 +0,0 @@ -import Route from '@ember/routing/route'; - -export default class OrgsOrgUsersIndexRoute extends Route {} diff --git a/ui/core/app/routes/orgs/org/users/new.js b/ui/core/app/routes/orgs/org/users/new.js deleted file mode 100644 index a740f6caf..000000000 --- a/ui/core/app/routes/orgs/org/users/new.js +++ /dev/null @@ -1,50 +0,0 @@ -import Route from '@ember/routing/route'; -import { inject as service } from '@ember/service'; -import { action } from '@ember/object'; - -export default class OrgsOrgUsersNewRoute extends Route { - // =services - - @service intl; - @service notify; - - // =methods - - /** - * Returns a new unsaved user. - * @return {Promise{UserModel}} - */ - model() { - return this.store.createRecord('user'); - } - - // =actions - - /** - * Rollback changes on user to destroy unsaved instance - * and redirect to users index. - * @param {UserModel} user - */ - @action - cancel(user) { - user.rollbackAttributes(); - this.transitionTo('orgs.org.users'); - } - - /** - * Handle saving a user. - * @param {UserModel} user - * @param {Event} e - */ - @action - async save(user) { - try { - await user.save(); - this.transitionTo('orgs.org.users.user', user); - this.notify.success(this.intl.t('notify.user.create-success')); - } catch (error) { - // TODO: replace with translated strings - this.notify.error(error.message, { closeAfter: null }); - } - } -} diff --git a/ui/core/app/routes/orgs/org/users/user.js b/ui/core/app/routes/orgs/org/users/user.js deleted file mode 100644 index be2ea6f98..000000000 --- a/ui/core/app/routes/orgs/org/users/user.js +++ /dev/null @@ -1,64 +0,0 @@ -import Route from '@ember/routing/route'; -import { inject as service } from '@ember/service'; -import { action } from '@ember/object'; - -export default class OrgsOrgUsersUserRoute extends Route { - // =services - - @service intl; - @service notify; - - // =methods - - /** - * Returns a user by id. - * @param {object} params - * @return {Promise{UserModel}} - */ - model({ user_id: id }) { - return this.store.findRecord('user', id); - } - - // =actions - - /** - * Rollback changes on user. - * @param {UserModel} user - */ - @action - cancel(user) { - user.rollbackAttributes(); - } - - /** - * Handle save user. - * @param {UserModel} user - * @param {Event} e - */ - @action - async save(user) { - try { - await user.save(); - this.notify.success(this.intl.t('notify.save-success')); - } catch (error) { - // TODO: replace with translated strings - this.notify.error(error.message, { closeAfter: null }); - } - } - - /** - * Deletes the user and redirects to index. - * @param {UserModel} user - */ - @action - async delete(user) { - try { - await user.destroyRecord(); - this.transitionTo('orgs.org.users'); - this.notify.success(this.intl.t('notify.user.delete-success')); - } catch (error) { - // TODO: replace with translated strings - this.notify.error(error.message, { closeAfter: null }); - } - } -} diff --git a/ui/core/app/templates/orgs.hbs b/ui/core/app/templates/orgs.hbs deleted file mode 100644 index e2147cab0..000000000 --- a/ui/core/app/templates/orgs.hbs +++ /dev/null @@ -1 +0,0 @@ -{{outlet}} \ No newline at end of file diff --git a/ui/core/app/templates/orgs/org.hbs b/ui/core/app/templates/orgs/org.hbs deleted file mode 100644 index 72326b152..000000000 --- a/ui/core/app/templates/orgs/org.hbs +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - {{t "resources.projects"}} - - - - {{outlet "sidebar"}} - - - - {{t "resources.users"}} - - - - {{t "resources.groups"}} - - - - {{t "resources.roles"}} - - - - - - -
- {{outlet}} -
-
- -
diff --git a/ui/core/app/templates/orgs/org/error.hbs b/ui/core/app/templates/orgs/org/error.hbs deleted file mode 100644 index b903c31ad..000000000 --- a/ui/core/app/templates/orgs/org/error.hbs +++ /dev/null @@ -1,58 +0,0 @@ -{{!-- - In an error route, the error instance is `@model`. Error instances contain - a list of errors, each of which has one convenience boolean set on it: - - * Error booleans are attached based on the error status code: - * - * - `error.isUnauthenticated`: the session isn't authenticated - * - `error.isForbidden`: the session is authenticated but does not have - * permission to perform the requested action - * - `error.isNotFound`: the requested resource could not be found - * - `error.isServer`: an internal server error occurred - * - `error.isUnknown`: an error occurred, but we don't know which or - * we don't distinguish it yet - ---}} - - {{#each @model.errors as |error|}} - {{!-- error.status === 401 --}} - {{#if error.isUnauthenticated}} - - {{t 'errors.descriptions.unauthenticated-error'}} - {{t 'actions.help'}} - - {{/if}} - - {{!-- error.status === 403 --}} - {{#if error.isForbidden}} - - {{t 'errors.descriptions.forbidden-error'}} - {{t 'actions.help'}} - - {{/if}} - - {{!-- error.status === 404 --}} - {{#if error.isNotFound}} - - {{t 'errors.descriptions.notfound-error'}} - {{t 'actions.help'}} - - {{/if}} - - {{!-- error.status === 500 --}} - {{#if error.isServer}} - - {{t 'errors.descriptions.server-error'}} - {{t 'actions.help'}} - - {{/if}} - - {{!-- error.status === 🤷 everything else --}} - {{#if error.isUnknown}} - - {{t 'errors.descriptions.unknown-error'}} - {{t 'actions.help'}} - - {{/if}} - {{/each}} - diff --git a/ui/core/app/templates/orgs/org/groups.hbs b/ui/core/app/templates/orgs/org/groups.hbs deleted file mode 100644 index c53da7f4b..000000000 --- a/ui/core/app/templates/orgs/org/groups.hbs +++ /dev/null @@ -1,3 +0,0 @@ -{{title (t "resources.groups")}} - -{{outlet}} diff --git a/ui/core/app/templates/orgs/org/groups/group.hbs b/ui/core/app/templates/orgs/org/groups/group.hbs deleted file mode 100644 index 2b05f838d..000000000 --- a/ui/core/app/templates/orgs/org/groups/group.hbs +++ /dev/null @@ -1,3 +0,0 @@ -{{title @model.name}} - -{{outlet}} diff --git a/ui/core/app/templates/orgs/org/groups/group/index.hbs b/ui/core/app/templates/orgs/org/groups/group/index.hbs deleted file mode 100644 index 576796dae..000000000 --- a/ui/core/app/templates/orgs/org/groups/group/index.hbs +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - -

{{t "resources.group"}}

-
- - - - - - {{t 'actions.delete'}} - - - -
diff --git a/ui/core/app/templates/orgs/org/groups/index.hbs b/ui/core/app/templates/orgs/org/groups/index.hbs deleted file mode 100644 index cf02056b2..000000000 --- a/ui/core/app/templates/orgs/org/groups/index.hbs +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - - -

{{t "resources.groups"}}

-
- - - {{#if @model}} - {{t 'titles.new'}} - {{/if}} - - - - - {{#if @model}} - - - - - {{t 'form.label.name'}} - {{t 'form.label.description'}} - - - - {{#each @model as |group|}} - - - - {{group.name}} - - - {{group.description}} - - {{/each}} - - - - {{else}} - - - - - {{t 'descriptions.groups-welcome'}} - - - - {{t 'titles.new'}} - - - - - {{/if}} - - - -
diff --git a/ui/core/app/templates/orgs/org/groups/new.hbs b/ui/core/app/templates/orgs/org/groups/new.hbs deleted file mode 100644 index 64602883c..000000000 --- a/ui/core/app/templates/orgs/org/groups/new.hbs +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - -

{{t "titles.new"}}

-
- - - - -
diff --git a/ui/core/app/templates/orgs/org/projects.hbs b/ui/core/app/templates/orgs/org/projects.hbs deleted file mode 100644 index 1f709c7f7..000000000 --- a/ui/core/app/templates/orgs/org/projects.hbs +++ /dev/null @@ -1,3 +0,0 @@ -{{title (t "resources.projects")}} - -{{outlet}} diff --git a/ui/core/app/templates/orgs/org/projects/index.hbs b/ui/core/app/templates/orgs/org/projects/index.hbs deleted file mode 100644 index b6cac496b..000000000 --- a/ui/core/app/templates/orgs/org/projects/index.hbs +++ /dev/null @@ -1,51 +0,0 @@ - - - - - - - -

{{t "resources.projects"}}

-
- - - {{#if @model}} - {{t 'titles.new'}} - {{/if}} - - - - - {{#if @model}} - - - {{#each @model as |project|}} - - {{/each}} - - - {{else}} - - - - - {{t 'descriptions.projects-welcome'}} - - - - {{t 'titles.new'}} - - - - - {{/if}} - - - -
diff --git a/ui/core/app/templates/orgs/org/projects/new.hbs b/ui/core/app/templates/orgs/org/projects/new.hbs deleted file mode 100644 index 5289636f7..000000000 --- a/ui/core/app/templates/orgs/org/projects/new.hbs +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - -

{{t "titles.new"}}

-
- - - - -
diff --git a/ui/core/app/templates/orgs/org/projects/project.hbs b/ui/core/app/templates/orgs/org/projects/project.hbs deleted file mode 100644 index d66e83390..000000000 --- a/ui/core/app/templates/orgs/org/projects/project.hbs +++ /dev/null @@ -1,3 +0,0 @@ -{{title @model.displayName}} - -{{outlet}} diff --git a/ui/core/app/templates/orgs/org/projects/project/_sidebar.hbs b/ui/core/app/templates/orgs/org/projects/project/_sidebar.hbs deleted file mode 100644 index 8581e1414..000000000 --- a/ui/core/app/templates/orgs/org/projects/project/_sidebar.hbs +++ /dev/null @@ -1,15 +0,0 @@ - - - - {{t "titles.overview"}} - - - - {{t "resources.host-catalogs"}} - - - diff --git a/ui/core/app/templates/orgs/org/projects/project/host-catalogs.hbs b/ui/core/app/templates/orgs/org/projects/project/host-catalogs.hbs deleted file mode 100644 index 6ad0035fb..000000000 --- a/ui/core/app/templates/orgs/org/projects/project/host-catalogs.hbs +++ /dev/null @@ -1,3 +0,0 @@ -{{title (t "resources.host-catalogs")}} - -{{outlet}} diff --git a/ui/core/app/templates/orgs/org/projects/project/host-catalogs/host-catalog.hbs b/ui/core/app/templates/orgs/org/projects/project/host-catalogs/host-catalog.hbs deleted file mode 100644 index d66e83390..000000000 --- a/ui/core/app/templates/orgs/org/projects/project/host-catalogs/host-catalog.hbs +++ /dev/null @@ -1,3 +0,0 @@ -{{title @model.displayName}} - -{{outlet}} diff --git a/ui/core/app/templates/orgs/org/projects/project/host-catalogs/host-catalog/index.hbs b/ui/core/app/templates/orgs/org/projects/project/host-catalogs/host-catalog/index.hbs deleted file mode 100644 index 735397eef..000000000 --- a/ui/core/app/templates/orgs/org/projects/project/host-catalogs/host-catalog/index.hbs +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - -

{{t "resources.host-catalog"}}

-
- - - - - - {{t 'actions.delete'}} - - - -
diff --git a/ui/core/app/templates/orgs/org/projects/project/host-catalogs/index.hbs b/ui/core/app/templates/orgs/org/projects/project/host-catalogs/index.hbs deleted file mode 100644 index 6e712fb11..000000000 --- a/ui/core/app/templates/orgs/org/projects/project/host-catalogs/index.hbs +++ /dev/null @@ -1,68 +0,0 @@ - - - - - - - -

{{t 'resources.host-catalogs'}}

-

{{t 'descriptions.host-catalogs'}}

-
- - - {{#if @model}} - - {{t 'titles.new'}} - - {{/if}} - - - - - {{#if @model}} - - - - - {{t 'form.label.name'}} - {{t 'form.label.type'}} - {{t 'form.label.description'}} - - - - {{#each @model as |hostCatalog|}} - - - - {{hostCatalog.name}} - - - {{hostCatalog.type}} - {{hostCatalog.description}} - - {{/each}} - - - - {{else}} - - - - - {{t 'descriptions.host-catalogs-welcome'}} - - - - {{t 'titles.new'}} - - - - - {{/if}} - - - -
diff --git a/ui/core/app/templates/orgs/org/projects/project/host-catalogs/new.hbs b/ui/core/app/templates/orgs/org/projects/project/host-catalogs/new.hbs deleted file mode 100644 index 1ac7bf86f..000000000 --- a/ui/core/app/templates/orgs/org/projects/project/host-catalogs/new.hbs +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - -

{{t "titles.new"}}

-
- - - - - -
diff --git a/ui/core/app/templates/orgs/org/projects/project/index.hbs b/ui/core/app/templates/orgs/org/projects/project/index.hbs deleted file mode 100644 index 77050cd72..000000000 --- a/ui/core/app/templates/orgs/org/projects/project/index.hbs +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - -

{{t "resources.project"}}

-
- - - - - {{t 'titles.settings'}} - - - - - - - - - {{t 'actions.delete'}} - - - -
diff --git a/ui/core/app/templates/orgs/org/roles.hbs b/ui/core/app/templates/orgs/org/roles.hbs deleted file mode 100644 index 6d511b7f7..000000000 --- a/ui/core/app/templates/orgs/org/roles.hbs +++ /dev/null @@ -1,3 +0,0 @@ -{{title (t "resources.roles")}} - -{{outlet}} diff --git a/ui/core/app/templates/orgs/org/roles/index.hbs b/ui/core/app/templates/orgs/org/roles/index.hbs deleted file mode 100644 index 07e3acfaa..000000000 --- a/ui/core/app/templates/orgs/org/roles/index.hbs +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - - -

{{t "resources.roles"}}

-
- - - {{#if @model}} - {{t 'titles.new'}} - {{/if}} - - - - - {{#if @model}} - - - - - {{t 'form.label.name'}} - {{t 'form.label.description'}} - - - - {{#each @model as |role|}} - - - - {{role.displayName}} - - - {{role.description}} - - {{/each}} - - - - {{else}} - - - - - {{t 'descriptions.roles-welcome'}} - - - - {{t 'titles.new'}} - - - - - {{/if}} - - - -
diff --git a/ui/core/app/templates/orgs/org/roles/new.hbs b/ui/core/app/templates/orgs/org/roles/new.hbs deleted file mode 100644 index 911310f26..000000000 --- a/ui/core/app/templates/orgs/org/roles/new.hbs +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - -

{{t "titles.new"}}

-
- - - - -
diff --git a/ui/core/app/templates/orgs/org/roles/role.hbs b/ui/core/app/templates/orgs/org/roles/role.hbs deleted file mode 100644 index d66e83390..000000000 --- a/ui/core/app/templates/orgs/org/roles/role.hbs +++ /dev/null @@ -1,3 +0,0 @@ -{{title @model.displayName}} - -{{outlet}} diff --git a/ui/core/app/templates/orgs/org/roles/role/index.hbs b/ui/core/app/templates/orgs/org/roles/role/index.hbs deleted file mode 100644 index 1b35c1929..000000000 --- a/ui/core/app/templates/orgs/org/roles/role/index.hbs +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - -

{{t "resources.role"}}

-
- - - - - - {{t 'actions.delete'}} - - - -
diff --git a/ui/core/app/templates/orgs/org/users.hbs b/ui/core/app/templates/orgs/org/users.hbs deleted file mode 100644 index 67ccbeb9e..000000000 --- a/ui/core/app/templates/orgs/org/users.hbs +++ /dev/null @@ -1,3 +0,0 @@ -{{title (t "resources.users")}} - -{{outlet}} diff --git a/ui/core/app/templates/orgs/org/users/index.hbs b/ui/core/app/templates/orgs/org/users/index.hbs deleted file mode 100644 index 8e147d3ac..000000000 --- a/ui/core/app/templates/orgs/org/users/index.hbs +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - - -

{{t "resources.users"}}

-
- - - {{#if @model}} - {{t 'titles.new'}} - {{/if}} - - - - - {{#if @model}} - - - - - {{t 'form.label.name'}} - {{t 'form.label.description'}} - - - - {{#each @model as |user|}} - - - - {{user.name}} - - - {{user.description}} - - {{/each}} - - - - {{else}} - - - - - {{t 'descriptions.users-welcome'}} - - - - {{t 'titles.new'}} - - - - - {{/if}} - - - -
diff --git a/ui/core/app/templates/orgs/org/users/new.hbs b/ui/core/app/templates/orgs/org/users/new.hbs deleted file mode 100644 index 4ae7ef85c..000000000 --- a/ui/core/app/templates/orgs/org/users/new.hbs +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - -

{{t "titles.new"}}

-
- - - - -
diff --git a/ui/core/app/templates/orgs/org/users/user.hbs b/ui/core/app/templates/orgs/org/users/user.hbs deleted file mode 100644 index 2b05f838d..000000000 --- a/ui/core/app/templates/orgs/org/users/user.hbs +++ /dev/null @@ -1,3 +0,0 @@ -{{title @model.name}} - -{{outlet}} diff --git a/ui/core/app/templates/orgs/org/users/user/index.hbs b/ui/core/app/templates/orgs/org/users/user/index.hbs deleted file mode 100644 index a1b876705..000000000 --- a/ui/core/app/templates/orgs/org/users/user/index.hbs +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - -

{{t "resources.user"}}

-
- - - - - - {{t 'actions.delete'}} - - - -
diff --git a/ui/core/app/templates/scopes/scope.hbs b/ui/core/app/templates/scopes/scope.hbs index 172b8d490..62f8be2de 100644 --- a/ui/core/app/templates/scopes/scope.hbs +++ b/ui/core/app/templates/scopes/scope.hbs @@ -7,7 +7,7 @@ {{t "resources.projects"}} - + {{t "resources.users"}} @@ -18,26 +18,10 @@ {{t "resources.roles"}} - + {{outlet "sidebar"}} - - {{!-- - - - {{t "resources.users"}} - - - - {{t "resources.groups"}} - - - - {{t "resources.roles"}} - - - --}} {{/if}} diff --git a/ui/core/tests/acceptance/bread-crumbs-test.js b/ui/core/tests/acceptance/bread-crumbs-test.js index 67288382c..68946549a 100644 --- a/ui/core/tests/acceptance/bread-crumbs-test.js +++ b/ui/core/tests/acceptance/bread-crumbs-test.js @@ -2,6 +2,12 @@ import { module, test } from 'qunit'; import { visit, currentURL, click, find, findAll } from '@ember/test-helpers'; import { setupApplicationTest } from 'ember-qunit'; import setupMirage from 'ember-cli-mirage/test-support/setup-mirage'; +import { + authenticateSession, + // These are left here intentionally for future reference. + //currentSession, + //invalidateSession, +} from 'ember-simple-auth/test-support'; module('Acceptance | breadcrumbs', function (hooks) { setupApplicationTest(hooks); @@ -23,6 +29,7 @@ module('Acceptance | breadcrumbs', function (hooks) { let crumbPath; hooks.beforeEach(function () { + authenticateSession({}); orgScope = this.server.create( 'scope', { diff --git a/ui/core/tests/acceptance/index-test.js b/ui/core/tests/acceptance/index-test.js deleted file mode 100644 index 5a2ec4631..000000000 --- a/ui/core/tests/acceptance/index-test.js +++ /dev/null @@ -1,15 +0,0 @@ -import { module, test } from 'qunit'; -import { visit, currentURL } from '@ember/test-helpers'; -import { setupApplicationTest } from 'ember-qunit'; -import a11yAudit from 'ember-a11y-testing/test-support/audit'; - -module('Acceptance | index', function (hooks) { - setupApplicationTest(hooks); - - test('visiting /', async function (assert) { - assert.expect(1); - await visit('/'); - await a11yAudit(); - assert.equal(currentURL(), '/'); - }); -}); diff --git a/ui/core/tests/acceptance/projects-test.js b/ui/core/tests/acceptance/projects-test.js index bfa87857c..ced4a8f13 100644 --- a/ui/core/tests/acceptance/projects-test.js +++ b/ui/core/tests/acceptance/projects-test.js @@ -4,6 +4,12 @@ import { setupApplicationTest } from 'ember-qunit'; import setupMirage from 'ember-cli-mirage/test-support/setup-mirage'; import a11yAudit from 'ember-a11y-testing/test-support/audit'; import { Response } from 'miragejs'; +import { + authenticateSession, + // These are left here intentionally for future reference. + //currentSession, + //invalidateSession, +} from 'ember-simple-auth/test-support'; module('Acceptance | projects', function (hooks) { setupApplicationTest(hooks); @@ -20,6 +26,7 @@ module('Acceptance | projects', function (hooks) { let initialProjectScopesCount; hooks.beforeEach(function () { + authenticateSession({}); org = this.server.create('scope', { type: 'org' }, 'withChildren'); getProjectScopesCount = () => this.server.schema.scopes.where({ type: 'project' }).length; diff --git a/ui/core/tests/integration/components/bread-crumbs-test.js b/ui/core/tests/integration/components/bread-crumbs-test.js index 2e59085cb..a84f99f55 100644 --- a/ui/core/tests/integration/components/bread-crumbs-test.js +++ b/ui/core/tests/integration/components/bread-crumbs-test.js @@ -22,7 +22,7 @@ module('Integration | Component | bread-crumbs', function (hooks) { this.set('breadCrumbs', [ { label: 'Level 2', - path: 'orgs.org.projects.project', + path: 'scopes.scope.projects.project', model: project, }, ]); diff --git a/ui/core/tests/unit/controllers/orgs/org/groups-test.js b/ui/core/tests/unit/controllers/orgs/org/groups-test.js deleted file mode 100644 index 338abe601..000000000 --- a/ui/core/tests/unit/controllers/orgs/org/groups-test.js +++ /dev/null @@ -1,12 +0,0 @@ -import { module, test } from 'qunit'; -import { setupTest } from 'ember-qunit'; - -module('Unit | Controller | orgs/org/groups', function (hooks) { - setupTest(hooks); - - // Replace this with your real tests. - test('it exists', function (assert) { - let controller = this.owner.lookup('controller:orgs/org/groups'); - assert.ok(controller); - }); -}); diff --git a/ui/core/tests/unit/controllers/orgs/org/groups/group-test.js b/ui/core/tests/unit/controllers/orgs/org/groups/group-test.js deleted file mode 100644 index c49531dfb..000000000 --- a/ui/core/tests/unit/controllers/orgs/org/groups/group-test.js +++ /dev/null @@ -1,12 +0,0 @@ -import { module, test } from 'qunit'; -import { setupTest } from 'ember-qunit'; - -module('Unit | Controller | orgs/org/groups/group', function (hooks) { - setupTest(hooks); - - // Replace this with your real tests. - test('it exists', function (assert) { - let controller = this.owner.lookup('controller:orgs/org/groups/group'); - assert.ok(controller); - }); -}); diff --git a/ui/core/tests/unit/controllers/orgs/org/groups/new-test.js b/ui/core/tests/unit/controllers/orgs/org/groups/new-test.js deleted file mode 100644 index a85f11078..000000000 --- a/ui/core/tests/unit/controllers/orgs/org/groups/new-test.js +++ /dev/null @@ -1,12 +0,0 @@ -import { module, test } from 'qunit'; -import { setupTest } from 'ember-qunit'; - -module('Unit | Controller | orgs/org/groups/new', function (hooks) { - setupTest(hooks); - - // Replace this with your real tests. - test('it exists', function (assert) { - let controller = this.owner.lookup('controller:orgs/org/groups/new'); - assert.ok(controller); - }); -}); diff --git a/ui/core/tests/unit/controllers/orgs/org/projects-test.js b/ui/core/tests/unit/controllers/orgs/org/projects-test.js deleted file mode 100644 index c4d200e50..000000000 --- a/ui/core/tests/unit/controllers/orgs/org/projects-test.js +++ /dev/null @@ -1,12 +0,0 @@ -import { module, test } from 'qunit'; -import { setupTest } from 'ember-qunit'; - -module('Unit | Controller | orgs/org/projects', function (hooks) { - setupTest(hooks); - - // Replace this with your real tests. - test('it exists', function (assert) { - let controller = this.owner.lookup('controller:orgs/org/projects'); - assert.ok(controller); - }); -}); diff --git a/ui/core/tests/unit/controllers/orgs/org/projects/new-test.js b/ui/core/tests/unit/controllers/orgs/org/projects/new-test.js deleted file mode 100644 index 055c60a9f..000000000 --- a/ui/core/tests/unit/controllers/orgs/org/projects/new-test.js +++ /dev/null @@ -1,12 +0,0 @@ -import { module, test } from 'qunit'; -import { setupTest } from 'ember-qunit'; - -module('Unit | Controller | orgs/org/projects/new', function (hooks) { - setupTest(hooks); - - // Replace this with your real tests. - test('it exists', function (assert) { - let controller = this.owner.lookup('controller:orgs/org/projects/new'); - assert.ok(controller); - }); -}); diff --git a/ui/core/tests/unit/controllers/orgs/org/projects/project-test.js b/ui/core/tests/unit/controllers/orgs/org/projects/project-test.js deleted file mode 100644 index 067429ee5..000000000 --- a/ui/core/tests/unit/controllers/orgs/org/projects/project-test.js +++ /dev/null @@ -1,12 +0,0 @@ -import { module, test } from 'qunit'; -import { setupTest } from 'ember-qunit'; - -module('Unit | Controller | orgs/org/projects/project', function (hooks) { - setupTest(hooks); - - // Replace this with your real tests. - test('it exists', function (assert) { - let controller = this.owner.lookup('controller:orgs/org/projects/project'); - assert.ok(controller); - }); -}); diff --git a/ui/core/tests/unit/controllers/orgs/org/projects/project/host-catalogs-test.js b/ui/core/tests/unit/controllers/orgs/org/projects/project/host-catalogs-test.js deleted file mode 100644 index 6127a3173..000000000 --- a/ui/core/tests/unit/controllers/orgs/org/projects/project/host-catalogs-test.js +++ /dev/null @@ -1,16 +0,0 @@ -import { module, test } from 'qunit'; -import { setupTest } from 'ember-qunit'; - -module('Unit | Controller | orgs/org/projects/project/host-catalogs', function ( - hooks -) { - setupTest(hooks); - - // Replace this with your real tests. - test('it exists', function (assert) { - let controller = this.owner.lookup( - 'controller:orgs/org/projects/project/host-catalogs' - ); - assert.ok(controller); - }); -}); diff --git a/ui/core/tests/unit/controllers/orgs/org/projects/project/host-catalogs/host-catalog-test.js b/ui/core/tests/unit/controllers/orgs/org/projects/project/host-catalogs/host-catalog-test.js deleted file mode 100644 index 38b3a9c9d..000000000 --- a/ui/core/tests/unit/controllers/orgs/org/projects/project/host-catalogs/host-catalog-test.js +++ /dev/null @@ -1,17 +0,0 @@ -import { module, test } from 'qunit'; -import { setupTest } from 'ember-qunit'; - -module( - 'Unit | Controller | orgs/org/projects/project/host-catalogs/host-catalog', - function (hooks) { - setupTest(hooks); - - // Replace this with your real tests. - test('it exists', function (assert) { - let controller = this.owner.lookup( - 'controller:orgs/org/projects/project/host-catalogs/host-catalog' - ); - assert.ok(controller); - }); - } -); diff --git a/ui/core/tests/unit/controllers/orgs/org/projects/project/host-catalogs/new-test.js b/ui/core/tests/unit/controllers/orgs/org/projects/project/host-catalogs/new-test.js deleted file mode 100644 index ee0f326a1..000000000 --- a/ui/core/tests/unit/controllers/orgs/org/projects/project/host-catalogs/new-test.js +++ /dev/null @@ -1,17 +0,0 @@ -import { module, test } from 'qunit'; -import { setupTest } from 'ember-qunit'; - -module( - 'Unit | Controller | orgs/org/projects/project/host-catalogs/new', - function (hooks) { - setupTest(hooks); - - // Replace this with your real tests. - test('it exists', function (assert) { - let controller = this.owner.lookup( - 'controller:orgs/org/projects/project/host-catalogs/new' - ); - assert.ok(controller); - }); - } -); diff --git a/ui/core/tests/unit/controllers/orgs/org/roles-test.js b/ui/core/tests/unit/controllers/orgs/org/roles-test.js deleted file mode 100644 index 12c57a108..000000000 --- a/ui/core/tests/unit/controllers/orgs/org/roles-test.js +++ /dev/null @@ -1,11 +0,0 @@ -import { module, test } from 'qunit'; -import { setupTest } from 'ember-qunit'; - -module('Unit | Controller | orgs/org/roles', function (hooks) { - setupTest(hooks); - - test('it exists', function (assert) { - let controller = this.owner.lookup('controller:orgs/org/roles'); - assert.ok(controller); - }); -}); diff --git a/ui/core/tests/unit/controllers/orgs/org/roles/new-test.js b/ui/core/tests/unit/controllers/orgs/org/roles/new-test.js deleted file mode 100644 index c9e4cfcc8..000000000 --- a/ui/core/tests/unit/controllers/orgs/org/roles/new-test.js +++ /dev/null @@ -1,11 +0,0 @@ -import { module, test } from 'qunit'; -import { setupTest } from 'ember-qunit'; - -module('Unit | Controller | orgs/org/roles/new', function (hooks) { - setupTest(hooks); - - test('it exists', function (assert) { - let controller = this.owner.lookup('controller:orgs/org/roles/new'); - assert.ok(controller); - }); -}); diff --git a/ui/core/tests/unit/controllers/orgs/org/roles/role-test.js b/ui/core/tests/unit/controllers/orgs/org/roles/role-test.js deleted file mode 100644 index 515ae8420..000000000 --- a/ui/core/tests/unit/controllers/orgs/org/roles/role-test.js +++ /dev/null @@ -1,11 +0,0 @@ -import { module, test } from 'qunit'; -import { setupTest } from 'ember-qunit'; - -module('Unit | Controller | orgs/org/roles/role', function (hooks) { - setupTest(hooks); - - test('it exists', function (assert) { - let controller = this.owner.lookup('controller:orgs/org/roles/role'); - assert.ok(controller); - }); -}); diff --git a/ui/core/tests/unit/controllers/orgs/org/users-test.js b/ui/core/tests/unit/controllers/orgs/org/users-test.js deleted file mode 100644 index 5f0c57d8c..000000000 --- a/ui/core/tests/unit/controllers/orgs/org/users-test.js +++ /dev/null @@ -1,12 +0,0 @@ -import { module, test } from 'qunit'; -import { setupTest } from 'ember-qunit'; - -module('Unit | Controller | orgs/org/users', function (hooks) { - setupTest(hooks); - - // Replace this with your real tests. - test('it exists', function (assert) { - let controller = this.owner.lookup('controller:orgs/org/users'); - assert.ok(controller); - }); -}); diff --git a/ui/core/tests/unit/controllers/orgs/org/users/new-test.js b/ui/core/tests/unit/controllers/orgs/org/users/new-test.js deleted file mode 100644 index 80a7e1890..000000000 --- a/ui/core/tests/unit/controllers/orgs/org/users/new-test.js +++ /dev/null @@ -1,12 +0,0 @@ -import { module, test } from 'qunit'; -import { setupTest } from 'ember-qunit'; - -module('Unit | Controller | orgs/org/users/new', function (hooks) { - setupTest(hooks); - - // Replace this with your real tests. - test('it exists', function (assert) { - let controller = this.owner.lookup('controller:orgs/org/users/new'); - assert.ok(controller); - }); -}); diff --git a/ui/core/tests/unit/controllers/orgs/org/users/user-test.js b/ui/core/tests/unit/controllers/orgs/org/users/user-test.js deleted file mode 100644 index e16d4ab6b..000000000 --- a/ui/core/tests/unit/controllers/orgs/org/users/user-test.js +++ /dev/null @@ -1,12 +0,0 @@ -import { module, test } from 'qunit'; -import { setupTest } from 'ember-qunit'; - -module('Unit | Controller | orgs/org/users/user', function (hooks) { - setupTest(hooks); - - // Replace this with your real tests. - test('it exists', function (assert) { - let controller = this.owner.lookup('controller:orgs/org/users/user'); - assert.ok(controller); - }); -}); diff --git a/ui/core/tests/unit/controllers/scopes/scope/projects/project/host-catalogs/new-test.js b/ui/core/tests/unit/controllers/scopes/scope/projects/project/host-catalogs/new-test.js new file mode 100644 index 000000000..2f653e30b --- /dev/null +++ b/ui/core/tests/unit/controllers/scopes/scope/projects/project/host-catalogs/new-test.js @@ -0,0 +1,12 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Controller | scopes/scope/projects/project/host-catalogs/new', function(hooks) { + setupTest(hooks); + + // Replace this with your real tests. + test('it exists', function(assert) { + let controller = this.owner.lookup('controller:scopes/scope/projects/project/host-catalogs/new'); + assert.ok(controller); + }); +}); diff --git a/ui/core/tests/unit/routes/orgs-test.js b/ui/core/tests/unit/routes/orgs-test.js deleted file mode 100644 index 72888b83a..000000000 --- a/ui/core/tests/unit/routes/orgs-test.js +++ /dev/null @@ -1,11 +0,0 @@ -import { module, test } from 'qunit'; -import { setupTest } from 'ember-qunit'; - -module('Unit | Route | orgs', function (hooks) { - setupTest(hooks); - - test('it exists', function (assert) { - let route = this.owner.lookup('route:orgs'); - assert.ok(route); - }); -}); diff --git a/ui/core/tests/unit/routes/orgs/org-test.js b/ui/core/tests/unit/routes/orgs/org-test.js deleted file mode 100644 index 8f1eb4fe2..000000000 --- a/ui/core/tests/unit/routes/orgs/org-test.js +++ /dev/null @@ -1,11 +0,0 @@ -import { module, test } from 'qunit'; -import { setupTest } from 'ember-qunit'; - -module('Unit | Route | orgs/org', function (hooks) { - setupTest(hooks); - - test('it exists', function (assert) { - let route = this.owner.lookup('route:orgs/org'); - assert.ok(route); - }); -}); diff --git a/ui/core/tests/unit/routes/orgs/org/groups-test.js b/ui/core/tests/unit/routes/orgs/org/groups-test.js deleted file mode 100644 index 465afedcd..000000000 --- a/ui/core/tests/unit/routes/orgs/org/groups-test.js +++ /dev/null @@ -1,11 +0,0 @@ -import { module, test } from 'qunit'; -import { setupTest } from 'ember-qunit'; - -module('Unit | Route | orgs/org/groups', function (hooks) { - setupTest(hooks); - - test('it exists', function (assert) { - let route = this.owner.lookup('route:orgs/org/groups'); - assert.ok(route); - }); -}); diff --git a/ui/core/tests/unit/routes/orgs/org/groups/group-test.js b/ui/core/tests/unit/routes/orgs/org/groups/group-test.js deleted file mode 100644 index ca6107bc1..000000000 --- a/ui/core/tests/unit/routes/orgs/org/groups/group-test.js +++ /dev/null @@ -1,11 +0,0 @@ -import { module, test } from 'qunit'; -import { setupTest } from 'ember-qunit'; - -module('Unit | Route | orgs/org/groups/group', function (hooks) { - setupTest(hooks); - - test('it exists', function (assert) { - let route = this.owner.lookup('route:orgs/org/groups/group'); - assert.ok(route); - }); -}); diff --git a/ui/core/tests/unit/routes/orgs/org/groups/group/index-test.js b/ui/core/tests/unit/routes/orgs/org/groups/group/index-test.js deleted file mode 100644 index e0c5c06d4..000000000 --- a/ui/core/tests/unit/routes/orgs/org/groups/group/index-test.js +++ /dev/null @@ -1,11 +0,0 @@ -import { module, test } from 'qunit'; -import { setupTest } from 'ember-qunit'; - -module('Unit | Route | orgs/org/groups/group/index', function (hooks) { - setupTest(hooks); - - test('it exists', function (assert) { - let route = this.owner.lookup('route:orgs/org/groups/group/index'); - assert.ok(route); - }); -}); diff --git a/ui/core/tests/unit/routes/orgs/org/groups/index-test.js b/ui/core/tests/unit/routes/orgs/org/groups/index-test.js deleted file mode 100644 index 60c10cfef..000000000 --- a/ui/core/tests/unit/routes/orgs/org/groups/index-test.js +++ /dev/null @@ -1,11 +0,0 @@ -import { module, test } from 'qunit'; -import { setupTest } from 'ember-qunit'; - -module('Unit | Route | orgs/org/groups/index', function (hooks) { - setupTest(hooks); - - test('it exists', function (assert) { - let route = this.owner.lookup('route:orgs/org/groups/index'); - assert.ok(route); - }); -}); diff --git a/ui/core/tests/unit/routes/orgs/org/groups/new-test.js b/ui/core/tests/unit/routes/orgs/org/groups/new-test.js deleted file mode 100644 index e119c3d53..000000000 --- a/ui/core/tests/unit/routes/orgs/org/groups/new-test.js +++ /dev/null @@ -1,11 +0,0 @@ -import { module, test } from 'qunit'; -import { setupTest } from 'ember-qunit'; - -module('Unit | Route | orgs/org/groups/new', function (hooks) { - setupTest(hooks); - - test('it exists', function (assert) { - let route = this.owner.lookup('route:orgs/org/groups/new'); - assert.ok(route); - }); -}); diff --git a/ui/core/tests/unit/routes/orgs/org/projects-test.js b/ui/core/tests/unit/routes/orgs/org/projects-test.js deleted file mode 100644 index ce18ada89..000000000 --- a/ui/core/tests/unit/routes/orgs/org/projects-test.js +++ /dev/null @@ -1,11 +0,0 @@ -import { module, test } from 'qunit'; -import { setupTest } from 'ember-qunit'; - -module('Unit | Route | orgs/org/projects', function (hooks) { - setupTest(hooks); - - test('it exists', function (assert) { - let route = this.owner.lookup('route:orgs/org/projects'); - assert.ok(route); - }); -}); diff --git a/ui/core/tests/unit/routes/orgs/org/projects/index-test.js b/ui/core/tests/unit/routes/orgs/org/projects/index-test.js deleted file mode 100644 index 2bc8af956..000000000 --- a/ui/core/tests/unit/routes/orgs/org/projects/index-test.js +++ /dev/null @@ -1,11 +0,0 @@ -import { module, test } from 'qunit'; -import { setupTest } from 'ember-qunit'; - -module('Unit | Route | orgs/org/projects/index', function (hooks) { - setupTest(hooks); - - test('it exists', function (assert) { - let route = this.owner.lookup('route:orgs/org/projects/index'); - assert.ok(route); - }); -}); diff --git a/ui/core/tests/unit/routes/orgs/org/projects/new-test.js b/ui/core/tests/unit/routes/orgs/org/projects/new-test.js deleted file mode 100644 index f084da7bf..000000000 --- a/ui/core/tests/unit/routes/orgs/org/projects/new-test.js +++ /dev/null @@ -1,11 +0,0 @@ -import { module, test } from 'qunit'; -import { setupTest } from 'ember-qunit'; - -module('Unit | Route | orgs/org/projects/new', function (hooks) { - setupTest(hooks); - - test('it exists', function (assert) { - let route = this.owner.lookup('route:orgs/org/projects/new'); - assert.ok(route); - }); -}); diff --git a/ui/core/tests/unit/routes/orgs/org/projects/project-test.js b/ui/core/tests/unit/routes/orgs/org/projects/project-test.js deleted file mode 100644 index a1e01801f..000000000 --- a/ui/core/tests/unit/routes/orgs/org/projects/project-test.js +++ /dev/null @@ -1,11 +0,0 @@ -import { module, test } from 'qunit'; -import { setupTest } from 'ember-qunit'; - -module('Unit | Route | orgs/org/projects/project', function (hooks) { - setupTest(hooks); - - test('it exists', function (assert) { - let route = this.owner.lookup('route:orgs/org/projects/project'); - assert.ok(route); - }); -}); diff --git a/ui/core/tests/unit/routes/orgs/org/projects/project/host-catalogs-test.js b/ui/core/tests/unit/routes/orgs/org/projects/project/host-catalogs-test.js deleted file mode 100644 index 761c0bfa8..000000000 --- a/ui/core/tests/unit/routes/orgs/org/projects/project/host-catalogs-test.js +++ /dev/null @@ -1,15 +0,0 @@ -import { module, test } from 'qunit'; -import { setupTest } from 'ember-qunit'; - -module('Unit | Route | orgs/org/projects/project/host-catalogs', function ( - hooks -) { - setupTest(hooks); - - test('it exists', function (assert) { - let route = this.owner.lookup( - 'route:orgs/org/projects/project/host-catalogs' - ); - assert.ok(route); - }); -}); diff --git a/ui/core/tests/unit/routes/orgs/org/projects/project/host-catalogs/host-catalog-test.js b/ui/core/tests/unit/routes/orgs/org/projects/project/host-catalogs/host-catalog-test.js deleted file mode 100644 index 5dd167f8a..000000000 --- a/ui/core/tests/unit/routes/orgs/org/projects/project/host-catalogs/host-catalog-test.js +++ /dev/null @@ -1,16 +0,0 @@ -import { module, test } from 'qunit'; -import { setupTest } from 'ember-qunit'; - -module( - 'Unit | Route | orgs/org/projects/project/host-catalogs/host-catalog', - function (hooks) { - setupTest(hooks); - - test('it exists', function (assert) { - let route = this.owner.lookup( - 'route:orgs/org/projects/project/host-catalogs/host-catalog' - ); - assert.ok(route); - }); - } -); diff --git a/ui/core/tests/unit/routes/orgs/org/projects/project/host-catalogs/host-catalog/index-test.js b/ui/core/tests/unit/routes/orgs/org/projects/project/host-catalogs/host-catalog/index-test.js deleted file mode 100644 index 1472a5b92..000000000 --- a/ui/core/tests/unit/routes/orgs/org/projects/project/host-catalogs/host-catalog/index-test.js +++ /dev/null @@ -1,16 +0,0 @@ -import { module, test } from 'qunit'; -import { setupTest } from 'ember-qunit'; - -module( - 'Unit | Route | orgs/org/projects/project/host-catalogs/host-catalog/index', - function (hooks) { - setupTest(hooks); - - test('it exists', function (assert) { - let route = this.owner.lookup( - 'route:orgs/org/projects/project/host-catalogs/host-catalog/index' - ); - assert.ok(route); - }); - } -); diff --git a/ui/core/tests/unit/routes/orgs/org/projects/project/host-catalogs/index-test.js b/ui/core/tests/unit/routes/orgs/org/projects/project/host-catalogs/index-test.js deleted file mode 100644 index 0d7a5e317..000000000 --- a/ui/core/tests/unit/routes/orgs/org/projects/project/host-catalogs/index-test.js +++ /dev/null @@ -1,16 +0,0 @@ -import { module, test } from 'qunit'; -import { setupTest } from 'ember-qunit'; - -module( - 'Unit | Route | orgs/org/projects/project/host-catalogs/index', - function (hooks) { - setupTest(hooks); - - test('it exists', function (assert) { - let route = this.owner.lookup( - 'route:orgs/org/projects/project/host-catalogs/index' - ); - assert.ok(route); - }); - } -); diff --git a/ui/core/tests/unit/routes/orgs/org/projects/project/host-catalogs/new-test.js b/ui/core/tests/unit/routes/orgs/org/projects/project/host-catalogs/new-test.js deleted file mode 100644 index 8b441cfd6..000000000 --- a/ui/core/tests/unit/routes/orgs/org/projects/project/host-catalogs/new-test.js +++ /dev/null @@ -1,15 +0,0 @@ -import { module, test } from 'qunit'; -import { setupTest } from 'ember-qunit'; - -module('Unit | Route | orgs/org/projects/project/host-catalogs/new', function ( - hooks -) { - setupTest(hooks); - - test('it exists', function (assert) { - let route = this.owner.lookup( - 'route:orgs/org/projects/project/host-catalogs/new' - ); - assert.ok(route); - }); -}); diff --git a/ui/core/tests/unit/routes/orgs/org/projects/project/index-test.js b/ui/core/tests/unit/routes/orgs/org/projects/project/index-test.js deleted file mode 100644 index 08f446f6f..000000000 --- a/ui/core/tests/unit/routes/orgs/org/projects/project/index-test.js +++ /dev/null @@ -1,11 +0,0 @@ -import { module, test } from 'qunit'; -import { setupTest } from 'ember-qunit'; - -module('Unit | Route | orgs/org/projects/project/index', function (hooks) { - setupTest(hooks); - - test('it exists', function (assert) { - let route = this.owner.lookup('route:orgs/org/projects/project/index'); - assert.ok(route); - }); -}); diff --git a/ui/core/tests/unit/routes/orgs/org/roles-test.js b/ui/core/tests/unit/routes/orgs/org/roles-test.js deleted file mode 100644 index 7cd35dab6..000000000 --- a/ui/core/tests/unit/routes/orgs/org/roles-test.js +++ /dev/null @@ -1,11 +0,0 @@ -import { module, test } from 'qunit'; -import { setupTest } from 'ember-qunit'; - -module('Unit | Route | orgs/org/roles', function (hooks) { - setupTest(hooks); - - test('it exists', function (assert) { - let route = this.owner.lookup('route:orgs/org/roles'); - assert.ok(route); - }); -}); diff --git a/ui/core/tests/unit/routes/orgs/org/roles/index-test.js b/ui/core/tests/unit/routes/orgs/org/roles/index-test.js deleted file mode 100644 index 979d26062..000000000 --- a/ui/core/tests/unit/routes/orgs/org/roles/index-test.js +++ /dev/null @@ -1,11 +0,0 @@ -import { module, test } from 'qunit'; -import { setupTest } from 'ember-qunit'; - -module('Unit | Route | orgs/org/roles/index', function (hooks) { - setupTest(hooks); - - test('it exists', function (assert) { - let route = this.owner.lookup('route:orgs/org/roles/index'); - assert.ok(route); - }); -}); diff --git a/ui/core/tests/unit/routes/orgs/org/roles/new-test.js b/ui/core/tests/unit/routes/orgs/org/roles/new-test.js deleted file mode 100644 index 59e26a949..000000000 --- a/ui/core/tests/unit/routes/orgs/org/roles/new-test.js +++ /dev/null @@ -1,11 +0,0 @@ -import { module, test } from 'qunit'; -import { setupTest } from 'ember-qunit'; - -module('Unit | Route | orgs/org/roles/new', function (hooks) { - setupTest(hooks); - - test('it exists', function (assert) { - let route = this.owner.lookup('route:orgs/org/roles/new'); - assert.ok(route); - }); -}); diff --git a/ui/core/tests/unit/routes/orgs/org/roles/role-test.js b/ui/core/tests/unit/routes/orgs/org/roles/role-test.js deleted file mode 100644 index 207a79ea2..000000000 --- a/ui/core/tests/unit/routes/orgs/org/roles/role-test.js +++ /dev/null @@ -1,11 +0,0 @@ -import { module, test } from 'qunit'; -import { setupTest } from 'ember-qunit'; - -module('Unit | Route | orgs/org/roles/role', function (hooks) { - setupTest(hooks); - - test('it exists', function (assert) { - let route = this.owner.lookup('route:orgs/org/roles/role'); - assert.ok(route); - }); -}); diff --git a/ui/core/tests/unit/routes/orgs/org/users-test.js b/ui/core/tests/unit/routes/orgs/org/users-test.js deleted file mode 100644 index b4c420690..000000000 --- a/ui/core/tests/unit/routes/orgs/org/users-test.js +++ /dev/null @@ -1,11 +0,0 @@ -import { module, test } from 'qunit'; -import { setupTest } from 'ember-qunit'; - -module('Unit | Route | orgs/org/users', function (hooks) { - setupTest(hooks); - - test('it exists', function (assert) { - let route = this.owner.lookup('route:orgs/org/users'); - assert.ok(route); - }); -}); diff --git a/ui/core/tests/unit/routes/orgs/org/users/index-test.js b/ui/core/tests/unit/routes/orgs/org/users/index-test.js deleted file mode 100644 index 21310f7a1..000000000 --- a/ui/core/tests/unit/routes/orgs/org/users/index-test.js +++ /dev/null @@ -1,11 +0,0 @@ -import { module, test } from 'qunit'; -import { setupTest } from 'ember-qunit'; - -module('Unit | Route | orgs/org/users/index', function (hooks) { - setupTest(hooks); - - test('it exists', function (assert) { - let route = this.owner.lookup('route:orgs/org/users/index'); - assert.ok(route); - }); -}); diff --git a/ui/core/tests/unit/routes/orgs/org/users/new-test.js b/ui/core/tests/unit/routes/orgs/org/users/new-test.js deleted file mode 100644 index 395979cad..000000000 --- a/ui/core/tests/unit/routes/orgs/org/users/new-test.js +++ /dev/null @@ -1,11 +0,0 @@ -import { module, test } from 'qunit'; -import { setupTest } from 'ember-qunit'; - -module('Unit | Route | orgs/org/users/new', function (hooks) { - setupTest(hooks); - - test('it exists', function (assert) { - let route = this.owner.lookup('route:orgs/org/users/new'); - assert.ok(route); - }); -}); diff --git a/ui/core/tests/unit/routes/orgs/org/users/user-test.js b/ui/core/tests/unit/routes/orgs/org/users/user-test.js deleted file mode 100644 index 48a657875..000000000 --- a/ui/core/tests/unit/routes/orgs/org/users/user-test.js +++ /dev/null @@ -1,11 +0,0 @@ -import { module, test } from 'qunit'; -import { setupTest } from 'ember-qunit'; - -module('Unit | Route | orgs/org/users/user', function (hooks) { - setupTest(hooks); - - test('it exists', function (assert) { - let route = this.owner.lookup('route:orgs/org/users/user'); - assert.ok(route); - }); -}); From 4d3b09d4602f819b2dc636f032128b4d2712206d Mon Sep 17 00:00:00 2001 From: Susmitha Girumala Date: Thu, 30 Jul 2020 13:16:44 -0400 Subject: [PATCH 69/70] =?UTF-8?q?test:=20=F0=9F=92=8D=20Fix=20breadcrumb?= =?UTF-8?q?=20tests=20after=20scopes=20refactor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add scoping information to breadcrumbs acceptance and integration tests --- ui/core/tests/acceptance/bread-crumbs-test.js | 8 ++++++-- .../tests/integration/components/bread-crumbs-test.js | 10 +++++----- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/ui/core/tests/acceptance/bread-crumbs-test.js b/ui/core/tests/acceptance/bread-crumbs-test.js index 68946549a..879eb311b 100644 --- a/ui/core/tests/acceptance/bread-crumbs-test.js +++ b/ui/core/tests/acceptance/bread-crumbs-test.js @@ -45,6 +45,12 @@ module('Acceptance | breadcrumbs', function (hooks) { user = this.server.create('user'); role = this.server.create('role'); group = this.server.create('group'); + hostCatalog = this.server.create('host-catalog', { + scope: { + id: project.id, + type: project.type + } + }); projectsURL = `/scopes/${orgScope.id}/projects`; newProjectURL = `${projectsURL}/new`; @@ -62,8 +68,6 @@ module('Acceptance | breadcrumbs', function (hooks) { newUserURL = `${usersURL}/${user.id}`; userURL = `${usersURL}/new`; - // TODO: Update hostcatalog to scopes - hostCatalog = this.server.create('host-catalog'); hostCatalogsURL = `${projectURL}/host-catalogs`; newHostCatalogURL = `${hostCatalogsURL}/new`; hostCatalogURL = `${hostCatalogsURL}/${hostCatalog.id}`; diff --git a/ui/core/tests/integration/components/bread-crumbs-test.js b/ui/core/tests/integration/components/bread-crumbs-test.js index a84f99f55..601741311 100644 --- a/ui/core/tests/integration/components/bread-crumbs-test.js +++ b/ui/core/tests/integration/components/bread-crumbs-test.js @@ -17,18 +17,18 @@ module('Integration | Component | bread-crumbs', function (hooks) { test('it renders with model', async function (assert) { let store = this.owner.lookup('service:store'); - let project = store.createRecord('project', { id: '1234' }); + let scope = store.createRecord('scope'); this.set('breadCrumbs', [ { - label: 'Level 2', - path: 'scopes.scope.projects.project', - model: project, + label: 'Level 1', + path: 'scopes.scope', + model: scope, }, ]); await render(hbs``); - assert.equal(find('.rose-nav-breadcrumbs').textContent.trim(), 'Level 2'); + assert.equal(find('.rose-nav-breadcrumbs').textContent.trim(), 'Level 1'); }); test('it renders multiple breadCrumbs', async function (assert) { From 4abbedc763693cd8d1b66930721b78086aa8bbf9 Mon Sep 17 00:00:00 2001 From: Susmitha Girumala Date: Thu, 30 Jul 2020 13:17:34 -0400 Subject: [PATCH 70/70] =?UTF-8?q?chore:=20=F0=9F=A4=96=20Add=20missing=20a?= =?UTF-8?q?uthentication-failed=20i18n=20string?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ui/core/translations/en-us.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/ui/core/translations/en-us.yaml b/ui/core/translations/en-us.yaml index c07cdee37..e563eb166 100644 --- a/ui/core/translations/en-us.yaml +++ b/ui/core/translations/en-us.yaml @@ -72,6 +72,7 @@ errors: notfound-error: Resource not found server-error: Server error unknown-error: Something went wrong + authentication-failed: Authentication Failed descriptions: unauthenticated-error: You are not authenticated. Please authenticate and try again later. forbidden-error: You must be granted permissions to view this data. Ask your administrator if you think you should have access.