diff --git a/addons/api/addon/generated/models/auth-method.js b/addons/api/addon/generated/models/auth-method.js index 995e65efd..7dd5805c0 100644 --- a/addons/api/addon/generated/models/auth-method.js +++ b/addons/api/addon/generated/models/auth-method.js @@ -39,4 +39,9 @@ export default class GeneratedAuthMethodModel extends BaseModel { }) disabled; + @attr('number', { + description: 'Current version number of this resource.', + }) + version; + } diff --git a/addons/api/addon/generated/models/group.js b/addons/api/addon/generated/models/group.js index bce0d1076..e8c442e1b 100644 --- a/addons/api/addon/generated/models/group.js +++ b/addons/api/addon/generated/models/group.js @@ -33,4 +33,10 @@ export default class GeneratedGroupModel extends BaseModel { description: 'Whether the resource is disabled', }) disabled; + + @attr('number', { + description: 'Current version number of this resource.', + }) + version; + } diff --git a/addons/api/addon/generated/models/host-catalog.js b/addons/api/addon/generated/models/host-catalog.js index 4a8166e3c..7207048c2 100644 --- a/addons/api/addon/generated/models/host-catalog.js +++ b/addons/api/addon/generated/models/host-catalog.js @@ -38,4 +38,9 @@ export default class GeneratedHostCatalogModel extends BaseModel { description: 'Attributes specific to the catalog type' }) attributes; + @attr('number', { + description: 'Current version number of this resource.', + }) + version; + } diff --git a/addons/api/addon/generated/models/scope.js b/addons/api/addon/generated/models/scope.js index a7448b034..3c3731b3d 100644 --- a/addons/api/addon/generated/models/scope.js +++ b/addons/api/addon/generated/models/scope.js @@ -34,4 +34,9 @@ export default class GeneratedScopeModel extends BaseModel { }) updated_time; + @attr('number', { + description: 'Current version number of this resource.', + }) + version; + } diff --git a/addons/api/addon/generated/models/target.js b/addons/api/addon/generated/models/target.js new file mode 100644 index 000000000..f80ea83cc --- /dev/null +++ b/addons/api/addon/generated/models/target.js @@ -0,0 +1,41 @@ +import BaseModel from '../../models/base'; +import { attr } from '@ember-data/model'; + +/** + * Target + */ +export default class GeneratedTargetModel extends BaseModel { + + // =attributes + + @attr('string', { + description: 'The protocol used in the target' + }) protocol; + + @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('boolean', { + description: 'Whether the catalog is disabled' + }) disabled; + + @attr('number', { + description: 'Current version number of this resource.', + }) + version; +} diff --git a/addons/api/addon/generated/models/user.js b/addons/api/addon/generated/models/user.js index 98fff7c32..433faeceb 100644 --- a/addons/api/addon/generated/models/user.js +++ b/addons/api/addon/generated/models/user.js @@ -33,4 +33,9 @@ export default class GeneratedUserModel extends BaseModel { description: 'Whether the resource is disabled', }) disabled; + + @attr('number', { + description: 'Current version number of this resource.', + }) + version; } diff --git a/addons/api/addon/models/target.js b/addons/api/addon/models/target.js new file mode 100644 index 000000000..84b2658dc --- /dev/null +++ b/addons/api/addon/models/target.js @@ -0,0 +1,3 @@ +import GeneratedTargetModel from '../generated/models/target'; + +export default class TargetModel extends GeneratedTargetModel {} diff --git a/addons/api/addon/serializers/application.js b/addons/api/addon/serializers/application.js index c839eb78c..bfe422161 100644 --- a/addons/api/addon/serializers/application.js +++ b/addons/api/addon/serializers/application.js @@ -40,6 +40,10 @@ export default class ApplicationSerializer extends RESTSerializer { let value = super.serializeAttribute(...arguments); // Do not serialize read-only attributes. if (options.readOnly) delete json[key]; + // Version is sent only if it has a non-nullish value + if (key === 'version') { + if (json[key] === null || json[key] === undefined) delete json[key]; + } // Do not serialize `disabled` fields. // TODO: disabled is temporarily disabled if (key === 'disabled') delete json[key]; diff --git a/addons/api/app/models/target.js b/addons/api/app/models/target.js new file mode 100644 index 000000000..a5b0bca2f --- /dev/null +++ b/addons/api/app/models/target.js @@ -0,0 +1 @@ +export { default } from 'api/models/target'; diff --git a/addons/api/tests/unit/models/target-test.js b/addons/api/tests/unit/models/target-test.js new file mode 100644 index 000000000..78fc71e43 --- /dev/null +++ b/addons/api/tests/unit/models/target-test.js @@ -0,0 +1,13 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Model | target', 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('target', {}); + assert.ok(model); + }); +}); diff --git a/addons/api/tests/unit/serializers/application-test.js b/addons/api/tests/unit/serializers/application-test.js index 4769cfa3f..c067939b9 100644 --- a/addons/api/tests/unit/serializers/application-test.js +++ b/addons/api/tests/unit/serializers/application-test.js @@ -29,6 +29,28 @@ module('Unit | Serializer | application', function (hooks) { }); }); + test('it serializes non-nullish version fields', function (assert) { + assert.expect(2); + const store = this.owner.lookup('service:store'); + const record = store.createRecord('user', { + name: 'User', + description: 'Description', + version: null + }); + let serializedRecord = record.serialize(); + assert.deepEqual(serializedRecord, { + name: 'User', + description: 'Description' + }); + record.version = 1; + serializedRecord = record.serialize(); + assert.deepEqual(serializedRecord, { + name: 'User', + description: 'Description', + version: 1 + }); + }); + test('it normalizes array records from an `items` root key', function (assert) { assert.expect(1); const store = this.owner.lookup('service:store'); diff --git a/ui/core/app/components/form/host/index.hbs b/ui/core/app/components/form/host/index.hbs new file mode 100644 index 000000000..9152b0ba0 --- /dev/null +++ b/ui/core/app/components/form/host/index.hbs @@ -0,0 +1,55 @@ + + + + + + {{#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/components/form/target/index.hbs b/ui/core/app/components/form/target/index.hbs new file mode 100644 index 000000000..45adc26d0 --- /dev/null +++ b/ui/core/app/components/form/target/index.hbs @@ -0,0 +1,49 @@ + + + + {{#if @model.errors.name}} + + {{#each @model.errors.name as |error|}} + {{error.message}} + {{/each}} + + {{/if}} + + + + {{#if @model.errors.description}} + + {{#each @model.errors.description as |error|}} + {{error.message}} + {{/each}} + + {{/if}} + + + + + diff --git a/ui/core/app/controllers/scopes/scope/projects/project/host-catalogs/host-catalog/hosts.js b/ui/core/app/controllers/scopes/scope/projects/project/host-catalogs/host-catalog/hosts.js new file mode 100644 index 000000000..0ce0c8ba4 --- /dev/null +++ b/ui/core/app/controllers/scopes/scope/projects/project/host-catalogs/host-catalog/hosts.js @@ -0,0 +1,20 @@ +import Controller from '@ember/controller'; +import { inject as service } from '@ember/service'; + +export default class ScopesScopeProjectsProjectHostCatalogsHostCatalogHostsController extends Controller { + + // =services + + @service intl; + + // =attributes + + /** + * Hosts breadcrumb + * @type {string} + */ + get breadCrumb() { + return this.intl.t('resources.hosts'); + } + +} diff --git a/ui/core/app/controllers/scopes/scope/projects/project/host-catalogs/host-catalog/hosts/host.js b/ui/core/app/controllers/scopes/scope/projects/project/host-catalogs/host-catalog/hosts/host.js new file mode 100644 index 000000000..7b1b2883b --- /dev/null +++ b/ui/core/app/controllers/scopes/scope/projects/project/host-catalogs/host-catalog/hosts/host.js @@ -0,0 +1,15 @@ +import Controller from '@ember/controller'; + +export default class ScopesScopeProjectsProjectHostCatalogsHostCatalogHostsHostController extends Controller { + + // =attributes + + /** + * Host breadcrumb + * @type {string} + */ + get breadCrumb() { + return this.model.displayName; + } + +} diff --git a/ui/core/app/controllers/scopes/scope/projects/project/host-catalogs/host-catalog/hosts/new.js b/ui/core/app/controllers/scopes/scope/projects/project/host-catalogs/host-catalog/hosts/new.js new file mode 100644 index 000000000..cc56bcf08 --- /dev/null +++ b/ui/core/app/controllers/scopes/scope/projects/project/host-catalogs/host-catalog/hosts/new.js @@ -0,0 +1,20 @@ +import Controller from '@ember/controller'; +import { inject as service } from '@ember/service'; + +export default class ScopesScopeProjectsProjectHostCatalogsHostCatalogHostsNewController extends Controller { + + // =services + + @service intl; + + // =attributes + + /** + * Translated new host breadcrumb + * @type {string} + */ + get breadCrumb() { + return this.intl.t('titles.new'); + } + +} diff --git a/ui/core/app/controllers/scopes/scope/projects/project/targets.js b/ui/core/app/controllers/scopes/scope/projects/project/targets.js new file mode 100644 index 000000000..a0d75250f --- /dev/null +++ b/ui/core/app/controllers/scopes/scope/projects/project/targets.js @@ -0,0 +1,20 @@ +import Controller from '@ember/controller'; +import { inject as service } from '@ember/service'; + +export default class ScopesScopeProjectsProjectTargetsController extends Controller { + + // =services + + @service intl; + + // =attributes + + /** + * Targets breadcrumb + * @type {string} + */ + get breadCrumb() { + return this.intl.t('resources.targets'); + } + +} diff --git a/ui/core/app/controllers/scopes/scope/projects/project/targets/new.js b/ui/core/app/controllers/scopes/scope/projects/project/targets/new.js new file mode 100644 index 000000000..035a83756 --- /dev/null +++ b/ui/core/app/controllers/scopes/scope/projects/project/targets/new.js @@ -0,0 +1,20 @@ +import Controller from '@ember/controller'; +import { inject as service } from '@ember/service'; + +export default class ScopesScopeProjectsProjectTargetsNewController extends Controller { + + // =services + + @service intl; + + // =attributes + + /** + * Translated new target breadcrumb + * @type {string} + */ + get breadCrumb() { + return this.intl.t('titles.new'); + } + +} diff --git a/ui/core/app/controllers/scopes/scope/projects/project/targets/target.js b/ui/core/app/controllers/scopes/scope/projects/project/targets/target.js new file mode 100644 index 000000000..0da51ffa4 --- /dev/null +++ b/ui/core/app/controllers/scopes/scope/projects/project/targets/target.js @@ -0,0 +1,15 @@ +import Controller from '@ember/controller'; + +export default class ScopesScopeProjectsProjectTargetsTargetController extends Controller { + + // =attributes + + /** + * A target breadcrumb + * @type {string} + */ + get breadCrumb() { + return this.model.displayName; + } + +} diff --git a/ui/core/app/router.js b/ui/core/app/router.js index 08f7d9af6..13e46eb06 100644 --- a/ui/core/app/router.js +++ b/ui/core/app/router.js @@ -29,8 +29,16 @@ Router.map(function () { }); this.route('new'); }); + this.route('hosts', function() { + this.route('host', { path: ':host_id' }, function() {}); + this.route('new'); + }); }); }); + this.route('targets', function() { + this.route('target', { path: ':target_id' }, function() {}); + this.route('new'); + }); }); this.route('new'); }); 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 7247e9446..b9afbdf98 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 @@ -17,7 +17,7 @@ export default class ScopesScopeProjectsProjectHostCatalogsHostCatalogRoute exte }); } - /** + /** * Renders the host-catalog specific templates for header, navigation, and actions page sections. * @override * @param {object} controller diff --git a/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/host-catalog/host-sets/host-set.js b/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/host-catalog/host-sets/host-set.js index 66c552c4d..6d2b4abd1 100644 --- a/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/host-catalog/host-sets/host-set.js +++ b/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/host-catalog/host-sets/host-set.js @@ -24,7 +24,7 @@ export default class ScopesScopeProjectsProjectHostCatalogsHostCatalogHostSetsHo * @param {object} model */ renderTemplate(controller, model) { - this.render('scopes/scope/projects/project/host-catalogs/host-catalog/host-sets'); + super.renderTemplate(...arguments); this.render('scopes/scope/projects/project/host-catalogs/host-catalog/host-sets/-header', { into: 'scopes/scope/projects/project/host-catalogs/host-catalog', diff --git a/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/host-catalog/host-sets/new.js b/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/host-catalog/host-sets/new.js index 87f67abc7..f0dbcc3bd 100644 --- a/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/host-catalog/host-sets/new.js +++ b/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/host-catalog/host-sets/new.js @@ -6,7 +6,7 @@ export default class ScopesScopeProjectsProjectHostCatalogsHostCatalogHostSetsNe /** * Creates a new unsaved host set in current host catalog scope. - * @return {HostCatalogModel} + * @return {HostSetModel} */ model() { const scopeModel = this.modelFor('scopes.scope.projects.project.host-catalogs.host-catalog'); diff --git a/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/host-catalog/hosts.js b/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/host-catalog/hosts.js new file mode 100644 index 000000000..805112d67 --- /dev/null +++ b/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/host-catalog/hosts.js @@ -0,0 +1,81 @@ +import Route from '@ember/routing/route'; +import { inject as service } from '@ember/service'; +import { action } from '@ember/object'; + +export default class ScopesScopeProjectsProjectHostCatalogsHostCatalogHostsRoute extends Route { + // =services + + @service intl; + @service notify; + + // =methods + + /** + * Loads all hosts under the current host catalog and it's parent project scope. + * @return {Promise{[HostModel]}} + */ + async model() { + return this.store.findAll('host', this.adapterOptions()); + } + + // =actions + + /** + * Rollback changes on a host. + * @param {HostModel} host + */ + @action + cancel(host) { + const { isNew } = host; + host.rollbackAttributes(); + if (isNew) this.transitionTo('scopes.scope.projects.project.host-catalogs.host-catalog.hosts'); + } + + /** + * Handle save of a host. + * @param {HostModel} host + * @param {Event} e + */ + @action + async save(host) { + try { + const { isNew } = host; + await host.save(this.adapterOptions()); + this.refresh(); + this.notify.success( + this.intl.t(isNew ? 'notify.create-success' : 'notify.save-success') + ); + this.transitionTo('scopes.scope.projects.project.host-catalogs.host-catalog.hosts.host', host); + } catch (error) { + // TODO: replace with translated strings + this.notify.error(error.message, { closeAfter: null }); + } + } + + /** + * Delete host in current scope and redirect to index. + * @param {HostModel} host + */ + @action + async delete(host) { + try { + await host.destroyRecord(this.adapterOptions()); + this.refresh(); + this.notify.success(this.intl.t('notify.delete-success')); + this.transitionTo('scopes.scope.projects.project.host-catalogs.host-catalog.hosts'); + } catch (error) { + // TODO: replace with translated strings + this.notify.error(error.message, { closeAfter: null }); + } + } + + /** + * Configure adapter options using current host catalog scope and it's parent project scope. + * @return {object} + */ + adapterOptions() { + const scopeID = this.modelFor('scopes.scope.projects.project').id; + const hostCatalogID = this.modelFor('scopes.scope.projects.project.host-catalogs.host-catalog').id; + return { adapterOptions : { scopeID, hostCatalogID } }; + } +} diff --git a/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/host-catalog/hosts/host.js b/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/host-catalog/hosts/host.js new file mode 100644 index 000000000..907454d36 --- /dev/null +++ b/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/host-catalog/hosts/host.js @@ -0,0 +1,47 @@ +import Route from '@ember/routing/route'; + +export default class ScopesScopeProjectsProjectHostCatalogsHostCatalogHostsHostRoute extends Route { + // =methods + + /** + * Load a host using current host-catalog and it's parent project scope. + * @param {object} params + * @param {string} params.host_id + * @return {HostModel} + */ + async model({ host_id }) { + const { id: scopeID } = this.modelFor('scopes.scope.projects.project'); + const hostCatalogID = this.modelFor('scopes.scope.projects.project.host-catalogs.host-catalog').id; + return this.store.findRecord('host', host_id, { + adapterOptions: { scopeID, hostCatalogID } + }); + } + + /** + * Renders the host specific templates for header, navigation, and action page sections. + * @override + * @param {object} controller + * @param {object} model + */ + renderTemplate(controller, model) { + super.renderTemplate(...arguments); + + this.render('scopes/scope/projects/project/host-catalogs/host-catalog/hosts/-header', { + into: 'scopes/scope/projects/project/host-catalogs/host-catalog', + outlet: 'header', + model: model + }); + + this.render('scopes/scope/projects/project/host-catalogs/host-catalog/hosts/-navigation', { + into: 'scopes/scope/projects/project/host-catalogs/host-catalog', + outlet: 'navigation', + model: model + }); + + this.render('scopes/scope/projects/project/host-catalogs/host-catalog/hosts/-actions', { + into: 'scopes/scope/projects/project/host-catalogs/host-catalog', + outlet: 'actions', + model: model + }); + } +} diff --git a/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/host-catalog/hosts/host/index.js b/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/host-catalog/hosts/host/index.js new file mode 100644 index 000000000..a270ffacc --- /dev/null +++ b/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/host-catalog/hosts/host/index.js @@ -0,0 +1,4 @@ +import Route from '@ember/routing/route'; + +export default class ScopesScopeProjectsProjectHostCatalogsHostCatalogHostsHostIndexRoute extends Route { +} diff --git a/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/host-catalog/hosts/index.js b/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/host-catalog/hosts/index.js new file mode 100644 index 000000000..96bba9927 --- /dev/null +++ b/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/host-catalog/hosts/index.js @@ -0,0 +1,4 @@ +import Route from '@ember/routing/route'; + +export default class ScopesScopeProjectsProjectHostCatalogsHostCatalogHostsIndexRoute extends Route { +} diff --git a/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/host-catalog/hosts/new.js b/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/host-catalog/hosts/new.js new file mode 100644 index 000000000..39f90d1bb --- /dev/null +++ b/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/host-catalog/hosts/new.js @@ -0,0 +1,46 @@ +import Route from '@ember/routing/route'; + +export default class ScopesScopeProjectsProjectHostCatalogsHostCatalogHostsNewRoute extends Route { + + // =methods + + /** + * Creates a new unsaved host in current host catalog scope. + * @return {HostModel} + */ + model() { + const scopeModel = this.modelFor('scopes.scope.projects.project.host-catalogs.host-catalog'); + return this.store.createRecord('host', { + type: 'static', + scopeModel + }); + } + + /** + * Renders new host specific templates for header, navigation, and action page sections. + * @override + * @param {object} controller + * @param {object} model + */ + renderTemplate(controller, model) { + super.renderTemplate(...arguments); + + this.render('scopes/scope/projects/project/host-catalogs/host-catalog/hosts/new/-header', { + into: 'scopes/scope/projects/project/host-catalogs/host-catalog', + outlet: 'header', + model: model + }); + + this.render('_empty', { + into: 'scopes/scope/projects/project/host-catalogs/host-catalog', + outlet: 'navigation', + model: model + }); + + this.render('_empty', { + into: 'scopes/scope/projects/project/host-catalogs/host-catalog', + outlet: 'actions', + model: model + }); + } +} diff --git a/ui/core/app/routes/scopes/scope/projects/project/targets.js b/ui/core/app/routes/scopes/scope/projects/project/targets.js new file mode 100644 index 000000000..acb1e07cc --- /dev/null +++ b/ui/core/app/routes/scopes/scope/projects/project/targets.js @@ -0,0 +1,74 @@ +import Route from '@ember/routing/route'; +import { inject as service } from '@ember/service'; +import { action } from '@ember/object'; + +export default class ScopesScopeProjectsProjectTargetsRoute extends Route { + + // =services + + @service intl; + @service notify; + + // =methods + + /** + * Loads all targets under current project scope. + * @return {Promise{[TargetModel]}} + */ + async model() { + const scopeID = this.modelFor('scopes.scope.projects.project').id; + return this.store.findAll('target', { adapterOptions: { scopeID } }); + } + + // =actions + + /** + * Rollback changes on a target. + * @param {TargetModel} target + */ + @action + cancel(target) { + const { isNew } = target; + target.rollbackAttributes(); + if (isNew) this.transitionTo('scopes.scope.projects.project.targets'); + } + + /** + * Handle save. + * @param {TargetModel} target + * @param {Event} e + */ + @action + async save(target) { + const { isNew } = target; + try { + await target.save(); + this.refresh(); + this.notify.success( + this.intl.t(isNew ? 'notify.create-success' : 'notify.save-success') + ); + this.transitionTo('scopes.scope.projects.project.targets.target', target); + } catch (error) { + // TODO: replace with translated strings + this.notify.error(error.message, { closeAfter: null }); + } + } + + /** + * Deletes a target and redirects to targets index. + * @param {TargetModel} target + */ + @action + async delete(target) { + try { + await target.destroyRecord(); + this.refresh(); + this.notify.success(this.intl.t('notify.delete-success')); + this.transitionTo('scopes.scope.projects.project.targets'); + } catch (error) { + // TODO: replace with translated strings + this.notify.error(error.message, { closeAfter: null }); + } + } + +} diff --git a/ui/core/app/routes/scopes/scope/projects/project/targets/index.js b/ui/core/app/routes/scopes/scope/projects/project/targets/index.js new file mode 100644 index 000000000..0506aa8da --- /dev/null +++ b/ui/core/app/routes/scopes/scope/projects/project/targets/index.js @@ -0,0 +1,4 @@ +import Route from '@ember/routing/route'; + +export default class ScopesScopeProjectsProjectTargetsIndexRoute extends Route { +} diff --git a/ui/core/app/routes/scopes/scope/projects/project/targets/new.js b/ui/core/app/routes/scopes/scope/projects/project/targets/new.js new file mode 100644 index 000000000..000752c13 --- /dev/null +++ b/ui/core/app/routes/scopes/scope/projects/project/targets/new.js @@ -0,0 +1,16 @@ +import Route from '@ember/routing/route'; + +export default class ScopesScopeProjectsProjectTargetsNewRoute extends Route { + + // =methods + + /** + * Creates a new unsaved target in current project scope. + * @return {TargetModel} + */ + model() { + const scopeModel = this.modelFor('scopes.scope.projects.project'); + return this.store.createRecord('target', { scopeModel }); + } + +} diff --git a/ui/core/app/routes/scopes/scope/projects/project/targets/target.js b/ui/core/app/routes/scopes/scope/projects/project/targets/target.js new file mode 100644 index 000000000..8b5cf77ab --- /dev/null +++ b/ui/core/app/routes/scopes/scope/projects/project/targets/target.js @@ -0,0 +1,18 @@ +import Route from '@ember/routing/route'; + +export default class ScopesScopeProjectsProjectTargetsTargetRoute extends Route { + + // =methods + + /** + * Load a target. + * @param {object} params + * @param {string} params.target_id + * @return {TargetModel} + */ + async model({ target_id }) { + const { id: scopeID } = this.modelFor('scopes.scope.projects.project'); + return this.store.findRecord('target', target_id, { adapterOptions: { scopeID } }); + } + +} diff --git a/ui/core/app/routes/scopes/scope/projects/project/targets/target/index.js b/ui/core/app/routes/scopes/scope/projects/project/targets/target/index.js new file mode 100644 index 000000000..ff772f61f --- /dev/null +++ b/ui/core/app/routes/scopes/scope/projects/project/targets/target/index.js @@ -0,0 +1,4 @@ +import Route from '@ember/routing/route'; + +export default class ScopesScopeProjectsProjectTargetsTargetIndexRoute extends Route { +} 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 a5bc4f485..3a7d79661 100644 --- a/ui/core/app/templates/scopes/scope/auth-methods/index.hbs +++ b/ui/core/app/templates/scopes/scope/auth-methods/index.hbs @@ -21,16 +21,16 @@ - {{t 'form.label.name'}} - {{t 'form.label.type'}} - {{t 'form.label.id'}} + {{t 'form.label.name'}} + {{t 'form.label.type'}} + {{t 'form.label.id'}} {{#each @model as |authMethod|}} - +

{{authMethod.description}}

-
+ {{t (concat 'auth.method-types.' authMethod.type)}} - {{authMethod.id}} + + {{authMethod.id}} +
{{/each}}
diff --git a/ui/core/app/templates/scopes/scope/groups/index.hbs b/ui/core/app/templates/scopes/scope/groups/index.hbs index cddaa478b..158ea31d0 100644 --- a/ui/core/app/templates/scopes/scope/groups/index.hbs +++ b/ui/core/app/templates/scopes/scope/groups/index.hbs @@ -21,22 +21,25 @@ - {{t 'form.label.name'}} - {{t 'form.label.description'}} + {{t "form.label.name"}} + {{t "form.label.id"}} {{#each @model as |group|}} - + {{group.name}} +

{{group.description}}

+
+ + {{group.id}} - {{group.description}}
{{/each}}
diff --git a/ui/core/app/templates/scopes/scope/projects/project/_sidebar.hbs b/ui/core/app/templates/scopes/scope/projects/project/_sidebar.hbs index 8f46f6113..ae4f79e0b 100644 --- a/ui/core/app/templates/scopes/scope/projects/project/_sidebar.hbs +++ b/ui/core/app/templates/scopes/scope/projects/project/_sidebar.hbs @@ -1,4 +1,7 @@ + + {{t "resources.targets"}} + {{t "resources.host-catalogs"}} diff --git a/ui/core/app/templates/scopes/scope/projects/project/host-catalogs/host-catalog/-actions.hbs b/ui/core/app/templates/scopes/scope/projects/project/host-catalogs/host-catalog/-actions.hbs index 9f1032438..50cd97fe0 100644 --- a/ui/core/app/templates/scopes/scope/projects/project/host-catalogs/host-catalog/-actions.hbs +++ b/ui/core/app/templates/scopes/scope/projects/project/host-catalogs/host-catalog/-actions.hbs @@ -1,4 +1,10 @@ + + {{t "actions.add-host"}} + {{t 'titles.host-sets'}} + + {{t 'titles.hosts'}} + diff --git a/ui/core/app/templates/scopes/scope/projects/project/host-catalogs/host-catalog/host-sets/index.hbs b/ui/core/app/templates/scopes/scope/projects/project/host-catalogs/host-catalog/host-sets/index.hbs index 8b7dc963b..a3d132659 100644 --- a/ui/core/app/templates/scopes/scope/projects/project/host-catalogs/host-catalog/host-sets/index.hbs +++ b/ui/core/app/templates/scopes/scope/projects/project/host-catalogs/host-catalog/host-sets/index.hbs @@ -3,15 +3,15 @@ - {{t "form.label.name"}} - {{t "form.label.type"}} - {{t "form.label.id"}} + {{t "form.label.name"}} + {{t "form.label.type"}} + {{t "form.label.id"}} {{#each @model as |hostSet|}} - +

{{hostSet.description}}

-
+ {{hostSet.type}} diff --git a/ui/core/app/templates/scopes/scope/projects/project/host-catalogs/host-catalog/hosts.hbs b/ui/core/app/templates/scopes/scope/projects/project/host-catalogs/host-catalog/hosts.hbs new file mode 100644 index 000000000..863e34ea9 --- /dev/null +++ b/ui/core/app/templates/scopes/scope/projects/project/host-catalogs/host-catalog/hosts.hbs @@ -0,0 +1,3 @@ +{{title (t "resources.hosts")}} + +{{outlet}} \ No newline at end of file diff --git a/ui/core/app/templates/scopes/scope/projects/project/host-catalogs/host-catalog/hosts/-actions.hbs b/ui/core/app/templates/scopes/scope/projects/project/host-catalogs/host-catalog/hosts/-actions.hbs new file mode 100644 index 000000000..a63265116 --- /dev/null +++ b/ui/core/app/templates/scopes/scope/projects/project/host-catalogs/host-catalog/hosts/-actions.hbs @@ -0,0 +1,9 @@ + + + {{t 'actions.delete-host'}} + + \ No newline at end of file diff --git a/ui/core/app/templates/scopes/scope/projects/project/host-catalogs/host-catalog/hosts/-header.hbs b/ui/core/app/templates/scopes/scope/projects/project/host-catalogs/host-catalog/hosts/-header.hbs new file mode 100644 index 000000000..3187158d1 --- /dev/null +++ b/ui/core/app/templates/scopes/scope/projects/project/host-catalogs/host-catalog/hosts/-header.hbs @@ -0,0 +1 @@ +

{{t "resources.host"}}

\ No newline at end of file diff --git a/ui/core/app/templates/scopes/scope/projects/project/host-catalogs/host-catalog/hosts/-navigation.hbs b/ui/core/app/templates/scopes/scope/projects/project/host-catalogs/host-catalog/hosts/-navigation.hbs new file mode 100644 index 000000000..d79e3afa2 --- /dev/null +++ b/ui/core/app/templates/scopes/scope/projects/project/host-catalogs/host-catalog/hosts/-navigation.hbs @@ -0,0 +1,5 @@ + + + {{t 'titles.details'}} + + \ No newline at end of file diff --git a/ui/core/app/templates/scopes/scope/projects/project/host-catalogs/host-catalog/hosts/host.hbs b/ui/core/app/templates/scopes/scope/projects/project/host-catalogs/host-catalog/hosts/host.hbs new file mode 100644 index 000000000..e2147cab0 --- /dev/null +++ b/ui/core/app/templates/scopes/scope/projects/project/host-catalogs/host-catalog/hosts/host.hbs @@ -0,0 +1 @@ +{{outlet}} \ No newline at end of file diff --git a/ui/core/app/templates/scopes/scope/projects/project/host-catalogs/host-catalog/hosts/host/index.hbs b/ui/core/app/templates/scopes/scope/projects/project/host-catalogs/host-catalog/hosts/host/index.hbs new file mode 100644 index 000000000..6cc780607 --- /dev/null +++ b/ui/core/app/templates/scopes/scope/projects/project/host-catalogs/host-catalog/hosts/host/index.hbs @@ -0,0 +1,5 @@ + diff --git a/ui/core/app/templates/scopes/scope/projects/project/host-catalogs/host-catalog/hosts/index.hbs b/ui/core/app/templates/scopes/scope/projects/project/host-catalogs/host-catalog/hosts/index.hbs new file mode 100644 index 000000000..3e5b48c5f --- /dev/null +++ b/ui/core/app/templates/scopes/scope/projects/project/host-catalogs/host-catalog/hosts/index.hbs @@ -0,0 +1,48 @@ +{{#if @model}} + + + + + {{t "form.label.name"}} + {{t "form.label.type"}} + {{t "form.label.id"}} + + + + {{#each @model as |host|}} + + + + {{host.displayName}} + +

{{host.description}}

+
+ + {{host.type}} + + + {{host.id}} + +
+ {{/each}} +
+
+ +{{else}} + + + + + {{t "descriptions.hosts-welcome"}} + + + + {{t "titles.new"}} + + + + +{{/if}} diff --git a/ui/core/app/templates/scopes/scope/projects/project/host-catalogs/host-catalog/hosts/new.hbs b/ui/core/app/templates/scopes/scope/projects/project/host-catalogs/host-catalog/hosts/new.hbs new file mode 100644 index 000000000..29bf7404f --- /dev/null +++ b/ui/core/app/templates/scopes/scope/projects/project/host-catalogs/host-catalog/hosts/new.hbs @@ -0,0 +1,7 @@ +{{title (t 'titles.new')}} + + \ No newline at end of file diff --git a/ui/core/app/templates/scopes/scope/projects/project/host-catalogs/host-catalog/hosts/new/-header.hbs b/ui/core/app/templates/scopes/scope/projects/project/host-catalogs/host-catalog/hosts/new/-header.hbs new file mode 100644 index 000000000..ace8ccb08 --- /dev/null +++ b/ui/core/app/templates/scopes/scope/projects/project/host-catalogs/host-catalog/hosts/new/-header.hbs @@ -0,0 +1 @@ +

{{t "titles.new"}}

\ No newline at end of file diff --git a/ui/core/app/templates/scopes/scope/projects/project/host-catalogs/index.hbs b/ui/core/app/templates/scopes/scope/projects/project/host-catalogs/index.hbs index 8cc81e3db..56c923f9c 100644 --- a/ui/core/app/templates/scopes/scope/projects/project/host-catalogs/index.hbs +++ b/ui/core/app/templates/scopes/scope/projects/project/host-catalogs/index.hbs @@ -24,18 +24,15 @@ - {{t "form.label.id"}} - {{t "form.label.name"}} - {{t "form.label.type"}} + {{t "form.label.name"}} + {{t "form.label.type"}} + {{t "form.label.id"}} {{#each @model as |hostCatalog|}} - - {{hostCatalog.id}} - - +

{{hostCatalog.description}}

-
+ {{hostCatalog.type}} + + {{hostCatalog.id}} +
{{/each}}
diff --git a/ui/core/app/templates/scopes/scope/projects/project/targets.hbs b/ui/core/app/templates/scopes/scope/projects/project/targets.hbs new file mode 100644 index 000000000..744cee2b2 --- /dev/null +++ b/ui/core/app/templates/scopes/scope/projects/project/targets.hbs @@ -0,0 +1,3 @@ +{{title (t "resources.targets")}} + +{{outlet}} diff --git a/ui/core/app/templates/scopes/scope/projects/project/targets/index.hbs b/ui/core/app/templates/scopes/scope/projects/project/targets/index.hbs new file mode 100644 index 000000000..16511299f --- /dev/null +++ b/ui/core/app/templates/scopes/scope/projects/project/targets/index.hbs @@ -0,0 +1,76 @@ + + + + + + + +

{{t "resources.targets"}}

+
+ + + {{#if @model}} + + {{t "titles.new"}} + + {{/if}} + + + + + {{#if @model}} + + + + + {{t "form.label.name"}} + {{t "form.label.protocol"}} + {{t "form.label.id"}} + + + + {{#each @model as |target|}} + + + + {{target.displayName}} + +

{{target.description}}

+
+ + {{#if target.protocol}} + + {{t (concat "target.protocols." target.protocol)}} + + {{/if}} + + + {{target.id}} + +
+ {{/each}} +
+
+ + {{else}} + + + + + {{t "descriptions.targets-welcome"}} + + + + {{t "titles.new"}} + + + + + {{/if}} + +
+ +
diff --git a/ui/core/app/templates/scopes/scope/projects/project/targets/new.hbs b/ui/core/app/templates/scopes/scope/projects/project/targets/new.hbs new file mode 100644 index 000000000..4ac91435b --- /dev/null +++ b/ui/core/app/templates/scopes/scope/projects/project/targets/new.hbs @@ -0,0 +1,21 @@ +{{title (t 'titles.new')}} + + + + + + + + +

{{t "titles.new"}}

+
+ + + + + +
diff --git a/ui/core/app/templates/scopes/scope/projects/project/targets/target.hbs b/ui/core/app/templates/scopes/scope/projects/project/targets/target.hbs new file mode 100644 index 000000000..e2147cab0 --- /dev/null +++ b/ui/core/app/templates/scopes/scope/projects/project/targets/target.hbs @@ -0,0 +1 @@ +{{outlet}} \ No newline at end of file diff --git a/ui/core/app/templates/scopes/scope/projects/project/targets/target/index.hbs b/ui/core/app/templates/scopes/scope/projects/project/targets/target/index.hbs new file mode 100644 index 000000000..da4860986 --- /dev/null +++ b/ui/core/app/templates/scopes/scope/projects/project/targets/target/index.hbs @@ -0,0 +1,39 @@ + + + + + + + +

{{t "resources.target"}}

+
+ + + + + {{t 'titles.details'}} + + + + + + + + {{t 'actions.delete-target'}} + + + + + + + + +
diff --git a/ui/core/app/templates/scopes/scope/roles/index.hbs b/ui/core/app/templates/scopes/scope/roles/index.hbs index e0e28cf9a..7572875cf 100644 --- a/ui/core/app/templates/scopes/scope/roles/index.hbs +++ b/ui/core/app/templates/scopes/scope/roles/index.hbs @@ -21,22 +21,25 @@ - {{t 'form.label.name'}} - {{t 'form.label.description'}} + {{t "form.label.name"}} + {{t "form.label.id"}} {{#each @model as |role|}} - + {{role.displayName}} +

{{role.description}}

+
+ + {{role.id}} - {{role.description}}
{{/each}}
diff --git a/ui/core/app/templates/scopes/scope/users/index.hbs b/ui/core/app/templates/scopes/scope/users/index.hbs index fb0a06a09..40c312997 100644 --- a/ui/core/app/templates/scopes/scope/users/index.hbs +++ b/ui/core/app/templates/scopes/scope/users/index.hbs @@ -21,8 +21,8 @@ - {{t 'form.label.name'}} - {{t 'form.label.description'}} + {{t "form.label.name"}} + {{t "form.label.id"}} @@ -35,8 +35,11 @@ > {{user.name}} +

{{user.description}}

- {{user.description}} + + {{user.id}} +
{{/each}}
diff --git a/ui/core/mirage/config.js b/ui/core/mirage/config.js index 4f69c6f33..56ebd68d0 100644 --- a/ui/core/mirage/config.js +++ b/ui/core/mirage/config.js @@ -123,6 +123,14 @@ export default function() { return hostSet.update(attrs); }); + // target + + this.get('/scopes/:scope_id/targets'); + this.post('/scopes/:scope_id/targets'); + this.get('/scopes/:scope_id/targets/:id'); + this.patch('/scopes/:scope_id/targets/:id'); + this.del('/scopes/:scope_id/targets/: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/target.js b/ui/core/mirage/factories/target.js new file mode 100644 index 000000000..b664ff88a --- /dev/null +++ b/ui/core/mirage/factories/target.js @@ -0,0 +1,3 @@ +import factory from '../generated/factories/target'; + +export default factory.extend(); diff --git a/ui/core/mirage/generated/factories/target.js b/ui/core/mirage/generated/factories/target.js new file mode 100644 index 000000000..134cc7c02 --- /dev/null +++ b/ui/core/mirage/generated/factories/target.js @@ -0,0 +1,17 @@ +import { Factory } from 'ember-cli-mirage'; +import { random, date } from 'faker'; + +/** + * GeneratedTargetModelFactory + */ +export default Factory.extend({ + + protocol: 'SSH', + name: () => random.words(), + description: () => random.words(), + created_time: () => date.recent(), + updated_time: () => date.recent(), + disabled: () => random.boolean(), + version: () => random.number(), + +}); diff --git a/ui/core/mirage/scenarios/default.js b/ui/core/mirage/scenarios/default.js index a4fec0451..439b9fd01 100644 --- a/ui/core/mirage/scenarios/default.js +++ b/ui/core/mirage/scenarios/default.js @@ -33,6 +33,7 @@ export default function(server) { server.db.scopes.where({type: 'project'}).forEach(({ id, type }) => { const scope = { id, type }; server.createList('host-catalog', 2, { scope }, 'withChildren'); + server.createList('target', 2, { scope }); }); - + } diff --git a/ui/core/mirage/serializers/target.js b/ui/core/mirage/serializers/target.js new file mode 100644 index 000000000..3d4bc5f3e --- /dev/null +++ b/ui/core/mirage/serializers/target.js @@ -0,0 +1,5 @@ +import ApplicationSerializer from './application'; + +export default ApplicationSerializer.extend({ + modelName: 'target' +}); diff --git a/ui/core/tests/acceptance/host-catalogs-test.js b/ui/core/tests/acceptance/host-catalogs-test.js index fada1730e..3b79331c9 100644 --- a/ui/core/tests/acceptance/host-catalogs-test.js +++ b/ui/core/tests/acceptance/host-catalogs-test.js @@ -69,7 +69,7 @@ module('Acceptance | host catalogs', function (hooks) { assert.expect(1); authenticateSession({}); await visit(urls.hostCatalogs); - await click('main tbody .rose-table-cell:nth-child(2) a'); + await click('main tbody .rose-table-header-cell:nth-child(1) a'); await a11yAudit(); assert.equal(currentURL(), urls.hostCatalog); }); diff --git a/ui/core/tests/acceptance/host-sets-test.js b/ui/core/tests/acceptance/host-sets-test.js index f7ba7226d..b018fc1d8 100644 --- a/ui/core/tests/acceptance/host-sets-test.js +++ b/ui/core/tests/acceptance/host-sets-test.js @@ -47,7 +47,7 @@ module('Acceptance | host sets', function (hooks) { assert.expect(0); }); - test('errors are displayed when save on auth method fails', async function (assert) { + test('errors are displayed when save on host set fails', async function (assert) { assert.expect(0); }); @@ -55,7 +55,7 @@ module('Acceptance | host sets', function (hooks) { assert.expect(0); }); - test('saving an existing auth method with invalid fields displays error messages', async function (assert) { + test('saving an existing host set with invalid fields displays error messages', async function (assert) { assert.expect(0); }); }); diff --git a/ui/core/tests/acceptance/hosts-test.js b/ui/core/tests/acceptance/hosts-test.js new file mode 100644 index 000000000..33ab3f05b --- /dev/null +++ b/ui/core/tests/acceptance/hosts-test.js @@ -0,0 +1,61 @@ +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 | hosts', function (hooks) { + setupApplicationTest(hooks); + setupMirage(hooks); + + hooks.beforeEach(function () { + // Setup Mirage mock resources for this test + // Generate route URLs for resources + }); + + test('visiting host', async function (assert) { + assert.expect(0); + }); + + test('can navigate to a host form', async function (assert) { + assert.expect(0); + }); + + test('can update a host and save changes', async function (assert) { + assert.expect(0); + }); + + test('can update a host and cancel changes', async function (assert) { + assert.expect(0); + }); + + test('can create new host', async function (assert) { + assert.expect(0); + }); + + test('can cancel new host creation', async function (assert) { + assert.expect(0); + }); + + test('saving a new host 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 a host fails', async function (assert) { + assert.expect(0); + }); + + test('saving an existing host with invalid fields displays error messages', async function (assert) { + assert.expect(0); + }); +}); diff --git a/ui/core/tests/acceptance/targets-test.js b/ui/core/tests/acceptance/targets-test.js new file mode 100644 index 000000000..8ff26fa7f --- /dev/null +++ b/ui/core/tests/acceptance/targets-test.js @@ -0,0 +1,61 @@ +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 | targets', function (hooks) { + setupApplicationTest(hooks); + setupMirage(hooks); + + hooks.beforeEach(function () { + // Setup Mirage mock resources for this test + // Generate route URLs for resources + }); + + test('visiting targets', async function (assert) { + assert.expect(0); + }); + + test('can navigate to a target form', async function (assert) { + assert.expect(0); + }); + + test('can update a target and save changes', async function (assert) { + assert.expect(0); + }); + + test('can update a target and cancel changes', async function (assert) { + assert.expect(0); + }); + + test('can create new target', async function (assert) { + assert.expect(0); + }); + + test('can cancel new target creation', async function (assert) { + assert.expect(0); + }); + + test('saving a new target with invalid fields displays error messages', async function (assert) { + assert.expect(0); + }); + + test('errors are displayed when save on target fails', async function (assert) { + assert.expect(0); + }); + + test('errors are displayed when delete on a target fails', async function (assert) { + assert.expect(0); + }); + + test('saving an existing target with invalid fields displays error messages', async function (assert) { + assert.expect(0); + }); +}); diff --git a/ui/core/tests/integration/components/form/host-test.js b/ui/core/tests/integration/components/form/host-test.js new file mode 100644 index 000000000..39c5147db --- /dev/null +++ b/ui/core/tests/integration/components/form/host-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/host', 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) { ... }); + this.submit = () => {}; + this.cancel = () => {}; + + await render(hbs` + `); + + assert.ok(find('form')); + }); +}); diff --git a/ui/core/tests/integration/components/form/target-test.js b/ui/core/tests/integration/components/form/target-test.js new file mode 100644 index 000000000..e0246833f --- /dev/null +++ b/ui/core/tests/integration/components/form/target-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/target', 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) { ... }); + this.submit = () => {}; + this.cancel = () => {}; + + await render(hbs` + `); + + assert.ok(find('form')); + }); +}); diff --git a/ui/core/tests/unit/controllers/scopes/scope/projects/project/host-catalogs/host-catalog/hosts-test.js b/ui/core/tests/unit/controllers/scopes/scope/projects/project/host-catalogs/host-catalog/hosts-test.js new file mode 100644 index 000000000..0abab9e08 --- /dev/null +++ b/ui/core/tests/unit/controllers/scopes/scope/projects/project/host-catalogs/host-catalog/hosts-test.js @@ -0,0 +1,12 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Controller | scopes/scope/projects/project/host-catalogs/host-catalog/hosts', function(hooks) { + setupTest(hooks); + + // Replace this with your real tests. + test('it exists', function(assert) { + let controller = this.owner.lookup('controller:scopes/scope/projects/project/host-catalogs/host-catalog/hosts'); + assert.ok(controller); + }); +}); diff --git a/ui/core/tests/unit/controllers/scopes/scope/projects/project/host-catalogs/host-catalog/hosts/host-test.js b/ui/core/tests/unit/controllers/scopes/scope/projects/project/host-catalogs/host-catalog/hosts/host-test.js new file mode 100644 index 000000000..caa2b88fd --- /dev/null +++ b/ui/core/tests/unit/controllers/scopes/scope/projects/project/host-catalogs/host-catalog/hosts/host-test.js @@ -0,0 +1,12 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Controller | scopes/scope/projects/project/host-catalogs/host-catalog/hosts/host', function(hooks) { + setupTest(hooks); + + // Replace this with your real tests. + test('it exists', function(assert) { + let controller = this.owner.lookup('controller:scopes/scope/projects/project/host-catalogs/host-catalog/hosts/host'); + assert.ok(controller); + }); +}); diff --git a/ui/core/tests/unit/controllers/scopes/scope/projects/project/host-catalogs/host-catalog/hosts/new-test.js b/ui/core/tests/unit/controllers/scopes/scope/projects/project/host-catalogs/host-catalog/hosts/new-test.js new file mode 100644 index 000000000..0fc59faf5 --- /dev/null +++ b/ui/core/tests/unit/controllers/scopes/scope/projects/project/host-catalogs/host-catalog/hosts/new-test.js @@ -0,0 +1,12 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Controller | scopes/scope/projects/project/host-catalogs/host-catalog/hosts/new', function(hooks) { + setupTest(hooks); + + // Replace this with your real tests. + test('it exists', function(assert) { + let controller = this.owner.lookup('controller:scopes/scope/projects/project/host-catalogs/host-catalog/hosts/new'); + assert.ok(controller); + }); +}); diff --git a/ui/core/tests/unit/controllers/scopes/scope/projects/project/targets-test.js b/ui/core/tests/unit/controllers/scopes/scope/projects/project/targets-test.js new file mode 100644 index 000000000..908c813bf --- /dev/null +++ b/ui/core/tests/unit/controllers/scopes/scope/projects/project/targets-test.js @@ -0,0 +1,12 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Controller | scopes/scope/projects/project/targets', function(hooks) { + setupTest(hooks); + + // Replace this with your real tests. + test('it exists', function(assert) { + let controller = this.owner.lookup('controller:scopes/scope/projects/project/targets'); + assert.ok(controller); + }); +}); diff --git a/ui/core/tests/unit/controllers/scopes/scope/projects/project/targets/new-test.js b/ui/core/tests/unit/controllers/scopes/scope/projects/project/targets/new-test.js new file mode 100644 index 000000000..d7634fb8c --- /dev/null +++ b/ui/core/tests/unit/controllers/scopes/scope/projects/project/targets/new-test.js @@ -0,0 +1,12 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Controller | scopes/scope/projects/project/targets/new', function(hooks) { + setupTest(hooks); + + // Replace this with your real tests. + test('it exists', function(assert) { + let controller = this.owner.lookup('controller:scopes/scope/projects/project/targets/new'); + assert.ok(controller); + }); +}); diff --git a/ui/core/tests/unit/controllers/scopes/scope/projects/project/targets/target-test.js b/ui/core/tests/unit/controllers/scopes/scope/projects/project/targets/target-test.js new file mode 100644 index 000000000..839332da1 --- /dev/null +++ b/ui/core/tests/unit/controllers/scopes/scope/projects/project/targets/target-test.js @@ -0,0 +1,12 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Controller | scopes/scope/projects/project/targets/target', function(hooks) { + setupTest(hooks); + + // Replace this with your real tests. + test('it exists', function(assert) { + let controller = this.owner.lookup('controller:scopes/scope/projects/project/targets/target'); + assert.ok(controller); + }); +}); diff --git a/ui/core/tests/unit/routes/scopes/scope/projects/project/host-catalogs/host-catalog/hosts-test.js b/ui/core/tests/unit/routes/scopes/scope/projects/project/host-catalogs/host-catalog/hosts-test.js new file mode 100644 index 000000000..528fd1ac1 --- /dev/null +++ b/ui/core/tests/unit/routes/scopes/scope/projects/project/host-catalogs/host-catalog/hosts-test.js @@ -0,0 +1,11 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Route | scopes/scope/projects/project/host-catalogs/host-catalog/hosts', function(hooks) { + setupTest(hooks); + + test('it exists', function(assert) { + let route = this.owner.lookup('route:scopes/scope/projects/project/host-catalogs/host-catalog/hosts'); + assert.ok(route); + }); +}); diff --git a/ui/core/tests/unit/routes/scopes/scope/projects/project/host-catalogs/host-catalog/hosts/host-test.js b/ui/core/tests/unit/routes/scopes/scope/projects/project/host-catalogs/host-catalog/hosts/host-test.js new file mode 100644 index 000000000..ea1cc4118 --- /dev/null +++ b/ui/core/tests/unit/routes/scopes/scope/projects/project/host-catalogs/host-catalog/hosts/host-test.js @@ -0,0 +1,11 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Route | scopes/scope/projects/project/host-catalogs/host-catalog/hosts/host', function(hooks) { + setupTest(hooks); + + test('it exists', function(assert) { + let route = this.owner.lookup('route:scopes/scope/projects/project/host-catalogs/host-catalog/hosts/host'); + assert.ok(route); + }); +}); diff --git a/ui/core/tests/unit/routes/scopes/scope/projects/project/host-catalogs/host-catalog/hosts/host/index-test.js b/ui/core/tests/unit/routes/scopes/scope/projects/project/host-catalogs/host-catalog/hosts/host/index-test.js new file mode 100644 index 000000000..58704fe8c --- /dev/null +++ b/ui/core/tests/unit/routes/scopes/scope/projects/project/host-catalogs/host-catalog/hosts/host/index-test.js @@ -0,0 +1,11 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Route | scopes/scope/projects/project/host-catalogs/host-catalog/hosts/host/index', function(hooks) { + setupTest(hooks); + + test('it exists', function(assert) { + let route = this.owner.lookup('route:scopes/scope/projects/project/host-catalogs/host-catalog/hosts/host/index'); + assert.ok(route); + }); +}); diff --git a/ui/core/tests/unit/routes/scopes/scope/projects/project/host-catalogs/host-catalog/hosts/index-test.js b/ui/core/tests/unit/routes/scopes/scope/projects/project/host-catalogs/host-catalog/hosts/index-test.js new file mode 100644 index 000000000..151e905b5 --- /dev/null +++ b/ui/core/tests/unit/routes/scopes/scope/projects/project/host-catalogs/host-catalog/hosts/index-test.js @@ -0,0 +1,11 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Route | scopes/scope/projects/project/host-catalogs/host-catalog/hosts/index', function(hooks) { + setupTest(hooks); + + test('it exists', function(assert) { + let route = this.owner.lookup('route:scopes/scope/projects/project/host-catalogs/host-catalog/hosts/index'); + assert.ok(route); + }); +}); diff --git a/ui/core/tests/unit/routes/scopes/scope/projects/project/host-catalogs/host-catalog/hosts/new-test.js b/ui/core/tests/unit/routes/scopes/scope/projects/project/host-catalogs/host-catalog/hosts/new-test.js new file mode 100644 index 000000000..93ad5522c --- /dev/null +++ b/ui/core/tests/unit/routes/scopes/scope/projects/project/host-catalogs/host-catalog/hosts/new-test.js @@ -0,0 +1,11 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Route | scopes/scope/projects/project/host-catalogs/host-catalog/hosts/new', function(hooks) { + setupTest(hooks); + + test('it exists', function(assert) { + let route = this.owner.lookup('route:scopes/scope/projects/project/host-catalogs/host-catalog/hosts/new'); + assert.ok(route); + }); +}); diff --git a/ui/core/tests/unit/routes/scopes/scope/projects/project/targets-test.js b/ui/core/tests/unit/routes/scopes/scope/projects/project/targets-test.js new file mode 100644 index 000000000..ca2ead9ca --- /dev/null +++ b/ui/core/tests/unit/routes/scopes/scope/projects/project/targets-test.js @@ -0,0 +1,11 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Route | scopes/scope/projects/project/targets', function(hooks) { + setupTest(hooks); + + test('it exists', function(assert) { + let route = this.owner.lookup('route:scopes/scope/projects/project/targets'); + assert.ok(route); + }); +}); diff --git a/ui/core/tests/unit/routes/scopes/scope/projects/project/targets/index-test.js b/ui/core/tests/unit/routes/scopes/scope/projects/project/targets/index-test.js new file mode 100644 index 000000000..fa478cbe9 --- /dev/null +++ b/ui/core/tests/unit/routes/scopes/scope/projects/project/targets/index-test.js @@ -0,0 +1,11 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Route | scopes/scope/projects/project/targets/index', function(hooks) { + setupTest(hooks); + + test('it exists', function(assert) { + let route = this.owner.lookup('route:scopes/scope/projects/project/targets/index'); + assert.ok(route); + }); +}); diff --git a/ui/core/tests/unit/routes/scopes/scope/projects/project/targets/new-test.js b/ui/core/tests/unit/routes/scopes/scope/projects/project/targets/new-test.js new file mode 100644 index 000000000..dea235c50 --- /dev/null +++ b/ui/core/tests/unit/routes/scopes/scope/projects/project/targets/new-test.js @@ -0,0 +1,11 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Route | scopes/scope/projects/project/targets/new', function(hooks) { + setupTest(hooks); + + test('it exists', function(assert) { + let route = this.owner.lookup('route:scopes/scope/projects/project/targets/new'); + assert.ok(route); + }); +}); diff --git a/ui/core/tests/unit/routes/scopes/scope/projects/project/targets/target-test.js b/ui/core/tests/unit/routes/scopes/scope/projects/project/targets/target-test.js new file mode 100644 index 000000000..0fa4c6fc5 --- /dev/null +++ b/ui/core/tests/unit/routes/scopes/scope/projects/project/targets/target-test.js @@ -0,0 +1,11 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Route | scopes/scope/projects/project/targets/target', function(hooks) { + setupTest(hooks); + + test('it exists', function(assert) { + let route = this.owner.lookup('route:scopes/scope/projects/project/targets/target'); + assert.ok(route); + }); +}); diff --git a/ui/core/tests/unit/routes/scopes/scope/projects/project/targets/target/index-test.js b/ui/core/tests/unit/routes/scopes/scope/projects/project/targets/target/index-test.js new file mode 100644 index 000000000..5b1f35962 --- /dev/null +++ b/ui/core/tests/unit/routes/scopes/scope/projects/project/targets/target/index-test.js @@ -0,0 +1,11 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Route | scopes/scope/projects/project/targets/target/index', function(hooks) { + setupTest(hooks); + + test('it exists', function(assert) { + let route = this.owner.lookup('route:scopes/scope/projects/project/targets/target/index'); + assert.ok(route); + }); +}); diff --git a/ui/core/translations/en-us.yaml b/ui/core/translations/en-us.yaml index e02f2f7b6..55783c623 100644 --- a/ui/core/translations/en-us.yaml +++ b/ui/core/translations/en-us.yaml @@ -16,7 +16,10 @@ actions: delete-host-catalog: Delete host catalog delete-auth-method: Delete auth method delete-host-set: Delete host set + delete-host: Delete host + delete-target: Delete target add-host-set: Create Host Set + add-host: Create Host auth: titles: no-scopes: No Scopes Available @@ -31,6 +34,12 @@ auth: host-set: types: static: Static +target: + protocols: + SSH: SSH +host: + types: + static: Static resources: user: User users: Users @@ -50,6 +59,8 @@ resources: host-sets: Host Sets host: Host hosts: Hosts + target: Target + targets: Targets titles: iam: IAM new: New @@ -62,14 +73,18 @@ titles: projects-welcome: Welcome to Projects host-catalogs-welcome: Welcome to Host Catalogs host-sets-welcome: Welcome to Host Sets + hosts-welcome: Welcome to Hosts users-welcome: Welcome to Users roles-welcome: Welcome to Roles groups-welcome: Welcome to Groups auth-methods-welcome: Welcome to Auth Methods + targets-welcome: Create your first Target grants: Permissions new-grant: New Grant principals: Principals host-sets: Host Sets + hosts: Hosts + targets: Targets global: Global descriptions: host-catalogs: Overview of collections of hosts and host sets. @@ -77,10 +92,12 @@ descriptions: 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. host-sets-welcome: Configure a static host set in this host catalog. + hosts-welcome: Configure a static host in this host catalog. users-welcome: Users are authorized to access Boundary. roles-welcome: Roles are collections of capabilities that can be granted to user and/or groups. groups-welcome: Groups are collections of users. auth-methods-welcome: Auth methods are configurations to authenticate users. + targets-welcome: Targets provide Users the means to connect to and interact networked services. A Target contains a collection of Hosts and a collection of Credentials. states: info: Info success: Success @@ -98,6 +115,7 @@ form: grant: Grant principal: Principal address: Address + protocol: Protocol errors: titles: unauthenticated-error: You are not signed in