From d7967446d468a9622b947075fcffb4e84c3da9a6 Mon Sep 17 00:00:00 2001 From: Susmitha Girumala Date: Tue, 23 Jun 2020 14:53:07 -0400 Subject: [PATCH 1/9] Add user api adapter --- addons/api/addon/adapters/user.js | 14 ++++++++++++++ addons/api/app/adapters/user.js | 1 + addons/api/tests/unit/adapters/user-test.js | 11 +++++++++++ 3 files changed, 26 insertions(+) create mode 100644 addons/api/addon/adapters/user.js create mode 100644 addons/api/app/adapters/user.js create mode 100644 addons/api/tests/unit/adapters/user-test.js diff --git a/addons/api/addon/adapters/user.js b/addons/api/addon/adapters/user.js new file mode 100644 index 000000000..b32743c79 --- /dev/null +++ b/addons/api/addon/adapters/user.js @@ -0,0 +1,14 @@ +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/app/adapters/user.js b/addons/api/app/adapters/user.js new file mode 100644 index 000000000..78f340b99 --- /dev/null +++ b/addons/api/app/adapters/user.js @@ -0,0 +1 @@ +export { default } from 'api/adapters/user'; diff --git a/addons/api/tests/unit/adapters/user-test.js b/addons/api/tests/unit/adapters/user-test.js new file mode 100644 index 000000000..04c42f929 --- /dev/null +++ b/addons/api/tests/unit/adapters/user-test.js @@ -0,0 +1,11 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Adapter | user', function(hooks) { + setupTest(hooks); + + test('it exists', function(assert) { + let adapter = this.owner.lookup('adapter:user'); + assert.ok(adapter); + }); +}); From 3c37d38c9706e95080e6792e5755894e40418fc8 Mon Sep 17 00:00:00 2001 From: Susmitha Girumala Date: Tue, 23 Jun 2020 14:56:13 -0400 Subject: [PATCH 2/9] Add user mirage factory --- ui/core/mirage/factories/user.js | 3 +++ ui/core/mirage/generated/factories/user.js | 16 ++++++++++++++++ ui/core/mirage/scenarios/default.js | 3 +++ ui/core/mirage/serializers/user.js | 5 +++++ 4 files changed, 27 insertions(+) create mode 100644 ui/core/mirage/factories/user.js create mode 100644 ui/core/mirage/generated/factories/user.js create mode 100644 ui/core/mirage/serializers/user.js diff --git a/ui/core/mirage/factories/user.js b/ui/core/mirage/factories/user.js new file mode 100644 index 000000000..6c0997f8f --- /dev/null +++ b/ui/core/mirage/factories/user.js @@ -0,0 +1,3 @@ +import { Factory } from 'ember-cli-mirage'; + +export default Factory.extend(); diff --git a/ui/core/mirage/generated/factories/user.js b/ui/core/mirage/generated/factories/user.js new file mode 100644 index 000000000..3934bc7e9 --- /dev/null +++ b/ui/core/mirage/generated/factories/user.js @@ -0,0 +1,16 @@ +import { Factory } from 'ember-cli-mirage'; +import { random, date } from 'faker'; + +/** + * GeneratedUserModelFactory + * User contains all fields related to a User 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..ad7dfe2f0 100644 --- a/ui/core/mirage/scenarios/default.js +++ b/ui/core/mirage/scenarios/default.js @@ -12,6 +12,9 @@ export default function(server) { // same items for request at all scopes. server.createList('auth-method', 3); + // User + server.createList('user', 5); + // Other resources server.createList('host-catalog', 6); diff --git a/ui/core/mirage/serializers/user.js b/ui/core/mirage/serializers/user.js new file mode 100644 index 000000000..142cbf508 --- /dev/null +++ b/ui/core/mirage/serializers/user.js @@ -0,0 +1,5 @@ +import ApplicationSerializer from './application'; + +export default ApplicationSerializer.extend({ + modelName: 'user' +}); From 22b3ad7f385af6c54ae7ff4c5120986ac4602339 Mon Sep 17 00:00:00 2001 From: Susmitha Girumala Date: Tue, 23 Jun 2020 14:58:57 -0400 Subject: [PATCH 3/9] Add users routes --- ui/core/app/router.js | 5 +++++ ui/core/app/routes/orgs/org/users.js | 4 ++++ ui/core/app/routes/orgs/org/users/index.js | 4 ++++ ui/core/app/routes/orgs/org/users/new.js | 4 ++++ ui/core/app/routes/orgs/org/users/user.js | 4 ++++ ui/core/app/templates/orgs/org/users.hbs | 1 + ui/core/app/templates/orgs/org/users/index.hbs | 1 + ui/core/app/templates/orgs/org/users/new.hbs | 1 + ui/core/app/templates/orgs/org/users/user.hbs | 1 + ui/core/tests/unit/routes/orgs/org/users-test.js | 11 +++++++++++ .../tests/unit/routes/orgs/org/users/index-test.js | 11 +++++++++++ ui/core/tests/unit/routes/orgs/org/users/new-test.js | 11 +++++++++++ ui/core/tests/unit/routes/orgs/org/users/user-test.js | 11 +++++++++++ 13 files changed, 69 insertions(+) create mode 100644 ui/core/app/routes/orgs/org/users.js create mode 100644 ui/core/app/routes/orgs/org/users/index.js create mode 100644 ui/core/app/routes/orgs/org/users/new.js create mode 100644 ui/core/app/routes/orgs/org/users/user.js create mode 100644 ui/core/app/templates/orgs/org/users.hbs create mode 100644 ui/core/app/templates/orgs/org/users/index.hbs create mode 100644 ui/core/app/templates/orgs/org/users/new.hbs create mode 100644 ui/core/app/templates/orgs/org/users/user.hbs create mode 100644 ui/core/tests/unit/routes/orgs/org/users-test.js create mode 100644 ui/core/tests/unit/routes/orgs/org/users/index-test.js create mode 100644 ui/core/tests/unit/routes/orgs/org/users/new-test.js create mode 100644 ui/core/tests/unit/routes/orgs/org/users/user-test.js diff --git a/ui/core/app/router.js b/ui/core/app/router.js index 76af92941..58b16952b 100644 --- a/ui/core/app/router.js +++ b/ui/core/app/router.js @@ -18,6 +18,11 @@ Router.map(function () { }); this.route('new'); }); + + this.route('users', function() { + this.route('user', { path: ':user_id' }, function() {}); + this.route('new'); + }); }); }); }); diff --git a/ui/core/app/routes/orgs/org/users.js b/ui/core/app/routes/orgs/org/users.js new file mode 100644 index 000000000..c4dacd2f6 --- /dev/null +++ b/ui/core/app/routes/orgs/org/users.js @@ -0,0 +1,4 @@ +import Route from '@ember/routing/route'; + +export default class OrgsOrgUsersRoute extends Route { +} diff --git a/ui/core/app/routes/orgs/org/users/index.js b/ui/core/app/routes/orgs/org/users/index.js new file mode 100644 index 000000000..b6203b0d4 --- /dev/null +++ b/ui/core/app/routes/orgs/org/users/index.js @@ -0,0 +1,4 @@ +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 new file mode 100644 index 000000000..5df3b6cb8 --- /dev/null +++ b/ui/core/app/routes/orgs/org/users/new.js @@ -0,0 +1,4 @@ +import Route from '@ember/routing/route'; + +export default class OrgsOrgUsersNewRoute extends Route { +} diff --git a/ui/core/app/routes/orgs/org/users/user.js b/ui/core/app/routes/orgs/org/users/user.js new file mode 100644 index 000000000..9a15b7e07 --- /dev/null +++ b/ui/core/app/routes/orgs/org/users/user.js @@ -0,0 +1,4 @@ +import Route from '@ember/routing/route'; + +export default class OrgsOrgUsersUserRoute extends Route { +} diff --git a/ui/core/app/templates/orgs/org/users.hbs b/ui/core/app/templates/orgs/org/users.hbs new file mode 100644 index 000000000..e2147cab0 --- /dev/null +++ b/ui/core/app/templates/orgs/org/users.hbs @@ -0,0 +1 @@ +{{outlet}} \ No newline at end of file diff --git a/ui/core/app/templates/orgs/org/users/index.hbs b/ui/core/app/templates/orgs/org/users/index.hbs new file mode 100644 index 000000000..e2147cab0 --- /dev/null +++ b/ui/core/app/templates/orgs/org/users/index.hbs @@ -0,0 +1 @@ +{{outlet}} \ No newline at end of file diff --git a/ui/core/app/templates/orgs/org/users/new.hbs b/ui/core/app/templates/orgs/org/users/new.hbs new file mode 100644 index 000000000..e2147cab0 --- /dev/null +++ b/ui/core/app/templates/orgs/org/users/new.hbs @@ -0,0 +1 @@ +{{outlet}} \ No newline at end of file diff --git a/ui/core/app/templates/orgs/org/users/user.hbs b/ui/core/app/templates/orgs/org/users/user.hbs new file mode 100644 index 000000000..e2147cab0 --- /dev/null +++ b/ui/core/app/templates/orgs/org/users/user.hbs @@ -0,0 +1 @@ +{{outlet}} \ No newline at end of file diff --git a/ui/core/tests/unit/routes/orgs/org/users-test.js b/ui/core/tests/unit/routes/orgs/org/users-test.js new file mode 100644 index 000000000..93956225b --- /dev/null +++ b/ui/core/tests/unit/routes/orgs/org/users-test.js @@ -0,0 +1,11 @@ +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 new file mode 100644 index 000000000..cd215662e --- /dev/null +++ b/ui/core/tests/unit/routes/orgs/org/users/index-test.js @@ -0,0 +1,11 @@ +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 new file mode 100644 index 000000000..1aef05bab --- /dev/null +++ b/ui/core/tests/unit/routes/orgs/org/users/new-test.js @@ -0,0 +1,11 @@ +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 new file mode 100644 index 000000000..4bd6f26a2 --- /dev/null +++ b/ui/core/tests/unit/routes/orgs/org/users/user-test.js @@ -0,0 +1,11 @@ +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 01108573f002043b5f714d277abc8b7fdea9c161 Mon Sep 17 00:00:00 2001 From: Susmitha Girumala Date: Tue, 23 Jun 2020 15:06:30 -0400 Subject: [PATCH 4/9] Add populate users route with user table --- ui/core/app/routes/orgs/org/users.js | 9 +++ ui/core/app/templates/orgs/org/users.hbs | 4 +- .../app/templates/orgs/org/users/index.hbs | 64 ++++++++++++++++++- ui/core/mirage/config.js | 8 +++ ui/core/mirage/factories/user.js | 4 +- ui/core/translations/en-us.yaml | 4 ++ 6 files changed, 89 insertions(+), 4 deletions(-) diff --git a/ui/core/app/routes/orgs/org/users.js b/ui/core/app/routes/orgs/org/users.js index c4dacd2f6..7833312e3 100644 --- a/ui/core/app/routes/orgs/org/users.js +++ b/ui/core/app/routes/orgs/org/users.js @@ -1,4 +1,13 @@ 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/templates/orgs/org/users.hbs b/ui/core/app/templates/orgs/org/users.hbs index e2147cab0..67ccbeb9e 100644 --- a/ui/core/app/templates/orgs/org/users.hbs +++ b/ui/core/app/templates/orgs/org/users.hbs @@ -1 +1,3 @@ -{{outlet}} \ No newline at end of file +{{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 index e2147cab0..fe38c0c7f 100644 --- a/ui/core/app/templates/orgs/org/users/index.hbs +++ b/ui/core/app/templates/orgs/org/users/index.hbs @@ -1 +1,63 @@ -{{outlet}} \ No newline at end of file + + + + + + + +

{{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/mirage/config.js b/ui/core/mirage/config.js index 247fc6f36..f6dd9bf6d 100644 --- a/ui/core/mirage/config.js +++ b/ui/core/mirage/config.js @@ -40,6 +40,14 @@ export default function() { this.patch('/orgs/:org_id/auth-methods/:id'); this.del('/orgs/:org_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'); + // Other resources // host-catalog diff --git a/ui/core/mirage/factories/user.js b/ui/core/mirage/factories/user.js index 6c0997f8f..77ed8e69d 100644 --- a/ui/core/mirage/factories/user.js +++ b/ui/core/mirage/factories/user.js @@ -1,3 +1,3 @@ -import { Factory } from 'ember-cli-mirage'; +import factory from '../generated/factories/user'; -export default Factory.extend(); +export default factory.extend(); diff --git a/ui/core/translations/en-us.yaml b/ui/core/translations/en-us.yaml index 29ba0c47f..9b925155f 100644 --- a/ui/core/translations/en-us.yaml +++ b/ui/core/translations/en-us.yaml @@ -5,6 +5,8 @@ actions: save: Save help: Need help? resources: + user: User + users: Users projects: Projects project: Project host-catalog: Host Catalog @@ -16,11 +18,13 @@ titles: empty-set: No Items Yet projects-welcome: Welcome to Projects host-catalogs-welcome: Welcome to Host Catalogs + users-welcome: Welcome to Users 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. + users-welcome: Users are authorized to access Watchtower. states: info: Info success: Success From 8cb1d81d7173339701dfbf403ad10d0e5cb4949d Mon Sep 17 00:00:00 2001 From: Susmitha Girumala Date: Tue, 23 Jun 2020 15:15:47 -0400 Subject: [PATCH 5/9] Add user creation view --- ui/core/app/components/form/user/index.hbs | 49 +++++++++++++++++++ ui/core/app/routes/orgs/org/users/new.js | 46 +++++++++++++++++ ui/core/app/templates/orgs/org/users/new.hbs | 18 ++++++- ui/core/app/templates/orgs/org/users/user.hbs | 2 +- ui/core/translations/en-us.yaml | 5 ++ 5 files changed, 118 insertions(+), 2 deletions(-) create mode 100644 ui/core/app/components/form/user/index.hbs diff --git a/ui/core/app/components/form/user/index.hbs b/ui/core/app/components/form/user/index.hbs new file mode 100644 index 000000000..9db695baa --- /dev/null +++ b/ui/core/app/components/form/user/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/routes/orgs/org/users/new.js b/ui/core/app/routes/orgs/org/users/new.js index 5df3b6cb8..a740f6caf 100644 --- a/ui/core/app/routes/orgs/org/users/new.js +++ b/ui/core/app/routes/orgs/org/users/new.js @@ -1,4 +1,50 @@ 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/templates/orgs/org/users/new.hbs b/ui/core/app/templates/orgs/org/users/new.hbs index e2147cab0..4ae7ef85c 100644 --- a/ui/core/app/templates/orgs/org/users/new.hbs +++ b/ui/core/app/templates/orgs/org/users/new.hbs @@ -1 +1,17 @@ -{{outlet}} \ No newline at end of file + + + + + + +

{{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 index e2147cab0..c24cd6895 100644 --- a/ui/core/app/templates/orgs/org/users/user.hbs +++ b/ui/core/app/templates/orgs/org/users/user.hbs @@ -1 +1 @@ -{{outlet}} \ No newline at end of file +{{outlet}} diff --git a/ui/core/translations/en-us.yaml b/ui/core/translations/en-us.yaml index 9b925155f..e2b3c8f9b 100644 --- a/ui/core/translations/en-us.yaml +++ b/ui/core/translations/en-us.yaml @@ -48,6 +48,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. + user: + create-success: User created succesfully. + delete-success: Deleted user successfully. dev: default-org: Default Org menu-item: Menu item From d53f0aeae880bc275d647b5c9a70bb84304376e8 Mon Sep 17 00:00:00 2001 From: Susmitha Girumala Date: Tue, 23 Jun 2020 15:21:00 -0400 Subject: [PATCH 6/9] Add user edit view --- ui/core/app/routes/orgs/org/users/user.js | 60 +++++++++++++++++++ ui/core/app/templates/orgs/org/users/user.hbs | 2 + .../templates/orgs/org/users/user/index.hbs | 27 +++++++++ 3 files changed, 89 insertions(+) create mode 100644 ui/core/app/templates/orgs/org/users/user/index.hbs diff --git a/ui/core/app/routes/orgs/org/users/user.js b/ui/core/app/routes/orgs/org/users/user.js index 9a15b7e07..be2ea6f98 100644 --- a/ui/core/app/routes/orgs/org/users/user.js +++ b/ui/core/app/routes/orgs/org/users/user.js @@ -1,4 +1,64 @@ 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/org/users/user.hbs b/ui/core/app/templates/orgs/org/users/user.hbs index c24cd6895..2b05f838d 100644 --- a/ui/core/app/templates/orgs/org/users/user.hbs +++ b/ui/core/app/templates/orgs/org/users/user.hbs @@ -1 +1,3 @@ +{{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 new file mode 100644 index 000000000..a1b876705 --- /dev/null +++ b/ui/core/app/templates/orgs/org/users/user/index.hbs @@ -0,0 +1,27 @@ + + + + + + + +

{{t "resources.user"}}

+
+ + + + + + {{t 'actions.delete'}} + + + +
From 8b55714f2f3b574c9d7f3a2a97d3da98453a264c Mon Sep 17 00:00:00 2001 From: Susmitha Girumala Date: Tue, 23 Jun 2020 15:33:38 -0400 Subject: [PATCH 7/9] Add breadcrumbs to users --- ui/core/app/controllers/orgs/org/users.js | 18 +++++++++++ ui/core/app/controllers/orgs/org/users/new.js | 17 +++++++++++ .../app/controllers/orgs/org/users/user.js | 11 +++++++ ui/core/tests/acceptance/bread-crumbs-test.js | 30 +++++++++++++++++++ .../unit/controllers/orgs/org/users-test.js | 12 ++++++++ .../controllers/orgs/org/users/new-test.js | 12 ++++++++ .../controllers/orgs/org/users/user-test.js | 12 ++++++++ 7 files changed, 112 insertions(+) create mode 100644 ui/core/app/controllers/orgs/org/users.js create mode 100644 ui/core/app/controllers/orgs/org/users/new.js create mode 100644 ui/core/app/controllers/orgs/org/users/user.js create mode 100644 ui/core/tests/unit/controllers/orgs/org/users-test.js create mode 100644 ui/core/tests/unit/controllers/orgs/org/users/new-test.js create mode 100644 ui/core/tests/unit/controllers/orgs/org/users/user-test.js diff --git a/ui/core/app/controllers/orgs/org/users.js b/ui/core/app/controllers/orgs/org/users.js new file mode 100644 index 000000000..ae81e0302 --- /dev/null +++ b/ui/core/app/controllers/orgs/org/users.js @@ -0,0 +1,18 @@ +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 new file mode 100644 index 000000000..5e047015a --- /dev/null +++ b/ui/core/app/controllers/orgs/org/users/new.js @@ -0,0 +1,17 @@ +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 new file mode 100644 index 000000000..7e0216636 --- /dev/null +++ b/ui/core/app/controllers/orgs/org/users/user.js @@ -0,0 +1,11 @@ +import Controller from '@ember/controller'; +import { alias } from '@ember/object/computed'; + +export default class OrgsOrgUsersUserController extends Controller { + // =attributes + + /** + * @type {string} + */ + @alias('model.name') breadCrumb; +} diff --git a/ui/core/tests/acceptance/bread-crumbs-test.js b/ui/core/tests/acceptance/bread-crumbs-test.js index 2b134ae0a..4d2b65f9b 100644 --- a/ui/core/tests/acceptance/bread-crumbs-test.js +++ b/ui/core/tests/acceptance/bread-crumbs-test.js @@ -9,6 +9,7 @@ module('Acceptance | breadcrumbs', function (hooks) { let org; let project; + let user; let hostCatalog; let projectsURL; let newProjectURL; @@ -16,12 +17,17 @@ module('Acceptance | breadcrumbs', function (hooks) { let hostCatalogsURL; let newHostCatalogURL; let hostCatalogURL; + let usersURL; + let newUserURL; + let userURL; let crumbPath; hooks.beforeEach(function () { org = this.server.create('org'); project = this.server.create('project'); + user = this.server.create('user'); + hostCatalog = this.server.create('host-catalog'); projectsURL = `/orgs/${org.id}/projects`; newProjectURL = `${projectsURL}/new`; @@ -29,6 +35,11 @@ module('Acceptance | breadcrumbs', function (hooks) { hostCatalogsURL = `${projectURL}/host-catalogs`; newHostCatalogURL = `${hostCatalogsURL}/new`; hostCatalogURL = `${hostCatalogsURL}/${hostCatalog.id}`; + + // Users + usersURL = `/orgs/${org.id}/users`; + newUserURL = `${usersURL}/${user.id}`; + userURL = `${usersURL}/new`; }); test('can navigate via breadcrumbs to projects from project', async function (assert) { @@ -81,4 +92,23 @@ module('Acceptance | breadcrumbs', function (hooks) { assert.equal(currentURL(), hostCatalogsURL, '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; + 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.'); + }); + + 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; + 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.'); + }); }); diff --git a/ui/core/tests/unit/controllers/orgs/org/users-test.js b/ui/core/tests/unit/controllers/orgs/org/users-test.js new file mode 100644 index 000000000..c5be2fb52 --- /dev/null +++ b/ui/core/tests/unit/controllers/orgs/org/users-test.js @@ -0,0 +1,12 @@ +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 new file mode 100644 index 000000000..e6925b4d7 --- /dev/null +++ b/ui/core/tests/unit/controllers/orgs/org/users/new-test.js @@ -0,0 +1,12 @@ +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 new file mode 100644 index 000000000..f35f620fa --- /dev/null +++ b/ui/core/tests/unit/controllers/orgs/org/users/user-test.js @@ -0,0 +1,12 @@ +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); + }); +}); From 661379b1e412c3cfcadcfd18f0d86d90e4b5f1f3 Mon Sep 17 00:00:00 2001 From: Susmitha Girumala Date: Tue, 23 Jun 2020 15:42:37 -0400 Subject: [PATCH 8/9] Add users acceptance test --- .../app/templates/orgs/org/users/index.hbs | 2 +- ui/core/tests/acceptance/users-test.js | 192 ++++++++++++++++++ 2 files changed, 193 insertions(+), 1 deletion(-) create mode 100644 ui/core/tests/acceptance/users-test.js diff --git a/ui/core/app/templates/orgs/org/users/index.hbs b/ui/core/app/templates/orgs/org/users/index.hbs index fe38c0c7f..8e147d3ac 100644 --- a/ui/core/app/templates/orgs/org/users/index.hbs +++ b/ui/core/app/templates/orgs/org/users/index.hbs @@ -49,7 +49,7 @@ {{t 'descriptions.users-welcome'}} - + {{t 'titles.new'}} diff --git a/ui/core/tests/acceptance/users-test.js b/ui/core/tests/acceptance/users-test.js new file mode 100644 index 000000000..f1160c7f3 --- /dev/null +++ b/ui/core/tests/acceptance/users-test.js @@ -0,0 +1,192 @@ +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 | users', function(hooks) { + setupApplicationTest(hooks); + setupMirage(hooks); + + hooks.beforeEach(function () { + this.server.create('org'); + }); + + test('visiting users', async function (assert) { + assert.expect(1); + this.server.createList('user', 1); + await visit('/orgs/1/users'); + await a11yAudit(); + assert.equal(currentURL(), '/orgs/1/users'); + }); + + 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 fillIn('[name="name"]', 'User name'); + await click('[type="submit"]'); + assert.equal(currentURL(), '/orgs/1/users/1'); + assert.equal(this.server.db.users.length, 1); + assert.equal(this.server.db.users[0].name, 'User name'); + }); + + 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 fillIn('[name="name"]', 'User name'); + await click('.rose-form-actions [type="button"]'); + assert.equal(currentURL(), '/orgs/1/users'); + 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', () => { + 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/users/new'); + await fillIn('[name="name"]', 'User 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 user', async function (assert) { + assert.expect(2); + 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"]'); + assert.equal(currentURL(), '/orgs/1/users/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 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) { + assert.expect(2); + this.server.createList('user', 1); + assert.equal(this.server.db.users.length, 1); + await visit('/orgs/1/users/1'); + await click('.rose-button-warning'); + assert.equal(this.server.db.users.length, 0); + }); + + 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', () => { + 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/users/1'); + await fillIn('[name="name"]', 'User 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('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', () => { + return new Response( + 490, + {}, + { + status: 490, + code: 'error', + message: 'Oops.', + } + ); + }); + await visit('/orgs/1/users/1'); + await fillIn('[name="name"]', 'User name'); + await click('[type="submit"]'); + assert.ok( + find('[role="alert"]').textContent.trim(), + 'Oops.', + 'Displays primary error message.' + ); + }); + + 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', () => { + return new Response( + 490, + {}, + { + status: 490, + code: 'error', + message: 'Oops.', + } + ); + }); + await visit('/orgs/1/users/1'); + await click('.rose-button-warning'); + assert.ok( + find('[role="alert"]').textContent.trim(), + 'Oops.', + 'Displays primary error message.' + ); + }); +}); From e1985e10ee3215a4b166661b1170c96e67bf130e Mon Sep 17 00:00:00 2001 From: Susmitha Girumala Date: Mon, 29 Jun 2020 11:06:27 -0400 Subject: [PATCH 9/9] =?UTF-8?q?chore:=20=F0=9F=A4=96=20Update=20user=20bre?= =?UTF-8?q?adcrumb=20display=20name?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- addons/api/tests/unit/adapters/user-test.js | 10 ++++++++++ ui/core/app/components/form/user/index.hbs | 2 +- ui/core/app/controllers/orgs/org/users/user.js | 2 +- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/addons/api/tests/unit/adapters/user-test.js b/addons/api/tests/unit/adapters/user-test.js index 04c42f929..17dc82ff6 100644 --- a/addons/api/tests/unit/adapters/user-test.js +++ b/addons/api/tests/unit/adapters/user-test.js @@ -1,5 +1,6 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; +import config from 'ember-get-config'; module('Unit | Adapter | user', function(hooks) { setupTest(hooks); @@ -8,4 +9,13 @@ module('Unit | Adapter | user', function(hooks) { 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/ui/core/app/components/form/user/index.hbs b/ui/core/app/components/form/user/index.hbs index 9db695baa..e8d100b60 100644 --- a/ui/core/app/components/form/user/index.hbs +++ b/ui/core/app/components/form/user/index.hbs @@ -2,7 +2,7 @@ @onSubmit={{@submit}} @cancel={{@cancel}} @disabled={{@model.isSaving}} - @error={{true}} + @error={{@model.errors.base}} as |form| > diff --git a/ui/core/app/controllers/orgs/org/users/user.js b/ui/core/app/controllers/orgs/org/users/user.js index 7e0216636..3461021b1 100644 --- a/ui/core/app/controllers/orgs/org/users/user.js +++ b/ui/core/app/controllers/orgs/org/users/user.js @@ -7,5 +7,5 @@ export default class OrgsOrgUsersUserController extends Controller { /** * @type {string} */ - @alias('model.name') breadCrumb; + @alias('model.displayName') breadCrumb; }