From 7ff3bae26d2052e13f99ca67b7cc7a75217048b2 Mon Sep 17 00:00:00 2001 From: Susmitha Girumala Date: Wed, 12 Aug 2020 17:16:41 -0400 Subject: [PATCH 01/13] chore: Add auth-method index route --- .../app/components/form/auth-method/index.hbs | 47 ++++++++++++++++ ui/core/app/router.js | 4 +- .../app/routes/scopes/scope/auth-methods.js | 56 +++++++++++++++++++ .../scopes/scope/auth-methods/auth-method.js | 16 ++++++ .../scope/auth-methods/auth-method/index.js | 4 ++ .../scopes/scope/auth-methods/auth-method.hbs | 1 + .../scope/auth-methods/auth-method/index.hbs | 24 ++++++++ .../scopes/scope/auth-methods/index.hbs | 7 ++- .../components/form/auth-method/index-test.js | 26 +++++++++ .../scope/auth-methods/auth-method-test.js | 11 ++++ .../auth-methods/auth-method/index-test.js | 11 ++++ ui/core/translations/en-us.yaml | 1 + 12 files changed, 206 insertions(+), 2 deletions(-) create mode 100644 ui/core/app/components/form/auth-method/index.hbs create mode 100644 ui/core/app/routes/scopes/scope/auth-methods/auth-method.js create mode 100644 ui/core/app/routes/scopes/scope/auth-methods/auth-method/index.js create mode 100644 ui/core/app/templates/scopes/scope/auth-methods/auth-method.hbs create mode 100644 ui/core/app/templates/scopes/scope/auth-methods/auth-method/index.hbs create mode 100644 ui/core/tests/integration/components/form/auth-method/index-test.js create mode 100644 ui/core/tests/unit/routes/scopes/scope/auth-methods/auth-method-test.js create mode 100644 ui/core/tests/unit/routes/scopes/scope/auth-methods/auth-method/index-test.js 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..158f80db2 --- /dev/null +++ b/ui/core/app/components/form/auth-method/index.hbs @@ -0,0 +1,47 @@ + + + {{#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/router.js b/ui/core/app/router.js index 1471216ea..e23fad074 100644 --- a/ui/core/app/router.js +++ b/ui/core/app/router.js @@ -35,7 +35,9 @@ 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() {}); + }); }); }); }); diff --git a/ui/core/app/routes/scopes/scope/auth-methods.js b/ui/core/app/routes/scopes/scope/auth-methods.js index 112c40248..9c747ba6f 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,53 @@ 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'); + } catch (error) { + //TODO: replace with translated strings + this.notify.error(error.message, { closeAfter: null }); + } + } + + /** + * Delete an auth-method in current scope and redirect to auth-methods + * @param {AuthMethodModel} authMethod + */ + @action + async delete(authMethod) { + try { + await authMethod.destroyRecord(); + this.refresh(); + this.notify.success(this.intl.t('notify.delete-success')); + this.transitionTo('scopes.scope.auth-methods'); + } 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/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..3fbc14eca --- /dev/null +++ b/ui/core/app/templates/scopes/scope/auth-methods/auth-method/index.hbs @@ -0,0 +1,24 @@ + + +

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

+
+ + + + + {{t 'titles.details'}} + + + + + + + {{@model.type}} + + + +
\ 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 1b805f939..3286dff79 100644 --- a/ui/core/app/templates/scopes/scope/auth-methods/index.hbs +++ b/ui/core/app/templates/scopes/scope/auth-methods/index.hbs @@ -18,7 +18,12 @@ {{#each @model as |authMethod|}} - {{authMethod.name}} + + {{authMethod.name}} + {{authMethod.description}} diff --git a/ui/core/tests/integration/components/form/auth-method/index-test.js b/ui/core/tests/integration/components/form/auth-method/index-test.js new file mode 100644 index 000000000..df00881db --- /dev/null +++ b/ui/core/tests/integration/components/form/auth-method/index-test.js @@ -0,0 +1,26 @@ +import { module, test } from 'qunit'; +import { setupRenderingTest } from 'ember-qunit'; +import { render } from '@ember/test-helpers'; +import { hbs } from 'ember-cli-htmlbars'; + +module('Integration | Component | form/auth-method/index', function(hooks) { + setupRenderingTest(hooks); + + test('it renders', async function(assert) { + // Set any properties with this.set('myProperty', 'value'); + // Handle any actions with this.set('myAction', function(val) { ... }); + + await render(hbs``); + + assert.equal(this.element.textContent.trim(), ''); + + // Template block usage: + await render(hbs` + + template block text + + `); + + assert.equal(this.element.textContent.trim(), 'template block text'); + }); +}); 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/translations/en-us.yaml b/ui/core/translations/en-us.yaml index 1002147e2..996073949 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 From 3524b7c3ff51a1e1daeae08b8e43a90340e6d0c2 Mon Sep 17 00:00:00 2001 From: Susmitha Girumala Date: Thu, 13 Aug 2020 10:47:51 -0400 Subject: [PATCH 02/13] chore: Remove unused delete method --- .../app/components/form/auth-method/index.hbs | 6 +++++ .../app/routes/scopes/scope/auth-methods.js | 17 ------------ .../scope/auth-methods/auth-method/index.hbs | 3 --- .../components/form/auth-method-test.js | 21 +++++++++++++++ .../components/form/auth-method/index-test.js | 26 ------------------- 5 files changed, 27 insertions(+), 46 deletions(-) create mode 100644 ui/core/tests/integration/components/form/auth-method-test.js delete mode 100644 ui/core/tests/integration/components/form/auth-method/index-test.js diff --git a/ui/core/app/components/form/auth-method/index.hbs b/ui/core/app/components/form/auth-method/index.hbs index 158f80db2..c2dccd6b6 100644 --- a/ui/core/app/components/form/auth-method/index.hbs +++ b/ui/core/app/components/form/auth-method/index.hbs @@ -5,6 +5,12 @@ @error={{@model.errors.base}} as |form| > + + - - {{@model.type}} - {}; + this.cancel = () => {}; + + await render(hbs` + `); + + assert.ok(find('form')); + }); +}); diff --git a/ui/core/tests/integration/components/form/auth-method/index-test.js b/ui/core/tests/integration/components/form/auth-method/index-test.js deleted file mode 100644 index df00881db..000000000 --- a/ui/core/tests/integration/components/form/auth-method/index-test.js +++ /dev/null @@ -1,26 +0,0 @@ -import { module, test } from 'qunit'; -import { setupRenderingTest } from 'ember-qunit'; -import { render } from '@ember/test-helpers'; -import { hbs } from 'ember-cli-htmlbars'; - -module('Integration | Component | form/auth-method/index', function(hooks) { - setupRenderingTest(hooks); - - test('it renders', async function(assert) { - // Set any properties with this.set('myProperty', 'value'); - // Handle any actions with this.set('myAction', function(val) { ... }); - - await render(hbs``); - - assert.equal(this.element.textContent.trim(), ''); - - // Template block usage: - await render(hbs` - - template block text - - `); - - assert.equal(this.element.textContent.trim(), 'template block text'); - }); -}); From 0d73c81ca533a4ddc1cb8a9f73f3f284ad192fa0 Mon Sep 17 00:00:00 2001 From: Susmitha Girumala Date: Thu, 13 Aug 2020 15:12:31 -0400 Subject: [PATCH 03/13] chore: Add auth-method type badge to page title --- .../scopes/scope/auth-methods/auth-method/index.hbs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) 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 index 4fa1f7ea3..0d9a5ca5e 100644 --- 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 @@ -1,6 +1,9 @@ -

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

+

+ {{t "resources.auth-method"}} + {{@model.type}} +

From 89384a29de061baa3d6306c83d3110c8819e4888 Mon Sep 17 00:00:00 2001 From: Susmitha Girumala Date: Thu, 13 Aug 2020 15:41:33 -0400 Subject: [PATCH 04/13] test: Add authmethods acceptance test --- ui/core/tests/acceptance/auth-methods-test.js | 82 +++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 ui/core/tests/acceptance/auth-methods-test.js 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..fe4dc5c0e --- /dev/null +++ b/ui/core/tests/acceptance/auth-methods-test.js @@ -0,0 +1,82 @@ +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('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); + }); +}); From 8dd13a0936979f7032abfa9d06d0ec674dcc1f22 Mon Sep 17 00:00:00 2001 From: Susmitha Girumala Date: Thu, 13 Aug 2020 16:08:45 -0400 Subject: [PATCH 05/13] chore: Create auth-method route --- ui/core/app/router.js | 1 + ui/core/app/routes/scopes/scope/auth-methods.js | 2 +- .../app/routes/scopes/scope/auth-methods/new.js | 14 ++++++++++++++ .../templates/scopes/scope/auth-methods/index.hbs | 8 ++++++++ .../templates/scopes/scope/auth-methods/new.hbs | 15 +++++++++++++++ ui/core/tests/acceptance/auth-methods-test.js | 12 ++++++++++++ .../routes/scopes/scope/auth-methods/new-test.js | 11 +++++++++++ 7 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 ui/core/app/routes/scopes/scope/auth-methods/new.js create mode 100644 ui/core/app/templates/scopes/scope/auth-methods/new.hbs create mode 100644 ui/core/tests/unit/routes/scopes/scope/auth-methods/new-test.js diff --git a/ui/core/app/router.js b/ui/core/app/router.js index da0c7dcce..af71f6132 100644 --- a/ui/core/app/router.js +++ b/ui/core/app/router.js @@ -38,6 +38,7 @@ Router.map(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 91c6ac1f4..ffad801fa 100644 --- a/ui/core/app/routes/scopes/scope/auth-methods.js +++ b/ui/core/app/routes/scopes/scope/auth-methods.js @@ -44,7 +44,7 @@ export default class ScopesScopeAuthMethodsRoute extends Route { this.notify.success( this.intl.t(isNew ? 'notify.create-success' : 'notify.save-success') ); - this.transitionTo('scopes.scope.auth-methods.auth-method'); + 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/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/index.hbs b/ui/core/app/templates/scopes/scope/auth-methods/index.hbs index 9d741b6cd..61c7a08e4 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,14 @@

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

+ + {{#if @model}} + + {{t "titles.new"}} + + {{/if}} + + {{#if @model}} 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..315156454 --- /dev/null +++ b/ui/core/app/templates/scopes/scope/auth-methods/new.hbs @@ -0,0 +1,15 @@ +{{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 index fe4dc5c0e..5b816f253 100644 --- a/ui/core/tests/acceptance/auth-methods-test.js +++ b/ui/core/tests/acceptance/auth-methods-test.js @@ -64,6 +64,18 @@ module('Acceptance | auth methods', function (hooks) { 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); }); 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); + }); +}); From 013395eec1a0928c3e9900d1187e63de29c05337 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Thu, 13 Aug 2020 17:05:09 -0400 Subject: [PATCH 06/13] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20add=20initial=20mo?= =?UTF-8?q?deling=20for=20host=20sets?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- addons/api/addon/generated/models/host-set.js | 42 +++++++++++++++++++ addons/api/addon/models/host-catalog.js | 10 ++++- addons/api/addon/models/host-set.js | 13 ++++++ addons/api/app/models/host-set.js | 1 + addons/api/tests/unit/models/host-set-test.js | 13 ++++++ 5 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 addons/api/addon/generated/models/host-set.js create mode 100644 addons/api/addon/models/host-set.js create mode 100644 addons/api/app/models/host-set.js create mode 100644 addons/api/tests/unit/models/host-set-test.js diff --git a/addons/api/addon/generated/models/host-set.js b/addons/api/addon/generated/models/host-set.js new file mode 100644 index 000000000..5793cc4a5 --- /dev/null +++ b/addons/api/addon/generated/models/host-set.js @@ -0,0 +1,42 @@ +import BaseModel from '../../models/base'; +import { attr } from '@ember-data/model'; + +/** + * HostSet is a collection of Hosts created and managed by a HostCatalog + */ +export default class GeneratedHostSetModel extends BaseModel { + + // =attributes + + @attr('string', { + description: 'The type of the resource, to help differentiate schemas' + }) 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; + + @attr('number', { + description: 'Current version number of this resource.', + }) + version; + + @attr('number', { + description: 'The total count of hosts in this host set\nOutput only.' + }) size; + +} diff --git a/addons/api/addon/models/host-catalog.js b/addons/api/addon/models/host-catalog.js index a0162bfeb..6f3729a47 100644 --- a/addons/api/addon/models/host-catalog.js +++ b/addons/api/addon/models/host-catalog.js @@ -1,3 +1,11 @@ import GeneratedHostCatalogModel from '../generated/models/host-catalog'; +import { hasMany } from '@ember-data/model'; -export default class HostCatalogModel extends GeneratedHostCatalogModel { } +export default class HostCatalogModel extends GeneratedHostCatalogModel { + + /** + * @type {HostSetModel[]} + */ + @hasMany('host-set') hostSets; + +} diff --git a/addons/api/addon/models/host-set.js b/addons/api/addon/models/host-set.js new file mode 100644 index 000000000..ad1fd89a6 --- /dev/null +++ b/addons/api/addon/models/host-set.js @@ -0,0 +1,13 @@ +import GeneratedHostSetModel from '../generated/models/host-set'; +import { belongsTo } from '@ember-data/model'; + +export default class HostSetModel extends GeneratedHostSetModel { + + // =relationships + + /** + * @type {HostCatalogModel} + */ + @belongsTo('host-catalog') hostCatalog; + +} diff --git a/addons/api/app/models/host-set.js b/addons/api/app/models/host-set.js new file mode 100644 index 000000000..6963bf5e3 --- /dev/null +++ b/addons/api/app/models/host-set.js @@ -0,0 +1 @@ +export { default } from 'api/models/host-set'; diff --git a/addons/api/tests/unit/models/host-set-test.js b/addons/api/tests/unit/models/host-set-test.js new file mode 100644 index 000000000..61e007f70 --- /dev/null +++ b/addons/api/tests/unit/models/host-set-test.js @@ -0,0 +1,13 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Model | host set', 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('host-set', {}); + assert.ok(model); + }); +}); From e790d5871738ad9239bfc17bf7efead96d9c529b Mon Sep 17 00:00:00 2001 From: Susmitha Girumala Date: Thu, 13 Aug 2020 17:20:10 -0400 Subject: [PATCH 07/13] chore: Use translated string for auth-method type --- .../app/components/form/auth-method/index.hbs | 2 +- .../scope/auth-methods/auth-method/index.hbs | 8 ++++---- .../scopes/scope/auth-methods/index.hbs | 16 +++++++++------- .../components/form/auth-method-test.js | 2 ++ 4 files changed, 16 insertions(+), 12 deletions(-) diff --git a/ui/core/app/components/form/auth-method/index.hbs b/ui/core/app/components/form/auth-method/index.hbs index c2dccd6b6..7c1bc5162 100644 --- a/ui/core/app/components/form/auth-method/index.hbs +++ b/ui/core/app/components/form/auth-method/index.hbs @@ -6,7 +6,7 @@ as |form| > 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 index 0d9a5ca5e..580249d53 100644 --- 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 @@ -1,9 +1,9 @@ -

- {{t "resources.auth-method"}} - {{@model.type}} -

+

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

+ + {{t (concat 'auth.method-types.' @model.type)}} +
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 61c7a08e4..3cf07c9af 100644 --- a/ui/core/app/templates/scopes/scope/auth-methods/index.hbs +++ b/ui/core/app/templates/scopes/scope/auth-methods/index.hbs @@ -27,17 +27,19 @@ {{#each @model as |authMethod|}} - - {{authMethod.name}} - +

+ + {{authMethod.name}} + +

{{authMethod.description}}
- {{authMethod.type}} + {{t (concat 'auth.method-types.' authMethod.type)}} {{authMethod.id}} diff --git a/ui/core/tests/integration/components/form/auth-method-test.js b/ui/core/tests/integration/components/form/auth-method-test.js index e7320e927..267e516b5 100644 --- a/ui/core/tests/integration/components/form/auth-method-test.js +++ b/ui/core/tests/integration/components/form/auth-method-test.js @@ -9,9 +9,11 @@ module('Integration | Component | form/auth-method', function (hooks) { test('it renders', async function (assert) { this.submit = () => {}; this.cancel = () => {}; + this.model = { type: 'password' }; await render(hbs` `); From 99169e7ff982e1bf80b8f49e944b9b8f3b84a555 Mon Sep 17 00:00:00 2001 From: Susmitha Girumala Date: Thu, 13 Aug 2020 17:54:02 -0400 Subject: [PATCH 08/13] chore: Add 'New' button to auth-methods welcome message --- ui/core/app/templates/scopes/scope/auth-methods/index.hbs | 4 ++++ 1 file changed, 4 insertions(+) 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 3cf07c9af..5b4fffc2a 100644 --- a/ui/core/app/templates/scopes/scope/auth-methods/index.hbs +++ b/ui/core/app/templates/scopes/scope/auth-methods/index.hbs @@ -55,6 +55,10 @@ {{t 'descriptions.auth-methods-welcome'}} + + + {{t 'titles.new'}} + From 252e5764f1b417ac67fecc09a41c29ef77c78893 Mon Sep 17 00:00:00 2001 From: Susmitha Girumala Date: Thu, 13 Aug 2020 18:04:27 -0400 Subject: [PATCH 09/13] chore: Add breadcrumbs to auth-methods views --- .../controllers/scopes/scope/auth-methods.js | 18 ++++++++++++++++++ .../scopes/scope/auth-methods/auth-method.js | 13 +++++++++++++ .../scopes/scope/auth-methods/new.js | 18 ++++++++++++++++++ .../scope/auth-methods/auth-method/index.hbs | 4 ++++ .../scopes/scope/auth-methods/index.hbs | 4 ++++ .../scopes/scope/auth-methods/new.hbs | 4 ++++ .../scopes/scope/auth-methods-test.js | 12 ++++++++++++ .../scope/auth-methods/auth-method-test.js | 12 ++++++++++++ .../scopes/scope/auth-methods/new-test.js | 12 ++++++++++++ 9 files changed, 97 insertions(+) create mode 100644 ui/core/app/controllers/scopes/scope/auth-methods.js create mode 100644 ui/core/app/controllers/scopes/scope/auth-methods/auth-method.js create mode 100644 ui/core/app/controllers/scopes/scope/auth-methods/new.js create mode 100644 ui/core/tests/unit/controllers/scopes/scope/auth-methods-test.js create mode 100644 ui/core/tests/unit/controllers/scopes/scope/auth-methods/auth-method-test.js create mode 100644 ui/core/tests/unit/controllers/scopes/scope/auth-methods/new-test.js 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/templates/scopes/scope/auth-methods/auth-method/index.hbs b/ui/core/app/templates/scopes/scope/auth-methods/auth-method/index.hbs index 580249d53..c08c3fd4a 100644 --- 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 @@ -6,6 +6,10 @@ + + + + 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 5b4fffc2a..2d772bcd6 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,10 @@

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

+ + + + {{#if @model}} diff --git a/ui/core/app/templates/scopes/scope/auth-methods/new.hbs b/ui/core/app/templates/scopes/scope/auth-methods/new.hbs index 315156454..44c4f35d6 100644 --- a/ui/core/app/templates/scopes/scope/auth-methods/new.hbs +++ b/ui/core/app/templates/scopes/scope/auth-methods/new.hbs @@ -5,6 +5,10 @@

{{t "titles.new"}}

+ + + + Date: Fri, 14 Aug 2020 10:29:24 -0400 Subject: [PATCH 10/13] =?UTF-8?q?chore:=20=F0=9F=A4=96=20add=20adapter=20t?= =?UTF-8?q?o=20generate=20URLs=20for=20host=20sets?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- addons/api/addon/adapters/host-set.js | 38 +++++++++++++++++++ addons/api/addon/mixins/adapter-build-url.js | 38 +++++++++++++------ addons/api/app/adapters/host-set.js | 1 + .../api/tests/unit/adapters/host-set-test.js | 28 ++++++++++++++ 4 files changed, 93 insertions(+), 12 deletions(-) create mode 100644 addons/api/addon/adapters/host-set.js create mode 100644 addons/api/app/adapters/host-set.js create mode 100644 addons/api/tests/unit/adapters/host-set-test.js diff --git a/addons/api/addon/adapters/host-set.js b/addons/api/addon/adapters/host-set.js new file mode 100644 index 000000000..fbbe11495 --- /dev/null +++ b/addons/api/addon/adapters/host-set.js @@ -0,0 +1,38 @@ +import ApplicationAdapter from './application'; +import { inject as service } from '@ember/service'; + +export default class HostSetAdapter extends ApplicationAdapter { + + // =services + + @service store; + + // =methods + + /** + * Append a host catalog URL ahead of the host sets path if `hostCatalogID` is + * passed via adapter options. Otherwise, generate the prefix as normal. + * + * TODO: generalize the interface to resource-prefixed URLs by accepting + * a modelName and an ID, rather than looking for a known key. + * + * @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) { + const hostCatalogID = snapshot?.adapterOptions?.hostCatalogID; + if (hostCatalogID) { + const hostCatalogAdapter = this.store.adapterFor('host-catalog'); + return hostCatalogAdapter + ._buildPrefixedURL('host-catalog', hostCatalogID, snapshot); + } else { + return super.urlPrefix(...arguments); + } + } + +} diff --git a/addons/api/addon/mixins/adapter-build-url.js b/addons/api/addon/mixins/adapter-build-url.js index 264364d0d..cee278e05 100644 --- a/addons/api/addon/mixins/adapter-build-url.js +++ b/addons/api/addon/mixins/adapter-build-url.js @@ -46,6 +46,31 @@ export default Mixin.create({ return ''; }, + /** + * Builds the (mostly full) URL with a prefix, but not the suffix. + * The `_buildURL` method adds a suffix. + * Do not call directly. + * @override + * @param {String} modelName + * @param {String} id + * @return {String} url + */ + _buildPrefixedURL(modelName, id, snapshot) { + let path; + let url = []; + const host = get(this, 'host'); + const prefix = this.urlPrefix(null, null, 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}`; + }, + /** * Overrides the default `_buildURL` with a nearly identical version. * This method simply passes arguments into the `urlPrefix` method for @@ -58,19 +83,8 @@ export default Mixin.create({ * @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 url = this._buildPrefixedURL(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/app/adapters/host-set.js b/addons/api/app/adapters/host-set.js new file mode 100644 index 000000000..958a53f64 --- /dev/null +++ b/addons/api/app/adapters/host-set.js @@ -0,0 +1 @@ +export { default } from 'api/adapters/host-set'; diff --git a/addons/api/tests/unit/adapters/host-set-test.js b/addons/api/tests/unit/adapters/host-set-test.js new file mode 100644 index 000000000..e47c7a7d6 --- /dev/null +++ b/addons/api/tests/unit/adapters/host-set-test.js @@ -0,0 +1,28 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Adapter | host set', function(hooks) { + setupTest(hooks); + + test('it generates correct complete URLs', function (assert) { + assert.expect(2); + const scopeID = 'o_1'; + const hostCatalogID = 'hc_098'; + const method = 'my-custom-method'; + const mockSnapshot = { + adapterOptions: { + scopeID, + method + } + }; + const adapter = this.owner.lookup('adapter:host-set'); + // Without hostCatalogID... + let url = adapter.buildURL('host-set', null, mockSnapshot, 'findAll'); + assert.equal(url, '/v1/scopes/o_1/host-sets:my-custom-method'); + // With hostCatalogID... + mockSnapshot.adapterOptions.hostCatalogID = hostCatalogID; + url = adapter.buildURL('host-set', null, mockSnapshot, 'findAll'); + assert.equal(url, '/v1/scopes/o_1/host-catalogs/hc_098/host-sets:my-custom-method'); + }); + +}); From c7f1b6830e2e874ee470e62eb65a27b1abf0cdd1 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Fri, 14 Aug 2020 10:46:17 -0400 Subject: [PATCH 11/13] =?UTF-8?q?chore:=20=F0=9F=A4=96=20add=20host=20set?= =?UTF-8?q?=20mocks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../project/host-catalogs/host-catalog.js | 16 ++++++++++++++++ ui/core/mirage/config.js | 8 ++++++++ ui/core/mirage/factories/host-catalog.js | 15 ++++++++++++++- ui/core/mirage/factories/host-set.js | 3 +++ ui/core/mirage/factories/scope.js | 2 +- ui/core/mirage/generated/factories/host-set.js | 17 +++++++++++++++++ ui/core/mirage/scenarios/default.js | 2 +- 7 files changed, 60 insertions(+), 3 deletions(-) create mode 100644 ui/core/mirage/factories/host-set.js create mode 100644 ui/core/mirage/generated/factories/host-set.js 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 index f54947470..d25608633 100644 --- 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 @@ -1,4 +1,20 @@ import Route from '@ember/routing/route'; export default class ScopesScopeProjectsProjectHostCatalogsHostCatalogRoute extends Route { + + // =methods + + /** + * Load a host catalog. + * @param {object} params + * @param {string} params.host_catalog_id + * @return {HostCatalogModel} + */ + async model({ host_catalog_id }) { + const { id: scopeID } = this.modelFor('scopes.scope'); + return this.store.findRecord('host-catalog', host_catalog_id, { + adapterOptions: { scopeID }, + }); + } + } diff --git a/ui/core/mirage/config.js b/ui/core/mirage/config.js index 73342c6b0..89159b0e0 100644 --- a/ui/core/mirage/config.js +++ b/ui/core/mirage/config.js @@ -88,6 +88,14 @@ export default function() { this.patch('/scopes/:scope_id/host-catalogs/:id'); this.del('/scopes/:scope_id/host-catalogs/:id'); + // host-set + + this.get('/scopes/:scope_id/host-catalogs/:host_catalog_id/host-sets'); + this.post('/scopes/:scope_id/host-catalogs/:host_catalog_id/host-sets'); + this.get('/scopes/:scope_id/host-catalogs/:host_catalog_id/host-sets/:id'); + this.patch('/scopes/:scope_id/host-catalogs/:host_catalog_id/host-sets/:id'); + this.del('/scopes/:scope_id/host-catalogs/:host_catalog_id/host-sets/:id'); + // Uncomment the following line and the Response import above // Then change the response code to simulate error responses. // this.get('/scopes/:scope_id/projects', () => new Response(505)); diff --git a/ui/core/mirage/factories/host-catalog.js b/ui/core/mirage/factories/host-catalog.js index ffd635036..d2f84b3c5 100644 --- a/ui/core/mirage/factories/host-catalog.js +++ b/ui/core/mirage/factories/host-catalog.js @@ -1,3 +1,16 @@ import factory from '../generated/factories/host-catalog'; +import { trait } from 'ember-cli-mirage'; -export default factory.extend(); +export default factory.extend({ + + /** + * Create catalogs with associated host sets and hosts. + */ + withChildren: trait({ + afterCreate(record, server) { + const host_set_ids = server.createList('host-set', 3).map(set => set.id); + record.update({ host_set_ids }); + } + }) + +}); diff --git a/ui/core/mirage/factories/host-set.js b/ui/core/mirage/factories/host-set.js new file mode 100644 index 000000000..2f12361c6 --- /dev/null +++ b/ui/core/mirage/factories/host-set.js @@ -0,0 +1,3 @@ +import factory from '../generated/factories/host-set'; + +export default factory.extend(); diff --git a/ui/core/mirage/factories/scope.js b/ui/core/mirage/factories/scope.js index 703e80ad4..d60486d15 100644 --- a/ui/core/mirage/factories/scope.js +++ b/ui/core/mirage/factories/scope.js @@ -14,7 +14,7 @@ export default factory.extend({ }, 'withChildren'); } if (record.type === 'org') { - server.createList('scope', 5, { + server.createList('scope', 3, { type: 'project', scope: { id: record.id, type: record.type } }); diff --git a/ui/core/mirage/generated/factories/host-set.js b/ui/core/mirage/generated/factories/host-set.js new file mode 100644 index 000000000..95636bda0 --- /dev/null +++ b/ui/core/mirage/generated/factories/host-set.js @@ -0,0 +1,17 @@ +import { Factory } from 'ember-cli-mirage'; +import { random, date } from 'faker'; + +/** + * GeneratedHostSetModelFactory + */ +export default Factory.extend({ + + type: 'static', + name: () => random.words(), + description: () => random.words(), + created_time: () => date.recent(), + updated_time: () => date.recent(), + version: () => random.number(), + size: () => random.number(), + +}); diff --git a/ui/core/mirage/scenarios/default.js b/ui/core/mirage/scenarios/default.js index 6f8e29775..9215e8a80 100644 --- a/ui/core/mirage/scenarios/default.js +++ b/ui/core/mirage/scenarios/default.js @@ -27,7 +27,7 @@ export default function(server) { server.db.scopes.where({type: 'project'}).forEach(projectScope => { const scope = { id: projectScope.id, type: projectScope.type }; - server.createList('host-catalog', 2, { scope }); + server.createList('host-catalog', 2, { scope }, 'withChildren'); }); } From acafc176cc5aa452b2afbe1338e1cab8971b346a Mon Sep 17 00:00:00 2001 From: Susmitha Girumala Date: Fri, 14 Aug 2020 10:49:22 -0400 Subject: [PATCH 12/13] chore: Wrap auth-method description in a paragraph tag --- .../scopes/scope/auth-methods/index.hbs | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) 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 5b4fffc2a..e49f040ba 100644 --- a/ui/core/app/templates/scopes/scope/auth-methods/index.hbs +++ b/ui/core/app/templates/scopes/scope/auth-methods/index.hbs @@ -27,15 +27,13 @@ {{#each @model as |authMethod|}} -

- - {{authMethod.name}} - -

- {{authMethod.description}} + + {{authMethod.name}} + +

{{authMethod.description}}

From b86f05e9770814ff9fd2d135314062b9f17e1cc5 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Fri, 14 Aug 2020 15:16:35 -0400 Subject: [PATCH 13/13] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20add=20host=20API?= =?UTF-8?q?=20support=20and=20mocks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- addons/api/addon/adapters/host.js | 38 +++++++++++++++++++ addons/api/addon/generated/models/host.js | 42 +++++++++++++++++++++ addons/api/addon/models/host-catalog.js | 10 +---- addons/api/addon/models/host-set.js | 12 +----- addons/api/addon/models/host.js | 3 ++ addons/api/app/adapters/host.js | 1 + addons/api/app/models/host.js | 1 + addons/api/tests/unit/adapters/host-test.js | 28 ++++++++++++++ addons/api/tests/unit/models/host-test.js | 13 +++++++ ui/core/mirage/config.js | 8 ++++ ui/core/mirage/factories/host-catalog.js | 15 +------- ui/core/mirage/factories/host.js | 3 ++ ui/core/mirage/generated/factories/host.js | 17 +++++++++ ui/core/mirage/scenarios/default.js | 10 +++-- 14 files changed, 164 insertions(+), 37 deletions(-) create mode 100644 addons/api/addon/adapters/host.js create mode 100644 addons/api/addon/generated/models/host.js create mode 100644 addons/api/addon/models/host.js create mode 100644 addons/api/app/adapters/host.js create mode 100644 addons/api/app/models/host.js create mode 100644 addons/api/tests/unit/adapters/host-test.js create mode 100644 addons/api/tests/unit/models/host-test.js create mode 100644 ui/core/mirage/factories/host.js create mode 100644 ui/core/mirage/generated/factories/host.js diff --git a/addons/api/addon/adapters/host.js b/addons/api/addon/adapters/host.js new file mode 100644 index 000000000..ae1747b06 --- /dev/null +++ b/addons/api/addon/adapters/host.js @@ -0,0 +1,38 @@ +import ApplicationAdapter from './application'; +import { inject as service } from '@ember/service'; + +export default class HostAdapter extends ApplicationAdapter { + + // =services + + @service store; + + // =methods + + /** + * Append a host catalog URL ahead of the host path if `hostCatalogID` is + * passed via adapter options. Otherwise, generate the prefix as normal. + * + * TODO: generalize the interface to resource-prefixed URLs by accepting + * a modelName and an ID, rather than looking for a known key. + * + * @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) { + const hostCatalogID = snapshot?.adapterOptions?.hostCatalogID; + if (hostCatalogID) { + const hostCatalogAdapter = this.store.adapterFor('host-catalog'); + return hostCatalogAdapter + ._buildPrefixedURL('host-catalog', hostCatalogID, snapshot); + } else { + return super.urlPrefix(...arguments); + } + } + +} diff --git a/addons/api/addon/generated/models/host.js b/addons/api/addon/generated/models/host.js new file mode 100644 index 000000000..0d9e0dd2d --- /dev/null +++ b/addons/api/addon/generated/models/host.js @@ -0,0 +1,42 @@ +import BaseModel from '../../models/base'; +import { attr } from '@ember-data/model'; + +/** + * Host contains all fields related to a Host resource + */ +export default class GeneratedHostModel extends BaseModel { + + // =attributes + + @attr('string', { + description: 'The type of the resource, to help differentiate schemas' + }) 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; + + @attr('number', { + description: 'Current version number of this resource.', + }) + version; + + @attr('string', { + description: 'The address (DNS or IP name) used to reach the host' + }) address; + +} diff --git a/addons/api/addon/models/host-catalog.js b/addons/api/addon/models/host-catalog.js index 6f3729a47..894ee9cf1 100644 --- a/addons/api/addon/models/host-catalog.js +++ b/addons/api/addon/models/host-catalog.js @@ -1,11 +1,3 @@ import GeneratedHostCatalogModel from '../generated/models/host-catalog'; -import { hasMany } from '@ember-data/model'; -export default class HostCatalogModel extends GeneratedHostCatalogModel { - - /** - * @type {HostSetModel[]} - */ - @hasMany('host-set') hostSets; - -} +export default class HostCatalogModel extends GeneratedHostCatalogModel {} diff --git a/addons/api/addon/models/host-set.js b/addons/api/addon/models/host-set.js index ad1fd89a6..01a376f6b 100644 --- a/addons/api/addon/models/host-set.js +++ b/addons/api/addon/models/host-set.js @@ -1,13 +1,3 @@ import GeneratedHostSetModel from '../generated/models/host-set'; -import { belongsTo } from '@ember-data/model'; -export default class HostSetModel extends GeneratedHostSetModel { - - // =relationships - - /** - * @type {HostCatalogModel} - */ - @belongsTo('host-catalog') hostCatalog; - -} +export default class HostSetModel extends GeneratedHostSetModel {} diff --git a/addons/api/addon/models/host.js b/addons/api/addon/models/host.js new file mode 100644 index 000000000..451097080 --- /dev/null +++ b/addons/api/addon/models/host.js @@ -0,0 +1,3 @@ +import GeneratedHostModel from '../generated/models/host'; + +export default class HostModel extends GeneratedHostModel {} diff --git a/addons/api/app/adapters/host.js b/addons/api/app/adapters/host.js new file mode 100644 index 000000000..46bc96d3c --- /dev/null +++ b/addons/api/app/adapters/host.js @@ -0,0 +1 @@ +export { default } from 'api/adapters/host'; diff --git a/addons/api/app/models/host.js b/addons/api/app/models/host.js new file mode 100644 index 000000000..ce1f67327 --- /dev/null +++ b/addons/api/app/models/host.js @@ -0,0 +1 @@ +export { default } from 'api/models/host'; diff --git a/addons/api/tests/unit/adapters/host-test.js b/addons/api/tests/unit/adapters/host-test.js new file mode 100644 index 000000000..9e019284e --- /dev/null +++ b/addons/api/tests/unit/adapters/host-test.js @@ -0,0 +1,28 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Adapter | host', function(hooks) { + setupTest(hooks); + + test('it generates correct complete URLs', function (assert) { + assert.expect(2); + const scopeID = 'o_1'; + const hostCatalogID = 'hc_098'; + const method = 'my-custom-method'; + const mockSnapshot = { + adapterOptions: { + scopeID, + method + } + }; + const adapter = this.owner.lookup('adapter:host'); + // Without hostCatalogID... + let url = adapter.buildURL('host', null, mockSnapshot, 'findAll'); + assert.equal(url, '/v1/scopes/o_1/hosts:my-custom-method'); + // With hostCatalogID... + mockSnapshot.adapterOptions.hostCatalogID = hostCatalogID; + url = adapter.buildURL('host', null, mockSnapshot, 'findAll'); + assert.equal(url, '/v1/scopes/o_1/host-catalogs/hc_098/hosts:my-custom-method'); + }); + +}); diff --git a/addons/api/tests/unit/models/host-test.js b/addons/api/tests/unit/models/host-test.js new file mode 100644 index 000000000..d19d98fc3 --- /dev/null +++ b/addons/api/tests/unit/models/host-test.js @@ -0,0 +1,13 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Model | host', 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('host', {}); + assert.ok(model); + }); +}); diff --git a/ui/core/mirage/config.js b/ui/core/mirage/config.js index 89159b0e0..4a949b05f 100644 --- a/ui/core/mirage/config.js +++ b/ui/core/mirage/config.js @@ -88,6 +88,14 @@ export default function() { this.patch('/scopes/:scope_id/host-catalogs/:id'); this.del('/scopes/:scope_id/host-catalogs/:id'); + // host + + this.get('/scopes/:scope_id/host-catalogs/:host_catalog_id/hosts'); + this.post('/scopes/:scope_id/host-catalogs/:host_catalog_id/hosts'); + this.get('/scopes/:scope_id/host-catalogs/:host_catalog_id/hosts/:id'); + this.patch('/scopes/:scope_id/host-catalogs/:host_catalog_id/hosts/:id'); + this.del('/scopes/:scope_id/host-catalogs/:host_catalog_id/hosts/:id'); + // host-set this.get('/scopes/:scope_id/host-catalogs/:host_catalog_id/host-sets'); diff --git a/ui/core/mirage/factories/host-catalog.js b/ui/core/mirage/factories/host-catalog.js index d2f84b3c5..ffd635036 100644 --- a/ui/core/mirage/factories/host-catalog.js +++ b/ui/core/mirage/factories/host-catalog.js @@ -1,16 +1,3 @@ import factory from '../generated/factories/host-catalog'; -import { trait } from 'ember-cli-mirage'; -export default factory.extend({ - - /** - * Create catalogs with associated host sets and hosts. - */ - withChildren: trait({ - afterCreate(record, server) { - const host_set_ids = server.createList('host-set', 3).map(set => set.id); - record.update({ host_set_ids }); - } - }) - -}); +export default factory.extend(); diff --git a/ui/core/mirage/factories/host.js b/ui/core/mirage/factories/host.js new file mode 100644 index 000000000..eb463f1f9 --- /dev/null +++ b/ui/core/mirage/factories/host.js @@ -0,0 +1,3 @@ +import factory from '../generated/factories/host'; + +export default factory.extend(); diff --git a/ui/core/mirage/generated/factories/host.js b/ui/core/mirage/generated/factories/host.js new file mode 100644 index 000000000..2d6925e82 --- /dev/null +++ b/ui/core/mirage/generated/factories/host.js @@ -0,0 +1,17 @@ +import { Factory } from 'ember-cli-mirage'; +import { random, date, internet } from 'faker'; + +/** + * GeneratedHostModelFactory + */ +export default Factory.extend({ + + type: 'static', + name: () => random.words(), + description: () => random.words(), + created_time: () => date.recent(), + updated_time: () => date.recent(), + version: () => random.number(), + address: () => internet.ipv6(), + +}); diff --git a/ui/core/mirage/scenarios/default.js b/ui/core/mirage/scenarios/default.js index 9215e8a80..2e830c4ce 100644 --- a/ui/core/mirage/scenarios/default.js +++ b/ui/core/mirage/scenarios/default.js @@ -25,9 +25,13 @@ export default function(server) { // Other resources - server.db.scopes.where({type: 'project'}).forEach(projectScope => { - const scope = { id: projectScope.id, type: projectScope.type }; - server.createList('host-catalog', 2, { scope }, 'withChildren'); + server.db.scopes.where({type: 'project'}).forEach(({ id, type }) => { + const scope = { id, type }; + server.createList('host-catalog', 2, { scope }); + // Hosts and host sets are supposed to be associated to a host catalog. + // For simplicity of mocks, we don't actually associate them here. + server.createList('host-set', 3, { scope }); + server.createList('host', 10, { scope }); }); }