diff --git a/ui/core/app/components/form/auth-method/index.hbs b/ui/core/app/components/form/auth-method/index.hbs new file mode 100644 index 000000000..7c1bc5162 --- /dev/null +++ b/ui/core/app/components/form/auth-method/index.hbs @@ -0,0 +1,53 @@ + + + + + {{#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}} + + + + \ No newline at end of file diff --git a/ui/core/app/controllers/scopes/scope/auth-methods.js b/ui/core/app/controllers/scopes/scope/auth-methods.js new file mode 100644 index 000000000..ab152cf62 --- /dev/null +++ b/ui/core/app/controllers/scopes/scope/auth-methods.js @@ -0,0 +1,18 @@ +import Controller from '@ember/controller'; +import { inject as service } from '@ember/service'; + +export default class ScopesScopeAuthMethodsController extends Controller { + // =services + + @service intl; + + // =attributes + + /** + * Translated auth-methods breadcrumb + * @type {string} + */ + get breadCrumb() { + return this.intl.t('resources.auth-methods'); + } +} diff --git a/ui/core/app/controllers/scopes/scope/auth-methods/auth-method.js b/ui/core/app/controllers/scopes/scope/auth-methods/auth-method.js new file mode 100644 index 000000000..1005f0216 --- /dev/null +++ b/ui/core/app/controllers/scopes/scope/auth-methods/auth-method.js @@ -0,0 +1,13 @@ +import Controller from '@ember/controller'; + +export default class ScopesScopeAuthMethodsAuthMethodController extends Controller { + // =attributes + + /** + * An auth-method breadcrumb + * @type {string} + */ + get breadCrumb() { + return this.model.displayName; + } +} diff --git a/ui/core/app/controllers/scopes/scope/auth-methods/new.js b/ui/core/app/controllers/scopes/scope/auth-methods/new.js new file mode 100644 index 000000000..ba550f456 --- /dev/null +++ b/ui/core/app/controllers/scopes/scope/auth-methods/new.js @@ -0,0 +1,18 @@ +import Controller from '@ember/controller'; +import { inject as service } from '@ember/service'; + +export default class ScopesScopeAuthMethodsNewController extends Controller { + // =services + + @service intl; + + // =attributes + + /** + * Translated new auth-method 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 cf5cea5d6..af71f6132 100644 --- a/ui/core/app/router.js +++ b/ui/core/app/router.js @@ -36,7 +36,10 @@ Router.map(function () { }); this.route('new'); }); - this.route('auth-methods', function() {}); + this.route('auth-methods', function() { + this.route('auth-method', { path: ':auth_method_id' }, function() {}); + this.route('new'); + }); }); }); }); diff --git a/ui/core/app/routes/scopes/scope/auth-methods.js b/ui/core/app/routes/scopes/scope/auth-methods.js index 112c40248..ffad801fa 100644 --- a/ui/core/app/routes/scopes/scope/auth-methods.js +++ b/ui/core/app/routes/scopes/scope/auth-methods.js @@ -1,6 +1,13 @@ import Route from '@ember/routing/route'; +import { inject as service } from '@ember/service'; +import { action } from '@ember/object'; export default class ScopesScopeAuthMethodsRoute extends Route { + // =services + + @service intl; + @service notify; + // =methods /** @@ -11,4 +18,36 @@ export default class ScopesScopeAuthMethodsRoute extends Route { const { id: scopeID} = this.modelFor('scopes.scope'); return this.store.findAll('auth-method', { adapterOptions: { scopeID } }); } + + // =actions + /** + * Rollback changes to an auth-method. + * @param {AuthMethodModel} authMethod + */ + @action + cancel(authMethod) { + const { isNew } = authMethod; + authMethod.rollbackAttributes(); + if (isNew) this.transitionTo('scopes.scope.auth-methods'); + } + + /** + * Save an auth-method in current scope. + * @param {AuthMethodModel} authMethod + */ + @action + async save(authMethod) { + const { isNew } = authMethod; + try { + await authMethod.save(); + this.refresh(); + this.notify.success( + this.intl.t(isNew ? 'notify.create-success' : 'notify.save-success') + ); + this.transitionTo('scopes.scope.auth-methods.auth-method', authMethod); + } catch (error) { + //TODO: replace with translated strings + this.notify.error(error.message, { closeAfter: null }); + } + } } diff --git a/ui/core/app/routes/scopes/scope/auth-methods/auth-method.js b/ui/core/app/routes/scopes/scope/auth-methods/auth-method.js new file mode 100644 index 000000000..5cc752eac --- /dev/null +++ b/ui/core/app/routes/scopes/scope/auth-methods/auth-method.js @@ -0,0 +1,16 @@ +import Route from '@ember/routing/route'; + +export default class ScopesScopeAuthMethodsAuthMethodRoute extends Route { + // =methods + + /** + * Load a single auth-method in current scope. + * @param {object} params + * @param {string} params.auth_method_id + * @return {authMethodModel} + */ + async model({ auth_method_id}) { + const { id: scopeID } = this.modelFor('scopes.scope'); + return this.store.findRecord('auth-method', auth_method_id, { adapterOptions: { scopeID }}); + } +} diff --git a/ui/core/app/routes/scopes/scope/auth-methods/auth-method/index.js b/ui/core/app/routes/scopes/scope/auth-methods/auth-method/index.js new file mode 100644 index 000000000..5cef508e0 --- /dev/null +++ b/ui/core/app/routes/scopes/scope/auth-methods/auth-method/index.js @@ -0,0 +1,4 @@ +import Route from '@ember/routing/route'; + +export default class ScopesScopeAuthMethodsAuthMethodIndexRoute extends Route { +} diff --git a/ui/core/app/routes/scopes/scope/auth-methods/new.js b/ui/core/app/routes/scopes/scope/auth-methods/new.js new file mode 100644 index 000000000..ceeb6a5d7 --- /dev/null +++ b/ui/core/app/routes/scopes/scope/auth-methods/new.js @@ -0,0 +1,14 @@ +import Route from '@ember/routing/route'; + +export default class ScopesScopeAuthMethodsNewRoute extends Route { + // =methods + + /** + * Create a new unsaved auth-method. + * @return {AuthMethodModel} + */ + model() { + const scopeModel = this.modelFor('scopes.scope'); + return this.store.createRecord('auth-method', { type: 'password', scopeModel }); + } +} diff --git a/ui/core/app/templates/scopes/scope/auth-methods/auth-method.hbs b/ui/core/app/templates/scopes/scope/auth-methods/auth-method.hbs new file mode 100644 index 000000000..c24cd6895 --- /dev/null +++ b/ui/core/app/templates/scopes/scope/auth-methods/auth-method.hbs @@ -0,0 +1 @@ +{{outlet}} diff --git a/ui/core/app/templates/scopes/scope/auth-methods/auth-method/index.hbs b/ui/core/app/templates/scopes/scope/auth-methods/auth-method/index.hbs new file mode 100644 index 000000000..c08c3fd4a --- /dev/null +++ b/ui/core/app/templates/scopes/scope/auth-methods/auth-method/index.hbs @@ -0,0 +1,28 @@ + + +

{{t "resources.auth-method"}}

+ + {{t (concat 'auth.method-types.' @model.type)}} + +
+ + + + + + + + + {{t 'titles.details'}} + + + + + + + +
\ No newline at end of file diff --git a/ui/core/app/templates/scopes/scope/auth-methods/index.hbs b/ui/core/app/templates/scopes/scope/auth-methods/index.hbs index 2d5456c58..a5bc4f485 100644 --- a/ui/core/app/templates/scopes/scope/auth-methods/index.hbs +++ b/ui/core/app/templates/scopes/scope/auth-methods/index.hbs @@ -3,6 +3,18 @@

{{t "resources.auth-methods"}}

+ + + + + + {{#if @model}} + + {{t "titles.new"}} + + {{/if}} + + {{#if @model}} @@ -19,12 +31,17 @@ {{#each @model as |authMethod|}} - {{authMethod.name}} - {{authMethod.description}} + + {{authMethod.name}} + +

{{authMethod.description}}

- {{authMethod.type}} + {{t (concat 'auth.method-types.' authMethod.type)}} {{authMethod.id}} @@ -40,6 +57,10 @@ {{t 'descriptions.auth-methods-welcome'}} + + + {{t 'titles.new'}} + diff --git a/ui/core/app/templates/scopes/scope/auth-methods/new.hbs b/ui/core/app/templates/scopes/scope/auth-methods/new.hbs new file mode 100644 index 000000000..44c4f35d6 --- /dev/null +++ b/ui/core/app/templates/scopes/scope/auth-methods/new.hbs @@ -0,0 +1,19 @@ +{{title (t "titles.new")}} + + + +

{{t "titles.new"}}

+
+ + + + + + + + +
\ No newline at end of file diff --git a/ui/core/tests/acceptance/auth-methods-test.js b/ui/core/tests/acceptance/auth-methods-test.js new file mode 100644 index 000000000..5b816f253 --- /dev/null +++ b/ui/core/tests/acceptance/auth-methods-test.js @@ -0,0 +1,94 @@ +import { module, test } from 'qunit'; +import { visit, currentURL } 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 { + authenticateSession, + // These are left here intentionally for future reference. + //currentSession, + //invalidateSession, +} from 'ember-simple-auth/test-support'; + +module('Acceptance | auth methods', function (hooks) { + setupApplicationTest(hooks); + setupMirage(hooks); + + const instances = { + scopes: { + global: null, + org: null + }, + authMethod: null + }; + const urls = { + orgScope: null, + authMethods: null + }; + + hooks.beforeEach(function () { + // 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.authMethod = this.server.create('auth-method', { + scope: { + id: instances.scopes.org.id, + type: instances.scopes.org.type + } + }); + + // Generate route URLs for resources + urls.orgScope = `/scopes/${instances.scopes.org.id}`; + urls.authMethods = `${urls.orgScope}/auth-methods`; + }); + + test('visiting auth methods', async function (assert) { + assert.expect(1); + authenticateSession(); + await visit(urls.authMethods); + await a11yAudit(); + assert.equal(currentURL(), urls.authMethods); + }); + + test('can navigate to an auth method form', async function (assert) { + assert.expect(0); + }); + + test('can update an auth method and save changes', async function (assert) { + assert.expect(0); + }); + + test('can update an auth method and cancel changes', async function (assert) { + assert.expect(0); + }); + + test('can create new auth method', async function (assert) { + assert.expect(0); + }); + + test('can cancel new auth method creation', async function (assert) { + assert.expect(0); + }); + + test('saving a new auth method with invalid fields displays error messages', async function (assert) { + assert.expect(0); + }); + + test('errors are displayed when save on auth method fails', async function (assert) { + assert.expect(0); + }); + + test('errors are displayed when delete on an auth method fails', async function (assert) { + assert.expect(0); + }); + + test('saving a new auth method with invalid fields displays error messages', async function (assert) { + assert.expect(0); + }); + + test('saving an existing auth method with invalid fields displays error messages', async function (assert) { + assert.expect(0); + }); +}); diff --git a/ui/core/tests/integration/components/form/auth-method-test.js b/ui/core/tests/integration/components/form/auth-method-test.js new file mode 100644 index 000000000..267e516b5 --- /dev/null +++ b/ui/core/tests/integration/components/form/auth-method-test.js @@ -0,0 +1,23 @@ +import { module, test } from 'qunit'; +import { setupRenderingTest } from 'ember-qunit'; +import { render, find } from '@ember/test-helpers'; +import { hbs } from 'ember-cli-htmlbars'; + +module('Integration | Component | form/auth-method', function (hooks) { + setupRenderingTest(hooks); + + test('it renders', async function (assert) { + this.submit = () => {}; + this.cancel = () => {}; + this.model = { type: 'password' }; + + await render(hbs` + `); + + assert.ok(find('form')); + }); +}); diff --git a/ui/core/tests/unit/controllers/scopes/scope/auth-methods-test.js b/ui/core/tests/unit/controllers/scopes/scope/auth-methods-test.js new file mode 100644 index 000000000..20cbdbe00 --- /dev/null +++ b/ui/core/tests/unit/controllers/scopes/scope/auth-methods-test.js @@ -0,0 +1,12 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Controller | scopes/scope/auth-methods', function(hooks) { + setupTest(hooks); + + // Replace this with your real tests. + test('it exists', function(assert) { + let controller = this.owner.lookup('controller:scopes/scope/auth-methods'); + assert.ok(controller); + }); +}); diff --git a/ui/core/tests/unit/controllers/scopes/scope/auth-methods/auth-method-test.js b/ui/core/tests/unit/controllers/scopes/scope/auth-methods/auth-method-test.js new file mode 100644 index 000000000..ad19d3e67 --- /dev/null +++ b/ui/core/tests/unit/controllers/scopes/scope/auth-methods/auth-method-test.js @@ -0,0 +1,12 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Controller | scopes/scope/auth-methods/auth-method', function(hooks) { + setupTest(hooks); + + // Replace this with your real tests. + test('it exists', function(assert) { + let controller = this.owner.lookup('controller:scopes/scope/auth-methods/auth-method'); + assert.ok(controller); + }); +}); diff --git a/ui/core/tests/unit/controllers/scopes/scope/auth-methods/new-test.js b/ui/core/tests/unit/controllers/scopes/scope/auth-methods/new-test.js new file mode 100644 index 000000000..2d5306d6a --- /dev/null +++ b/ui/core/tests/unit/controllers/scopes/scope/auth-methods/new-test.js @@ -0,0 +1,12 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Controller | scopes/scope/auth-methods/new', function(hooks) { + setupTest(hooks); + + // Replace this with your real tests. + test('it exists', function(assert) { + let controller = this.owner.lookup('controller:scopes/scope/auth-methods/new'); + assert.ok(controller); + }); +}); diff --git a/ui/core/tests/unit/routes/scopes/scope/auth-methods/auth-method-test.js b/ui/core/tests/unit/routes/scopes/scope/auth-methods/auth-method-test.js new file mode 100644 index 000000000..36b2e2f2d --- /dev/null +++ b/ui/core/tests/unit/routes/scopes/scope/auth-methods/auth-method-test.js @@ -0,0 +1,11 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Route | scopes/scope/auth-methods/auth-method', function(hooks) { + setupTest(hooks); + + test('it exists', function(assert) { + let route = this.owner.lookup('route:scopes/scope/auth-methods/auth-method'); + assert.ok(route); + }); +}); diff --git a/ui/core/tests/unit/routes/scopes/scope/auth-methods/auth-method/index-test.js b/ui/core/tests/unit/routes/scopes/scope/auth-methods/auth-method/index-test.js new file mode 100644 index 000000000..5a51858df --- /dev/null +++ b/ui/core/tests/unit/routes/scopes/scope/auth-methods/auth-method/index-test.js @@ -0,0 +1,11 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Route | scopes/scope/auth-methods/auth-method/index', function(hooks) { + setupTest(hooks); + + test('it exists', function(assert) { + let route = this.owner.lookup('route:scopes/scope/auth-methods/auth-method/index'); + assert.ok(route); + }); +}); diff --git a/ui/core/tests/unit/routes/scopes/scope/auth-methods/new-test.js b/ui/core/tests/unit/routes/scopes/scope/auth-methods/new-test.js new file mode 100644 index 000000000..6eea2751c --- /dev/null +++ b/ui/core/tests/unit/routes/scopes/scope/auth-methods/new-test.js @@ -0,0 +1,11 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Route | scopes/scope/auth-methods/new', function(hooks) { + setupTest(hooks); + + test('it exists', function(assert) { + let route = this.owner.lookup('route:scopes/scope/auth-methods/new'); + assert.ok(route); + }); +}); diff --git a/ui/core/translations/en-us.yaml b/ui/core/translations/en-us.yaml index 2fe1ad5f8..10e48f67c 100644 --- a/ui/core/translations/en-us.yaml +++ b/ui/core/translations/en-us.yaml @@ -31,6 +31,7 @@ resources: project: Project host-catalog: Host Catalog host-catalogs: Host Catalogs + auth-method: Auth Method auth-methods: Auth Methods titles: iam: IAM