diff --git a/addons/api/addon/models/host-catalog.js b/addons/api/addon/models/host-catalog.js index ec34b50e3..08e1a7d82 100644 --- a/addons/api/addon/models/host-catalog.js +++ b/addons/api/addon/models/host-catalog.js @@ -26,7 +26,7 @@ export default class HostCatalogModel extends GeneratedHostCatalogModel { * @type {boolean} */ get isUnknown() { - return this.isPlugin && !types.includes(this.plugin.name); + return this.isPlugin && !types.includes(this.plugin?.name); } /** diff --git a/addons/core/translations/form/en-us.yaml b/addons/core/translations/form/en-us.yaml index 05105fbac..87beceb23 100644 --- a/addons/core/translations/form/en-us.yaml +++ b/addons/core/translations/form/en-us.yaml @@ -118,3 +118,13 @@ callback_url: filter: label: Filter help: A boolean expression defining a filter run against the provided information. +tenant_id: + label: Tenant ID +subscription_id: + label: Subscription ID +access_key_id: + label: Access Key ID +secret_access_key: + label: Secret Access Key +region: + label: Region \ No newline at end of file diff --git a/addons/core/translations/resources/en-us.yaml b/addons/core/translations/resources/en-us.yaml index 4ac6c4023..27e3497c0 100644 --- a/addons/core/translations/resources/en-us.yaml +++ b/addons/core/translations/resources/en-us.yaml @@ -78,16 +78,32 @@ host-catalog: title: Host Catalog title_plural: Host Catalogs description: A host catalog is a collection of hosts and host sets. + type: Type messages: welcome: title: Welcome to Host Catalogs actions: delete: Delete Host Catalog types: - static: Static - unknown: Unknown - aws: AWS - azure: Azure + static: + label: Static + help: Provide a list of hosts and host sets with static addresses + unknown: + label: Unknown + aws: + label: AWS + azure: + label: Azure + dynamic: + label: Dynamic + help: Automate discovery of target hosts and services on Azure or AWS + form: + provider: + label: Provider Details + description: This is the provider for this host catalog. The format of the host set filters are specific to each provider. + choose_a_provider: + label: Choose a provider + description: There are several built-in plugins for the most common providers. You can choose from on of the included plugins or use a custom plugin. host-set: title: Host Set title_plural: Host Sets diff --git a/addons/rose/addon/styles/rose/components/form/_radio-card.scss b/addons/rose/addon/styles/rose/components/form/_radio-card.scss index 5e8018b40..d8b69c896 100644 --- a/addons/rose/addon/styles/rose/components/form/_radio-card.scss +++ b/addons/rose/addon/styles/rose/components/form/_radio-card.scss @@ -61,7 +61,7 @@ $radio-svg: ( display: inline-flex; align-items: center; justify-content: center; - margin: $size-m $size-xs $size-xl 0; + margin: $size-m $size-xs $size-m 0; .radio-card-overlay { position: absolute; diff --git a/ui/admin/app/components/form/host-catalog/aws/index.hbs b/ui/admin/app/components/form/host-catalog/aws/index.hbs new file mode 100644 index 000000000..d343e3cdd --- /dev/null +++ b/ui/admin/app/components/form/host-catalog/aws/index.hbs @@ -0,0 +1,150 @@ + + + {{#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}} + + + {{#if @model.isNew}} + + <:title>{{t 'form.type.label'}} + <:body> + + + + + + + + + + <:title>{{t 'resources.host-catalog.form.provider.label'}} + + + + <:title>{{t + 'resources.host-catalog.form.provider.choose_a_provider.label' + }} + <:description>{{t + 'resources.host-catalog.form.provider.choose_a_provider.description' + }} + + <:body> + + + + + + + {{else}} + + <:title>{{t 'resources.host-catalog.form.provider.label'}} + <:description>{{t + 'resources.host-catalog.form.provider.description' + }} + <:body> + + + + + + {{/if}} + + + + {{#if (can 'save model' @model)}} + + {{/if}} + \ No newline at end of file diff --git a/ui/admin/app/components/form/host-catalog/azure/index.hbs b/ui/admin/app/components/form/host-catalog/azure/index.hbs new file mode 100644 index 000000000..468416242 --- /dev/null +++ b/ui/admin/app/components/form/host-catalog/azure/index.hbs @@ -0,0 +1,156 @@ + + + {{#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}} + + + {{#if @model.isNew}} + {{!-- {{t + 'resources.host-catalog.form.provider.label' + }} --}} + + + <:title>{{t 'form.type.label'}} + <:body> + + + + + + + + + + <:title>{{t 'resources.host-catalog.form.provider.label'}} + + + <:title>{{t + 'resources.host-catalog.form.provider.choose_a_provider.label' + }} + <:description>{{t + 'resources.host-catalog.form.provider.choose_a_provider.description' + }} + + <:body> + + + + + + + {{else}} + + + <:title>{{t 'resources.host-catalog.form.provider.label'}} + <:description>{{t + 'resources.host-catalog.form.provider.description' + }} + + <:body> + + + + + + {{/if}} + + + + {{#if (can 'save model' @model)}} + + {{/if}} + + \ No newline at end of file diff --git a/ui/admin/app/components/form/host-catalog/index.hbs b/ui/admin/app/components/form/host-catalog/index.hbs index 39571b820..90cebebc9 100644 --- a/ui/admin/app/components/form/host-catalog/index.hbs +++ b/ui/admin/app/components/form/host-catalog/index.hbs @@ -1,56 +1,7 @@ - - - - {{#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}} - - {{#if (can 'save model' @model)}} - - {{/if}} - \ No newline at end of file +{{component + (concat 'form/host-catalog/' @model.compositeType) + model=@model + submit=@submit + cancel=@cancel + changeType=@changeType +}} \ No newline at end of file diff --git a/ui/admin/app/components/form/host-catalog/static/index.hbs b/ui/admin/app/components/form/host-catalog/static/index.hbs new file mode 100644 index 000000000..bc0ac2421 --- /dev/null +++ b/ui/admin/app/components/form/host-catalog/static/index.hbs @@ -0,0 +1,120 @@ + + + {{#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}} + + + {{#if @model.isNew}} + + <:title>{{t 'form.type.label'}} + <:body> + + + + + + + + {{#if @model.isPlugin}} + + + <:title>{{t 'resources.host-catalog.form.provider.label'}} + + + + <:title>{{t + 'resources.host-catalog.form.provider.choose_a_provider.label' + }} + <:description>{{t + 'resources.host-catalog.form.provider.choose_a_provider.description' + }} + + <:body> + + + + + + + {{/if}} + {{/if}} + + {{#if (can 'save model' @model)}} + + {{/if}} + \ No newline at end of file diff --git a/ui/admin/app/controllers/scopes/scope/host-catalogs/new.js b/ui/admin/app/controllers/scopes/scope/host-catalogs/new.js new file mode 100644 index 000000000..182b9c423 --- /dev/null +++ b/ui/admin/app/controllers/scopes/scope/host-catalogs/new.js @@ -0,0 +1,7 @@ +import Controller from '@ember/controller'; + +export default class ScopesScopeHostCatalogsNewController extends Controller { + // =attributes + + queryParams = ['type']; +} diff --git a/ui/admin/app/routes/scopes/scope/host-catalogs/new.js b/ui/admin/app/routes/scopes/scope/host-catalogs/new.js index 9af8baaef..588ecac90 100644 --- a/ui/admin/app/routes/scopes/scope/host-catalogs/new.js +++ b/ui/admin/app/routes/scopes/scope/host-catalogs/new.js @@ -1,17 +1,53 @@ import Route from '@ember/routing/route'; +import { action } from '@ember/object'; +import { inject as service } from '@ember/service'; export default class ScopesScopeHostCatalogsNewRoute extends Route { + // =services + + @service router; + + // =attributes + + queryParams = { + type: { + refreshModel: true, + }, + }; + // =methods + /** + * Rollback/destroy any new, unsaved instances from this route before + * creating another. + */ + beforeModel() { + if (this.currentModel?.isNew) this.currentModel.rollbackAttributes(); + } + /** * Creates a new unsaved host catalog belonging to the current scope. * @return {HostCatalogModel} */ model() { const scopeModel = this.modelFor('scopes.scope'); - return this.store.createRecord('host-catalog', { - type: 'static', - scopeModel, - }); + return this.store.createRecord('host-catalog', { scopeModel }); + } + + afterModel(model, transition) { + this.changeType(transition.to.queryParams?.type); + } + + /** + * Update type of host catalog + * @param {string} type + */ + @action + async changeType(type) { + const model = this.modelFor('scopes.scope.host-catalogs.new'); + if (!type) type = 'static'; // Unknown host catalog type defaults to 'static' + if (model.isUnknown) type = 'aws'; //Is this the default case for plugin type? + model.compositeType = type; + await this.router.replaceWith({ queryParams: { type } }); } } diff --git a/ui/admin/app/templates/scopes/scope/host-catalogs/index.hbs b/ui/admin/app/templates/scopes/scope/host-catalogs/index.hbs index f7def8b62..79e33a11c 100644 --- a/ui/admin/app/templates/scopes/scope/host-catalogs/index.hbs +++ b/ui/admin/app/templates/scopes/scope/host-catalogs/index.hbs @@ -62,6 +62,7 @@ (concat 'resources.host-catalog.types.' hostCatalog.compositeType + '.label' ) }} @@ -71,6 +72,7 @@ (concat 'resources.host-catalog.types.' hostCatalog.compositeType + '.label' ) }} @@ -86,6 +88,7 @@ (concat 'resources.host-catalog.types.' hostCatalog.compositeType + '.label' ) }} diff --git a/ui/admin/app/templates/scopes/scope/host-catalogs/new.hbs b/ui/admin/app/templates/scopes/scope/host-catalogs/new.hbs index fb936598e..68fad6fd1 100644 --- a/ui/admin/app/templates/scopes/scope/host-catalogs/new.hbs +++ b/ui/admin/app/templates/scopes/scope/host-catalogs/new.hbs @@ -18,6 +18,7 @@ @model={{@model}} @submit={{route-action 'save' @model}} @cancel={{route-action 'cancel' @model}} + @changeType={{route-action 'changeType'}} /> diff --git a/ui/admin/tests/acceptance/host-catalogs/create-test.js b/ui/admin/tests/acceptance/host-catalogs/create-test.js index b47b3b4ea..19fc1dd3d 100644 --- a/ui/admin/tests/acceptance/host-catalogs/create-test.js +++ b/ui/admin/tests/acceptance/host-catalogs/create-test.js @@ -31,6 +31,8 @@ module('Acceptance | host-catalogs | create', function (hooks) { hostCatalogs: null, hostCatalog: null, newHostCatalog: null, + newStaticHostCatalog: null, + newDynamicHostCatalog: null, }; hooks.beforeEach(function () { @@ -64,6 +66,8 @@ module('Acceptance | host-catalogs | create', function (hooks) { urls.hostCatalog = `${urls.hostCatalogs}/${instances.hostCatalog.id}`; urls.newHostCatalog = `${urls.hostCatalogs}/new`; + urls.newStaticHostCatalog = `${urls.newHostCatalog}?type=static`; + urls.newDynamicHostCatalog = `${urls.newHostCatalog}?type=aws`; // Generate resource couner gethostCatalogCount = () => this.server.schema.hostCatalogs.all().models.length; @@ -71,26 +75,49 @@ module('Acceptance | host-catalogs | create', function (hooks) { authenticateSession({}); }); - test('Users can create new host catalogs', async function (assert) { + test('Users can create new static host catalogs', async function (assert) { assert.expect(1); const count = gethostCatalogCount(); - await visit(urls.newHostCatalog); + await visit(urls.newStaticHostCatalog); await fillIn('[name="name"]', 'random string'); + await fillIn('[name="description"]', 'random string'); + await fillIn('[name="types"]', 'static'); await click('[type="submit"]'); assert.equal(gethostCatalogCount(), count + 1); }); - test('Users can cancel create new host catalogs', async function (assert) { + test('Users can create new dynamic host catalogs', async function (assert) { + assert.expect(1); + const count = gethostCatalogCount(); + await visit(urls.newDynamicHostCatalog); + await fillIn('[name="name"]', 'random string'); + await fillIn('[name="description"]', 'random string'); + await fillIn('[name="types"]', 'aws'); + await click('[type="submit"]'); + assert.equal(gethostCatalogCount(), count + 1); + }); + + test('Users can cancel creation of new static host catalogs', async function (assert) { assert.expect(2); const count = gethostCatalogCount(); - await visit(urls.newHostCatalog); + await visit(urls.newStaticHostCatalog); await fillIn('[name="name"]', 'random string'); await click('.rose-form-actions [type="button"]'); assert.equal(currentURL(), urls.hostCatalogs); assert.equal(gethostCatalogCount(), count); }); - test('Users can navigate to new host catalogs route with proper authorization', async function (assert) { + test('Users can cancel creation of new dynamic host catalogs', async function (assert) { + assert.expect(2); + const count = gethostCatalogCount(); + await visit(urls.newDynamicHostCatalog); + await fillIn('[name="name"]', 'random string'); + await click('.rose-form-actions [type="button"]'); + assert.equal(currentURL(), urls.hostCatalogs); + assert.equal(gethostCatalogCount(), count); + }); + + test('Users can navigate to new static host catalogs route with proper authorization', async function (assert) { assert.expect(2); await visit(urls.hostCatalogs); assert.ok( @@ -101,7 +128,7 @@ module('Acceptance | host-catalogs | create', function (hooks) { assert.ok(find(`[href="${urls.newHostCatalog}"]`)); }); - test('Users cannot navigate to new host catalogs route without proper authorization', async function (assert) { + test('Users cannot navigate to new static host catalogs route without proper authorization', async function (assert) { assert.expect(2); instances.scopes.project.authorized_collection_actions['host-catalogs'] = []; @@ -111,10 +138,10 @@ module('Acceptance | host-catalogs | create', function (hooks) { 'host-catalogs' ].includes('create') ); - assert.notOk(find(`[href="${urls.newHostCatalog}"]`)); + assert.notOk(find(`[href="${urls.newStaticHostCatalog}"]`)); }); - test('saving a new host catalog with invalid fields displays error messages', async function (assert) { + test('saving a new static host catalog with invalid fields displays error messages', async function (assert) { assert.expect(2); this.server.post('/host-catalogs', () => { return new Response( @@ -135,7 +162,7 @@ module('Acceptance | host-catalogs | create', function (hooks) { } ); }); - await visit(urls.newHostCatalog); + await visit(urls.newStaticHostCatalog); await click('[type="submit"]'); assert.ok( find('[role="alert"]').textContent.trim(), diff --git a/ui/admin/tests/unit/controllers/scopes/scope/host-catalogs/new-test.js b/ui/admin/tests/unit/controllers/scopes/scope/host-catalogs/new-test.js new file mode 100644 index 000000000..c872fa28a --- /dev/null +++ b/ui/admin/tests/unit/controllers/scopes/scope/host-catalogs/new-test.js @@ -0,0 +1,14 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Controller | scopes/scope/host-catalogs/new', function (hooks) { + setupTest(hooks); + + // TODO: Replace this with your real tests. + test('it exists', function (assert) { + let controller = this.owner.lookup( + 'controller:scopes/scope/host-catalogs/new' + ); + assert.ok(controller); + }); +});