diff --git a/addons/api/addon/adapters/host-set.js b/addons/api/addon/adapters/host-set.js index fe35a1935..d237c63d9 100644 --- a/addons/api/addon/adapters/host-set.js +++ b/addons/api/addon/adapters/host-set.js @@ -10,7 +10,7 @@ export default class HostSetAdapter extends ApplicationAdapter { // =attributes /** - * Host catalogs use the new, unprefixed pathing style. + * Host sets use the new, unprefixed pathing style. * @override */ hasScopePrefix = false; diff --git a/addons/api/addon/adapters/host.js b/addons/api/addon/adapters/host.js index ae1747b06..d6b6ea5eb 100644 --- a/addons/api/addon/adapters/host.js +++ b/addons/api/addon/adapters/host.js @@ -7,32 +7,12 @@ export default class HostAdapter extends ApplicationAdapter { @service store; - // =methods + // =attributes /** - * 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. - * + * Hosts use the new, unprefixed pathing style. * @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); - } - } + hasScopePrefix = false; } diff --git a/addons/api/addon/generated/models/host-set.js b/addons/api/addon/generated/models/host-set.js index d1af3fe32..309165055 100644 --- a/addons/api/addon/generated/models/host-set.js +++ b/addons/api/addon/generated/models/host-set.js @@ -15,7 +15,6 @@ export default class GeneratedHostSetModel extends BaseModel { @attr('string', { description: 'The owning host catalog ID.', - readOnly: true }) host_catalog_id; @attr('string', { diff --git a/addons/api/addon/generated/models/host.js b/addons/api/addon/generated/models/host.js index 8713b9b89..50f1463e0 100644 --- a/addons/api/addon/generated/models/host.js +++ b/addons/api/addon/generated/models/host.js @@ -12,6 +12,10 @@ export default class GeneratedHostModel extends BaseModel { description: 'The type of the resource, to help differentiate schemas' }) type; + @attr('string', { + description: 'The owning host catalog ID.', + }) host_catalog_id; + @attr('string', { description: 'Optional name for identification purposes' }) name; diff --git a/addons/api/addon/models/host-set.js b/addons/api/addon/models/host-set.js index c4b93e905..ffa0f7906 100644 --- a/addons/api/addon/models/host-set.js +++ b/addons/api/addon/models/host-set.js @@ -1,6 +1,5 @@ import GeneratedHostSetModel from '../generated/models/host-set'; import { fragmentArray } from 'ember-data-model-fragments/attributes'; -//import { computed } from '@ember/object'; export default class HostSetModel extends GeneratedHostSetModel { @@ -12,18 +11,6 @@ export default class HostSetModel extends GeneratedHostSetModel { */ @fragmentArray('fragment-string', {readOnly: true}) host_ids; - // TODO this is here in case we want it in the future, - // especially for the target / add host sets workflow - /** - * Returns the host catalog instance associated with this host set if - * it is already loaded into the store. - * @type {?HostCatalogModel} - */ - // @computed('host_catalog_id') - // get hostCatalog() { - // return this.store.peekRecord('host-catalog', this.host_catalog_id); - // } - // =methods /** diff --git a/addons/api/addon/serializers/host.js b/addons/api/addon/serializers/host.js new file mode 100644 index 000000000..06a7b91d3 --- /dev/null +++ b/addons/api/addon/serializers/host.js @@ -0,0 +1,13 @@ +import ApplicationSerializer from './application'; + +export default class HostSerializer extends ApplicationSerializer { + + // =attributes + + /** + * Serialized the scope ID into the `scope_id` field. + * @type {boolean} + */ + serializeScopeID = true; + +} diff --git a/addons/api/app/serializers/host.js b/addons/api/app/serializers/host.js new file mode 100644 index 000000000..33844fbaf --- /dev/null +++ b/addons/api/app/serializers/host.js @@ -0,0 +1 @@ +export { default } from 'api/serializers/host'; diff --git a/addons/api/tests/unit/adapters/host-test.js b/addons/api/tests/unit/adapters/host-test.js index 1e557ad26..1e45bbee7 100644 --- a/addons/api/tests/unit/adapters/host-test.js +++ b/addons/api/tests/unit/adapters/host-test.js @@ -4,25 +4,9 @@ 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/host-catalogs/hc_098/hosts:my-custom-method'); + test('it exists', function(assert) { + let adapter = this.owner.lookup('adapter:host'); + assert.ok(adapter); }); }); diff --git a/addons/api/tests/unit/serializers/host-set-test.js b/addons/api/tests/unit/serializers/host-set-test.js index 595c6f771..c10cbe915 100644 --- a/addons/api/tests/unit/serializers/host-set-test.js +++ b/addons/api/tests/unit/serializers/host-set-test.js @@ -11,6 +11,7 @@ module('Unit | Serializer | host set', function(hooks) { const record = store.createRecord('host-set', { name: 'Host Set 1', description: 'Description', + host_catalog_id: '123', version: 1 }); const snapshot = record._createSnapshot(); @@ -19,6 +20,7 @@ module('Unit | Serializer | host set', function(hooks) { assert.deepEqual(serializedRecord, { name: 'Host Set 1', description: 'Description', + host_catalog_id: '123', version: 1 }); }); diff --git a/addons/api/tests/unit/serializers/host-test.js b/addons/api/tests/unit/serializers/host-test.js new file mode 100644 index 000000000..c31a5ce4e --- /dev/null +++ b/addons/api/tests/unit/serializers/host-test.js @@ -0,0 +1,23 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Serializer | host', function(hooks) { + setupTest(hooks); + + // Replace this with your real tests. + test('it exists', function(assert) { + let store = this.owner.lookup('service:store'); + let serializer = store.serializerFor('host'); + + assert.ok(serializer); + }); + + test('it serializes records', function(assert) { + let store = this.owner.lookup('service:store'); + let record = store.createRecord('host', {}); + + let serializedRecord = record.serialize(); + + assert.ok(serializedRecord); + }); +}); diff --git a/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/host-catalog/host-sets.js b/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/host-catalog/host-sets.js index 045a0f632..3d2ec03bc 100644 --- a/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/host-catalog/host-sets.js +++ b/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/host-catalog/host-sets.js @@ -14,8 +14,7 @@ export default class ScopesScopeProjectsProjectHostCatalogsHostCatalogHostSetsRo * Loads all host-sets under the current host catalog and it's parent scope. * @return {Promise{[HostSetModel]}} */ - async model() { - this.store.unloadAll('host-set'); + model() { const { id: host_catalog_id } = this.modelFor('scopes.scope.projects.project.host-catalogs.host-catalog'); return this.store.query('host-set', { host_catalog_id }); @@ -44,10 +43,11 @@ export default class ScopesScopeProjectsProjectHostCatalogsHostCatalogHostSetsRo try { const { isNew } = hostSet; await hostSet.save(); + await this.transitionTo('scopes.scope.projects.project.host-catalogs.host-catalog.host-sets.host-set', hostSet); + await 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.host-sets.host-set', hostSet); } 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/host-catalogs/host-catalog/host-sets/host-set/add-hosts.js b/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/host-catalog/host-sets/host-set/add-hosts.js index b7ef1afd2..d4a301f93 100644 --- a/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/host-catalog/host-sets/host-set/add-hosts.js +++ b/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/host-catalog/host-sets/host-set/add-hosts.js @@ -24,11 +24,10 @@ export default class ScopesScopeProjectsProjectHostCatalogsHostCatalogHostSetsHo * @return {Promise{HostSetModel,[HostModel]}} */ async model() { - const scopeID = this.modelFor('scopes.scope.projects.project').id; - const hostCatalogID = this.modelFor('scopes.scope.projects.project.host-catalogs.host-catalog').id; + const { id: host_catalog_id } = this.modelFor('scopes.scope.projects.project.host-catalogs.host-catalog'); return hash({ hostSet: this.modelFor('scopes.scope.projects.project.host-catalogs.host-catalog.host-sets.host-set'), - hosts: this.store.findAll('host', { adapterOptions: { scopeID, hostCatalogID } }) + hosts: this.store.query('host', { host_catalog_id }) }); } @@ -84,13 +83,11 @@ export default class ScopesScopeProjectsProjectHostCatalogsHostCatalogHostSetsHo @action async addHosts(hostSet, hostIDs) { try { - const scopeID = this.modelFor('scopes.scope.projects.project').id; - const hostCatalogID = this.modelFor('scopes.scope.projects.project.host-catalogs.host-catalog').id; - await hostSet.addHosts(hostIDs, { adapterOptions : { scopeID, hostCatalogID } }); + await hostSet.addHosts(hostIDs); + await this.replaceWith('scopes.scope.projects.project.host-catalogs.host-catalog.host-sets.host-set.hosts'); this.notify.success( this.intl.t('notify.save-success') ); - this.replaceWith('scopes.scope.projects.project.host-catalogs.host-catalog.host-sets.host-set.hosts'); } 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/host-catalogs/host-catalog/host-sets/host-set/hosts.js b/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/host-catalog/host-sets/host-set/hosts.js index f349d0c72..7ae488994 100644 --- a/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/host-catalog/host-sets/host-set/hosts.js +++ b/ui/core/app/routes/scopes/scope/projects/project/host-catalogs/host-catalog/host-sets/host-set/hosts.js @@ -12,25 +12,16 @@ export default class ScopesScopeProjectsProjectHostCatalogsHostCatalogHostSetsHo // =methods - /** - * Empty out any previously loaded hosts. - */ - beforeModel() { - this.store.unloadAll('host'); - } - /** * Loads all hosts under the current host set. * @return {Promise{HostSetModel,[HostModel]}} */ async model() { - const scopeID = this.modelFor('scopes.scope.projects.project').id; - const hostCatalogID = this.modelFor('scopes.scope.projects.project.host-catalogs.host-catalog').id; const hostSet = this.modelFor('scopes.scope.projects.project.host-catalogs.host-catalog.host-sets.host-set'); return hash({ hostSet, hosts: all(hostSet.host_ids.map(host => - this.store.findRecord('host', host.value, { adapterOptions: { scopeID, hostCatalogID } }) + this.store.findRecord('host', host.value, { reload: true }) )) }); } 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 f0dbcc3bd..378b78c7a 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 @@ -9,10 +9,11 @@ export default class ScopesScopeProjectsProjectHostCatalogsHostCatalogHostSetsNe * @return {HostSetModel} */ model() { - const scopeModel = this.modelFor('scopes.scope.projects.project.host-catalogs.host-catalog'); + const { id: host_catalog_id } = + this.modelFor('scopes.scope.projects.project.host-catalogs.host-catalog'); return this.store.createRecord('host-set', { type: 'static', - scopeModel + host_catalog_id }); } 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 index 805112d67..d8bfcfddc 100644 --- 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 @@ -14,8 +14,10 @@ export default class ScopesScopeProjectsProjectHostCatalogsHostCatalogHostsRoute * 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()); + model() { + const { id: host_catalog_id } = + this.modelFor('scopes.scope.projects.project.host-catalogs.host-catalog'); + return this.store.query('host', { host_catalog_id }); } // =actions @@ -40,12 +42,12 @@ export default class ScopesScopeProjectsProjectHostCatalogsHostCatalogHostsRoute async save(host) { try { const { isNew } = host; - await host.save(this.adapterOptions()); - this.refresh(); + await host.save(); + await this.transitionTo('scopes.scope.projects.project.host-catalogs.host-catalog.hosts.host', host); + await 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 }); @@ -59,7 +61,7 @@ export default class ScopesScopeProjectsProjectHostCatalogsHostCatalogHostsRoute @action async delete(host) { try { - await host.destroyRecord(this.adapterOptions()); + await host.destroyRecord(); this.refresh(); this.notify.success(this.intl.t('notify.delete-success')); this.transitionTo('scopes.scope.projects.project.host-catalogs.host-catalog.hosts'); @@ -68,14 +70,4 @@ export default class ScopesScopeProjectsProjectHostCatalogsHostCatalogHostsRoute 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 index 87525e6c8..2561da8d6 100644 --- 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 @@ -10,12 +10,7 @@ export default class ScopesScopeProjectsProjectHostCatalogsHostCatalogHostsHostR * @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, { - reload: true, - adapterOptions: { scopeID, hostCatalogID } - }); + return this.store.findRecord('host', host_id, { reload: true }); } /** 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 index 39f90d1bb..d8234bc47 100644 --- 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 @@ -9,10 +9,11 @@ export default class ScopesScopeProjectsProjectHostCatalogsHostCatalogHostsNewRo * @return {HostModel} */ model() { - const scopeModel = this.modelFor('scopes.scope.projects.project.host-catalogs.host-catalog'); + const { id: host_catalog_id } = + this.modelFor('scopes.scope.projects.project.host-catalogs.host-catalog'); return this.store.createRecord('host', { type: 'static', - scopeModel + host_catalog_id }); } diff --git a/ui/core/mirage/config.js b/ui/core/mirage/config.js index 7c7a1fdf8..754e5a880 100644 --- a/ui/core/mirage/config.js +++ b/ui/core/mirage/config.js @@ -157,24 +157,30 @@ export default function() { // host - this.get('/host-catalogs/:hostCatalogId/hosts', function ({ hosts }, { params: { hostCatalogId } }) { + this.get('/hosts', function ({ hosts }, { queryParams: { host_catalog_id: hostCatalogId } }) { return hosts.where({ hostCatalogId }); }); - this.post('/host-catalogs/:hostCatalogId/hosts', function ({ hosts }, { params: { hostCatalogId } }) { + this.post('/hosts', function ({ hostCatalogs, hosts }) { const attrs = this.normalizedRequestAttrs(); - attrs.hostCatalogId = hostCatalogId; + const hostCatalog = hostCatalogs.find(attrs.hostCatalogId); + attrs.scopeId = hostCatalog.scope.id; return hosts.create(attrs); }); - this.get('/host-catalogs/:hostCatalogId/hosts/:id'); - this.patch('/host-catalogs/:hostCatalogId/hosts/:id'); - this.del('/host-catalogs/:hostCatalogId/hosts/:id'); + this.get('/hosts/:id'); + this.patch('/hosts/:id'); + this.del('/hosts/:id'); // host-set this.get('/host-sets', function ({ hostSets }, { queryParams: { host_catalog_id: hostCatalogId } }) { return hostSets.where({ hostCatalogId }); }); - this.post('/host-sets'); + this.post('/host-sets', function ({ hostCatalogs, hostSets }) { + const attrs = this.normalizedRequestAttrs(); + const hostCatalog = hostCatalogs.find(attrs.hostCatalogId); + attrs.scopeId = hostCatalog.scope.id; + return hostSets.create(attrs); + }); this.get('/host-sets/:id'); this.patch('/host-sets/:id'); this.del('/host-sets/:id'); diff --git a/ui/core/mirage/factories/host-catalog.js b/ui/core/mirage/factories/host-catalog.js index f2541fcb7..1eaed0433 100644 --- a/ui/core/mirage/factories/host-catalog.js +++ b/ui/core/mirage/factories/host-catalog.js @@ -6,8 +6,7 @@ export default factory.extend({ withChildren: trait({ afterCreate(hostCatalog, server) { const { scope } = hostCatalog; - const scopePojo = { id: scope.id, type: scope.type }; - const hosts = server.createList('host', 10, { scope: scopePojo, hostCatalog }); + const hosts = server.createList('host', 10, { scope, hostCatalog }); server.createList('host-set', 3, { scope, hostCatalog, hosts }); } }) diff --git a/ui/core/mirage/models/host.js b/ui/core/mirage/models/host.js index 89aba81e6..b9a941a4f 100644 --- a/ui/core/mirage/models/host.js +++ b/ui/core/mirage/models/host.js @@ -2,6 +2,7 @@ import { Model, belongsTo } from 'ember-cli-mirage'; export default Model.extend({ + scope: belongsTo(), hostCatalog: belongsTo() }); diff --git a/ui/core/mirage/serializers/host.js b/ui/core/mirage/serializers/host.js index ccd3994ca..4fb0353b4 100644 --- a/ui/core/mirage/serializers/host.js +++ b/ui/core/mirage/serializers/host.js @@ -1,5 +1,22 @@ import ApplicationSerializer from './application'; +import { underscore } from '@ember/string'; export default ApplicationSerializer.extend({ - modelName: 'host' + modelName: 'host', + + keyForRelationshipId(relationshipName) { + return `${this._container.inflector.singularize( + underscore(relationshipName) + )}_id`; + }, + + _hashForModel(model) { + const json = ApplicationSerializer.prototype._hashForModel.apply(this, arguments); + json.scope = { + id: model.scope.id, + type: model.scope.type + }; + return json; + } + });