From 2b59aeee4b7a22728829facfd8a87a70371a3476 Mon Sep 17 00:00:00 2001 From: Susmitha Girumala Date: Mon, 22 Jun 2020 21:39:38 -0400 Subject: [PATCH] =?UTF-8?q?chore:=20=F0=9F=A4=96=20Add=20role=20CRUD=20for?= =?UTF-8?q?ms=20to=20admin=20ui?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- addons/api/addon/adapters/role.js | 14 ++ addons/api/app/adapters/role.js | 1 + addons/api/tests/unit/adapters/role-test.js | 16 ++ ui/core/app/components/form/role/index.hbs | 49 +++++ ui/core/app/controllers/orgs/org/roles.js | 18 ++ ui/core/app/controllers/orgs/org/roles/new.js | 18 ++ .../app/controllers/orgs/org/roles/role.js | 18 ++ ui/core/app/router.js | 4 + ui/core/app/routes/orgs/org/roles.js | 32 +++ 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/templates/orgs/org/roles.hbs | 3 + .../app/templates/orgs/org/roles/_sidebar.hbs | 18 ++ .../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/mirage/config.js | 7 + ui/core/mirage/factories/role.js | 3 + ui/core/mirage/generated/factories/role.js | 16 ++ ui/core/mirage/scenarios/default.js | 4 + ui/core/mirage/serializers/role.js | 5 + ui/core/tests/acceptance/roles-test.js | 191 ++++++++++++++++++ .../unit/controllers/orgs/org/roles-test.js | 11 + .../controllers/orgs/org/roles/new-test.js | 11 + .../controllers/orgs/org/roles/role-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 + ui/core/translations/en-us.yaml | 11 + 32 files changed, 732 insertions(+) create mode 100644 addons/api/addon/adapters/role.js create mode 100644 addons/api/app/adapters/role.js create mode 100644 addons/api/tests/unit/adapters/role-test.js create mode 100644 ui/core/app/components/form/role/index.hbs create mode 100644 ui/core/app/controllers/orgs/org/roles.js create mode 100644 ui/core/app/controllers/orgs/org/roles/new.js create mode 100644 ui/core/app/controllers/orgs/org/roles/role.js create mode 100644 ui/core/app/routes/orgs/org/roles.js create mode 100644 ui/core/app/routes/orgs/org/roles/index.js create mode 100644 ui/core/app/routes/orgs/org/roles/new.js create mode 100644 ui/core/app/routes/orgs/org/roles/role.js create mode 100644 ui/core/app/templates/orgs/org/roles.hbs create mode 100644 ui/core/app/templates/orgs/org/roles/_sidebar.hbs create mode 100644 ui/core/app/templates/orgs/org/roles/index.hbs create mode 100644 ui/core/app/templates/orgs/org/roles/new.hbs create mode 100644 ui/core/app/templates/orgs/org/roles/role.hbs create mode 100644 ui/core/app/templates/orgs/org/roles/role/index.hbs create mode 100644 ui/core/mirage/factories/role.js create mode 100644 ui/core/mirage/generated/factories/role.js create mode 100644 ui/core/mirage/serializers/role.js create mode 100644 ui/core/tests/acceptance/roles-test.js create mode 100644 ui/core/tests/unit/controllers/orgs/org/roles-test.js create mode 100644 ui/core/tests/unit/controllers/orgs/org/roles/new-test.js create mode 100644 ui/core/tests/unit/controllers/orgs/org/roles/role-test.js create mode 100644 ui/core/tests/unit/routes/orgs/org/roles-test.js create mode 100644 ui/core/tests/unit/routes/orgs/org/roles/index-test.js create mode 100644 ui/core/tests/unit/routes/orgs/org/roles/new-test.js create mode 100644 ui/core/tests/unit/routes/orgs/org/roles/role-test.js diff --git a/addons/api/addon/adapters/role.js b/addons/api/addon/adapters/role.js new file mode 100644 index 000000000..c9b0c13b0 --- /dev/null +++ b/addons/api/addon/adapters/role.js @@ -0,0 +1,14 @@ +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/app/adapters/role.js b/addons/api/app/adapters/role.js new file mode 100644 index 000000000..f12325923 --- /dev/null +++ b/addons/api/app/adapters/role.js @@ -0,0 +1 @@ +export { default } from 'api/adapters/role'; diff --git a/addons/api/tests/unit/adapters/role-test.js b/addons/api/tests/unit/adapters/role-test.js new file mode 100644 index 000000000..adb7b1abd --- /dev/null +++ b/addons/api/tests/unit/adapters/role-test.js @@ -0,0 +1,16 @@ +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/ui/core/app/components/form/role/index.hbs b/ui/core/app/components/form/role/index.hbs new file mode 100644 index 000000000..9db695baa --- /dev/null +++ b/ui/core/app/components/form/role/index.hbs @@ -0,0 +1,49 @@ + + + + {{#if @model.errors.name}} + + {{#each @model.errors.name as |error|}} + {{error.message}} + {{/each}} + + {{/if}} + + + + {{#if @model.errors.description}} + + {{#each @model.errors.description as |error|}} + {{error.message}} + {{/each}} + + {{/if}} + + + + + diff --git a/ui/core/app/controllers/orgs/org/roles.js b/ui/core/app/controllers/orgs/org/roles.js new file mode 100644 index 000000000..7f62b0e1a --- /dev/null +++ b/ui/core/app/controllers/orgs/org/roles.js @@ -0,0 +1,18 @@ +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 new file mode 100644 index 000000000..38a098eaa --- /dev/null +++ b/ui/core/app/controllers/orgs/org/roles/new.js @@ -0,0 +1,18 @@ +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 new file mode 100644 index 000000000..7a5564ac0 --- /dev/null +++ b/ui/core/app/controllers/orgs/org/roles/role.js @@ -0,0 +1,18 @@ +import Controller from '@ember/controller'; +import { inject as service } from '@ember/service'; + +export default class OrgsOrgRolesRoleController extends Controller { + // =services + + @service intl; + + // =attributes + + /** + * @type {string} + */ + get breadCrumb() { + return this.intl.t('resources.role'); + } + +} diff --git a/ui/core/app/router.js b/ui/core/app/router.js index 76af92941..b9360c541 100644 --- a/ui/core/app/router.js +++ b/ui/core/app/router.js @@ -18,6 +18,10 @@ Router.map(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/orgs/org/roles.js b/ui/core/app/routes/orgs/org/roles.js new file mode 100644 index 000000000..9c804bf0b --- /dev/null +++ b/ui/core/app/routes/orgs/org/roles.js @@ -0,0 +1,32 @@ +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').catch(() => { + // FIX ME: API response of "{}" generates following error: + // Error while processing route: orgs.org.roles.index + // Error: Assertion Failed: You made a 'findAll' request for 'role' records, but the adapter's response did not have any data + }); + } + + /** + * Renders the roles-specific sidebar template. + * @override + * @param {object} controller + * @param {object} model + */ + renderTemplate(controller, model) { + this.render('orgs/org/roles/_sidebar', { + into: 'application', + outlet: 'sidebar', + model: model + }); + super.renderTemplate(...arguments); + } +} diff --git a/ui/core/app/routes/orgs/org/roles/index.js b/ui/core/app/routes/orgs/org/roles/index.js new file mode 100644 index 000000000..89b868927 --- /dev/null +++ b/ui/core/app/routes/orgs/org/roles/index.js @@ -0,0 +1,3 @@ +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 new file mode 100644 index 000000000..cad253804 --- /dev/null +++ b/ui/core/app/routes/orgs/org/roles/new.js @@ -0,0 +1,50 @@ +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 new file mode 100644 index 000000000..1820f2ba1 --- /dev/null +++ b/ui/core/app/routes/orgs/org/roles/role.js @@ -0,0 +1,64 @@ +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/templates/orgs/org/roles.hbs b/ui/core/app/templates/orgs/org/roles.hbs new file mode 100644 index 000000000..6d511b7f7 --- /dev/null +++ b/ui/core/app/templates/orgs/org/roles.hbs @@ -0,0 +1,3 @@ +{{title (t "resources.roles")}} + +{{outlet}} diff --git a/ui/core/app/templates/orgs/org/roles/_sidebar.hbs b/ui/core/app/templates/orgs/org/roles/_sidebar.hbs new file mode 100644 index 000000000..584ba2ccc --- /dev/null +++ b/ui/core/app/templates/orgs/org/roles/_sidebar.hbs @@ -0,0 +1,18 @@ + + + + {{t "resources.projects"}} + + + + + + + {{t "resources.roles"}} + + + diff --git a/ui/core/app/templates/orgs/org/roles/index.hbs b/ui/core/app/templates/orgs/org/roles/index.hbs new file mode 100644 index 000000000..498aecac5 --- /dev/null +++ b/ui/core/app/templates/orgs/org/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.name}} + + + {{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 new file mode 100644 index 000000000..911310f26 --- /dev/null +++ b/ui/core/app/templates/orgs/org/roles/new.hbs @@ -0,0 +1,17 @@ + + + + + + +

{{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 new file mode 100644 index 000000000..2b05f838d --- /dev/null +++ b/ui/core/app/templates/orgs/org/roles/role.hbs @@ -0,0 +1,3 @@ +{{title @model.name}} + +{{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 new file mode 100644 index 000000000..1b35c1929 --- /dev/null +++ b/ui/core/app/templates/orgs/org/roles/role/index.hbs @@ -0,0 +1,27 @@ + + + + + + + +

{{t "resources.role"}}

+
+ + + + + + {{t 'actions.delete'}} + + + +
diff --git a/ui/core/mirage/config.js b/ui/core/mirage/config.js index 247fc6f36..8a3e0f49c 100644 --- a/ui/core/mirage/config.js +++ b/ui/core/mirage/config.js @@ -40,6 +40,13 @@ export default function() { this.patch('/orgs/:org_id/auth-methods/:id'); this.del('/orgs/:org_id/auth-methods/:id'); + // role + 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'); + // Other resources // host-catalog diff --git a/ui/core/mirage/factories/role.js b/ui/core/mirage/factories/role.js new file mode 100644 index 000000000..abcb8de67 --- /dev/null +++ b/ui/core/mirage/factories/role.js @@ -0,0 +1,3 @@ +import factory from '../generated/factories/role'; + +export default factory.extend(); diff --git a/ui/core/mirage/generated/factories/role.js b/ui/core/mirage/generated/factories/role.js new file mode 100644 index 000000000..6265c7116 --- /dev/null +++ b/ui/core/mirage/generated/factories/role.js @@ -0,0 +1,16 @@ +import { Factory } from 'ember-cli-mirage'; +import { random, date } from 'faker'; + +/** + * GeneratedRoleModelFactory + * Role contains all field related to a Role resource + */ +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 b8accbf8b..ef06c6ee4 100644 --- a/ui/core/mirage/scenarios/default.js +++ b/ui/core/mirage/scenarios/default.js @@ -12,6 +12,10 @@ export default function(server) { // same items for request at all scopes. server.createList('auth-method', 3); + // IAM + // Roles + server.createList('role', 5); + // Other resources server.createList('host-catalog', 6); diff --git a/ui/core/mirage/serializers/role.js b/ui/core/mirage/serializers/role.js new file mode 100644 index 000000000..c0db8a130 --- /dev/null +++ b/ui/core/mirage/serializers/role.js @@ -0,0 +1,5 @@ +import ApplicationSerializer from './application'; + +export default ApplicationSerializer.extend({ + modelName: 'role' +}); diff --git a/ui/core/tests/acceptance/roles-test.js b/ui/core/tests/acceptance/roles-test.js new file mode 100644 index 000000000..976ba47b7 --- /dev/null +++ b/ui/core/tests/acceptance/roles-test.js @@ -0,0 +1,191 @@ +import { module, test } from 'qunit'; +import { visit, currentURL, click, fillIn, 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'; + +module('Acceptance | roles', function (hooks) { + setupApplicationTest(hooks); + setupMirage(hooks); + + hooks.beforeEach(function () { + this.server.create('org'); + }); + + test('visiting roles', async function (assert) { + assert.expect(1); + await visit('/orgs/1/roles'); + await a11yAudit(); + assert.equal(currentURL(), '/orgs/1/roles'); + }); + + 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 fillIn('[name="name"]', 'role name'); + await click('[type="submit"]'); + assert.equal(currentURL(), '/orgs/1/roles/1'); + assert.equal(this.server.db.roles.length, 1); + assert.equal(this.server.db.roles[0].name, 'role name'); + }); + + 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 fillIn('[name="name"]', 'role name'); + await click('.rose-form-actions [type="button"]'); + assert.equal(currentURL(), '/orgs/1/roles'); + 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', () => { + 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/roles/new'); + await fillIn('[name="name"]', 'role name'); + await click('[type="submit"]'); + assert.ok( + find('[role="alert"]').textContent.trim(), + 'The request was invalid.', + 'Displays primary error message.' + ); + assert.ok( + find('.rose-form-error-message').textContent.trim(), + 'Name is required.', + 'Displays field-level errors.' + ); + }); + + 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 fillIn('[name="name"]', 'Updated admin role'); + await click('.rose-form-actions [type="submit"]'); + assert.equal(currentURL(), '/orgs/1/roles/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 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) { + assert.expect(2); + this.server.createList('role', 1); + assert.equal(this.server.db.roles.length, 1); + await visit('/orgs/1/roles/1'); + await click('.rose-button-warning'); + assert.equal(this.server.db.roles.length, 0); + }); + + 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', () => { + 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/roles/1'); + await fillIn('[name="name"]', 'random string'); + await click('[type="submit"]'); + assert.ok( + find('[role="alert"]').textContent.trim(), + 'The request was invalid.', + 'Displays primary error message.' + ); + assert.ok( + find('.rose-form-error-message').textContent.trim(), + 'Name is required.', + 'Displays field-level errors.' + ); + }); + + 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', () => { + return new Response( + 490, + {}, + { + status: 490, + code: 'error', + message: 'Oops.', + } + ); + }); + await visit('/orgs/1/roles/1'); + await fillIn('[name="name"]', 'Role name'); + await click('[type="submit"]'); + assert.ok( + find('[role="alert"]').textContent.trim(), + 'Oops.', + 'Displays primary error message.' + ); + }); + + 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', () => { + return new Response( + 490, + {}, + { + status: 490, + code: 'error', + message: 'Oops.', + } + ); + }); + await visit('/orgs/1/roles/1'); + await click('.rose-button-warning'); + assert.ok( + find('[role="alert"]').textContent.trim(), + 'Oops.', + 'Displays primary error message.' + ); + }); +}); diff --git a/ui/core/tests/unit/controllers/orgs/org/roles-test.js b/ui/core/tests/unit/controllers/orgs/org/roles-test.js new file mode 100644 index 000000000..816459cd3 --- /dev/null +++ b/ui/core/tests/unit/controllers/orgs/org/roles-test.js @@ -0,0 +1,11 @@ +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 new file mode 100644 index 000000000..8d948ae06 --- /dev/null +++ b/ui/core/tests/unit/controllers/orgs/org/roles/new-test.js @@ -0,0 +1,11 @@ +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 new file mode 100644 index 000000000..cd56c941f --- /dev/null +++ b/ui/core/tests/unit/controllers/orgs/org/roles/role-test.js @@ -0,0 +1,11 @@ +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/routes/orgs/org/roles-test.js b/ui/core/tests/unit/routes/orgs/org/roles-test.js new file mode 100644 index 000000000..38bc3acb2 --- /dev/null +++ b/ui/core/tests/unit/routes/orgs/org/roles-test.js @@ -0,0 +1,11 @@ +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 new file mode 100644 index 000000000..12f247adb --- /dev/null +++ b/ui/core/tests/unit/routes/orgs/org/roles/index-test.js @@ -0,0 +1,11 @@ +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 new file mode 100644 index 000000000..d613eb34a --- /dev/null +++ b/ui/core/tests/unit/routes/orgs/org/roles/new-test.js @@ -0,0 +1,11 @@ +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 new file mode 100644 index 000000000..149fd657e --- /dev/null +++ b/ui/core/tests/unit/routes/orgs/org/roles/role-test.js @@ -0,0 +1,11 @@ +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/translations/en-us.yaml b/ui/core/translations/en-us.yaml index 29ba0c47f..d8151b25b 100644 --- a/ui/core/translations/en-us.yaml +++ b/ui/core/translations/en-us.yaml @@ -5,22 +5,28 @@ actions: save: Save help: Need help? resources: + role: Role + roles: Roles projects: Projects project: Project host-catalog: Host Catalog host-catalogs: Host Catalogs titles: + iam: IAM new: New + general: General overview: Overview settings: Settings empty-set: No Items Yet projects-welcome: Welcome to Projects host-catalogs-welcome: Welcome to Host Catalogs + roles-welcome: Welcome to Roles descriptions: host-catalogs: Overview of collections of hosts and host sets. empty-set: There are no items to display yet. You may be able to add items or try back later. projects-welcome: Projects allow you to create and manage host catalogs. To start, create a project. host-catalogs-welcome: Host catalogs allow you to configure to collect and create hosts and host sets. To start, create a host catalog. + roles-welcome: Roles are collections of capabilities that can be granted to user and/or groups. states: info: Info success: Success @@ -44,6 +50,11 @@ errors: notfound-error: We could not find the requested resource. You can ask your administrator or try again later. 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. + role: + create-success: Role created succesfully. + delete-success: Deleted role successfully. dev: default-org: Default Org menu-item: Menu item