From f8c4eb470f5f8517f3f3bb8f3862aa972cd47fb7 Mon Sep 17 00:00:00 2001 From: Carlos Alcaide Corvo Date: Mon, 7 Feb 2022 09:48:51 -0800 Subject: [PATCH 1/5] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20Add=20new=20fields?= =?UTF-8?q?=20to=20host?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add new fields to host and refactor field address to use no more fragment-host-attribute-model --- addons/api/addon/generated/models/host.js | 18 +++++++ .../addon/models/fragment-host-attributes.js | 9 ---- addons/api/addon/models/host.js | 21 +++++--- .../app/models/fragment-host-attributes.js | 1 - .../api/tests/unit/serializers/host-test.js | 51 +++++++++++++++---- 5 files changed, 73 insertions(+), 27 deletions(-) delete mode 100644 addons/api/addon/models/fragment-host-attributes.js delete mode 100644 addons/api/app/models/fragment-host-attributes.js diff --git a/addons/api/addon/generated/models/host.js b/addons/api/addon/generated/models/host.js index 08efa6397..3873070f2 100644 --- a/addons/api/addon/generated/models/host.js +++ b/addons/api/addon/generated/models/host.js @@ -44,9 +44,27 @@ export default class GeneratedHostModel extends BaseModel { }) version; + @attr({ + description: 'Attributes specific to the host type', + }) + attributes; + + @attr('string', { + description: 'The address (DNS or IP name) used to reach the host', + isNestedAttribute: true, + }) + address; + @attr({ description: 'Plugin information for this resource.', readOnly: true, }) plugin; + + @attr('string', { + description: '', + readOnly: true, + isNestedAttribute: true, + }) + external_id; } diff --git a/addons/api/addon/models/fragment-host-attributes.js b/addons/api/addon/models/fragment-host-attributes.js deleted file mode 100644 index 3f0ac9073..000000000 --- a/addons/api/addon/models/fragment-host-attributes.js +++ /dev/null @@ -1,9 +0,0 @@ -import Fragment from 'ember-data-model-fragments/fragment'; -import { attr } from '@ember-data/model'; - -export default class FragmentHostAttributesModel extends Fragment { - @attr('string', { - description: 'The address (DNS or IP name) used to reach the host', - }) - address; -} diff --git a/addons/api/addon/models/host.js b/addons/api/addon/models/host.js index 14acc4d27..133464027 100644 --- a/addons/api/addon/models/host.js +++ b/addons/api/addon/models/host.js @@ -1,12 +1,21 @@ import GeneratedHostModel from '../generated/models/host'; -import { fragment } from 'ember-data-model-fragments/attributes'; +import { fragmentArray } from 'ember-data-model-fragments/attributes'; export default class HostModel extends GeneratedHostModel { - /** - * Attributes of this resource, if any, represented as a JSON fragment. - * @type {FragmentHostAttributesModel} - */ - @fragment('fragment-host-attributes', { defaultValue: {} }) attributes; + @fragmentArray('fragment-string', { + readOnly: true, + emptyArrayIfMissing: true, + isNestedAttribute: true, + }) + ip_addresses; + + // Aws specific + @fragmentArray('fragment-string', { + readOnly: true, + emptyArrayIfMissing: true, + isNestedAttribute: true, + }) + dns_names; /** * True if the host is static. diff --git a/addons/api/app/models/fragment-host-attributes.js b/addons/api/app/models/fragment-host-attributes.js deleted file mode 100644 index 201efcb7a..000000000 --- a/addons/api/app/models/fragment-host-attributes.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from 'api/models/fragment-host-attributes'; diff --git a/addons/api/tests/unit/serializers/host-test.js b/addons/api/tests/unit/serializers/host-test.js index 4c531ca82..e6361984c 100644 --- a/addons/api/tests/unit/serializers/host-test.js +++ b/addons/api/tests/unit/serializers/host-test.js @@ -4,19 +4,48 @@ import { setupTest } from 'ember-qunit'; module('Unit | Serializer | host', function (hooks) { setupTest(hooks); - test('it exists', function (assert) { - let store = this.owner.lookup('service:store'); - let serializer = store.serializerFor('host'); - - assert.ok(serializer); + test('it serializes host with type static as expected', function (assert) { + assert.expect(1); + const store = this.owner.lookup('service:store'); + const record = store.createRecord('host', { + name: 'Host 1', + compositeType: 'static', + description: 'Description', + host_catalog_id: '123', + version: 1, + address: '188e:68a9:b342:c05c:2595:2f46:499c:759f', + }); + assert.deepEqual(record.serialize(), { + name: 'Host 1', + description: 'Description', + type: 'static', + host_catalog_id: '123', + version: 1, + attributes: { + address: '188e:68a9:b342:c05c:2595:2f46:499c:759f', + }, + }); }); - test('it serializes records', function (assert) { - let store = this.owner.lookup('service:store'); - let record = store.createRecord('host', {}); + test('it serializes host with type plugin as expected', function (assert) { + assert.expect(1); + const store = this.owner.lookup('service:store'); + const recordAws = store.createRecord('host', { + name: 'Host 2', + description: 'Description', + host_catalog_id: '123', + compositeType: 'aws', + address: '188e:68a9:b342:c05c:2595:2f46:499c:759f', + }); - let serializedRecord = record.serialize(); - - assert.ok(serializedRecord); + assert.deepEqual(recordAws.serialize(), { + name: 'Host 2', + description: 'Description', + host_catalog_id: '123', + type: 'plugin', + attributes: { + address: '188e:68a9:b342:c05c:2595:2f46:499c:759f', + }, + }); }); }); From 12277c5557be871d5f42c503290be563bec59e08 Mon Sep 17 00:00:00 2001 From: Carlos Alcaide Corvo Date: Tue, 8 Feb 2022 08:24:39 -0800 Subject: [PATCH 2/5] Move new fields attributes from host generated model to plain host model. Add normalization tests --- addons/api/addon/generated/models/host.js | 18 ------ addons/api/addon/models/host.js | 15 +++++ .../api/tests/unit/serializers/host-test.js | 60 +++++++++++++++++++ 3 files changed, 75 insertions(+), 18 deletions(-) diff --git a/addons/api/addon/generated/models/host.js b/addons/api/addon/generated/models/host.js index 3873070f2..08efa6397 100644 --- a/addons/api/addon/generated/models/host.js +++ b/addons/api/addon/generated/models/host.js @@ -44,27 +44,9 @@ export default class GeneratedHostModel extends BaseModel { }) version; - @attr({ - description: 'Attributes specific to the host type', - }) - attributes; - - @attr('string', { - description: 'The address (DNS or IP name) used to reach the host', - isNestedAttribute: true, - }) - address; - @attr({ description: 'Plugin information for this resource.', readOnly: true, }) plugin; - - @attr('string', { - description: '', - readOnly: true, - isNestedAttribute: true, - }) - external_id; } diff --git a/addons/api/addon/models/host.js b/addons/api/addon/models/host.js index 133464027..6ff2022e4 100644 --- a/addons/api/addon/models/host.js +++ b/addons/api/addon/models/host.js @@ -1,7 +1,9 @@ import GeneratedHostModel from '../generated/models/host'; +import { attr } from '@ember-data/model'; import { fragmentArray } from 'ember-data-model-fragments/attributes'; export default class HostModel extends GeneratedHostModel { + // =attributes (nested under attributes) @fragmentArray('fragment-string', { readOnly: true, emptyArrayIfMissing: true, @@ -9,6 +11,19 @@ export default class HostModel extends GeneratedHostModel { }) ip_addresses; + @attr('string', { + description: 'The address (DNS or IP name) used to reach the host', + isNestedAttribute: true, + }) + address; + + @attr('string', { + description: '', + readOnly: true, + isNestedAttribute: true, + }) + external_id; + // Aws specific @fragmentArray('fragment-string', { readOnly: true, diff --git a/addons/api/tests/unit/serializers/host-test.js b/addons/api/tests/unit/serializers/host-test.js index e6361984c..366ca62ad 100644 --- a/addons/api/tests/unit/serializers/host-test.js +++ b/addons/api/tests/unit/serializers/host-test.js @@ -48,4 +48,64 @@ module('Unit | Serializer | host', function (hooks) { }, }); }); + + test('it normalizes type static record', function (assert) { + assert.expect(1); + const store = this.owner.lookup('service:store'); + const serializer = store.serializerFor('host'); + const host = store.createRecord('host').constructor; + const payload = { + id: '1', + name: 'host test', + type: 'static', + }; + const normalized = serializer.normalizeSingleResponse(store, host, payload); + assert.deepEqual(normalized, { + included: [], + data: { + id: '1', + type: 'host', + attributes: { + name: 'host test', + type: 'static', + authorized_actions: [], + dns_names: [], + ip_addresses: [], + }, + relationships: {}, + }, + }); + }); + + test('it normalizes type plugin (aws) record', function (assert) { + assert.expect(1); + const store = this.owner.lookup('service:store'); + const serializer = store.serializerFor('host'); + const host = store.createRecord('host').constructor; + const payload = { + id: '1', + name: 'host test', + type: 'plugin', + plugin: { + name: 'aws', + }, + }; + const normalized = serializer.normalizeSingleResponse(store, host, payload); + assert.deepEqual(normalized, { + included: [], + data: { + id: '1', + type: 'host', + attributes: { + name: 'host test', + type: 'plugin', + authorized_actions: [], + dns_names: [], + ip_addresses: [], + plugin: { name: 'aws' }, + }, + relationships: {}, + }, + }); + }); }); From 75f75878a1bb8131f235f80c5ed5b7affe4ababf Mon Sep 17 00:00:00 2001 From: Carlos Alcaide Corvo Date: Tue, 8 Feb 2022 15:17:01 -0800 Subject: [PATCH 3/5] Add real payload and real expectations on host normalisation unit test --- .../api/tests/unit/serializers/host-test.js | 32 +++++++++++++++---- 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/addons/api/tests/unit/serializers/host-test.js b/addons/api/tests/unit/serializers/host-test.js index 366ca62ad..7252ebc9c 100644 --- a/addons/api/tests/unit/serializers/host-test.js +++ b/addons/api/tests/unit/serializers/host-test.js @@ -58,6 +58,9 @@ module('Unit | Serializer | host', function (hooks) { id: '1', name: 'host test', type: 'static', + ip_addresses: ['10.0.0.1', '10.0.0.2', '10.0.0.3'], + authorized_actions: ['no-op', 'read'], + dns_names: ['test.example.internal', 'test.example.external'], }; const normalized = serializer.normalizeSingleResponse(store, host, payload); assert.deepEqual(normalized, { @@ -68,9 +71,16 @@ module('Unit | Serializer | host', function (hooks) { attributes: { name: 'host test', type: 'static', - authorized_actions: [], - dns_names: [], - ip_addresses: [], + authorized_actions: ['no-op', 'read'], + dns_names: [ + { value: 'test.example.internal' }, + { value: 'test.example.external' }, + ], + ip_addresses: [ + { value: '10.0.0.1' }, + { value: '10.0.0.2' }, + { value: '10.0.0.3' }, + ], }, relationships: {}, }, @@ -89,6 +99,9 @@ module('Unit | Serializer | host', function (hooks) { plugin: { name: 'aws', }, + ip_addresses: ['10.0.0.1', '10.0.0.2', '10.0.0.3'], + authorized_actions: ['no-op', 'read'], + dns_names: ['test.example.internal', 'test.example.external'], }; const normalized = serializer.normalizeSingleResponse(store, host, payload); assert.deepEqual(normalized, { @@ -99,9 +112,16 @@ module('Unit | Serializer | host', function (hooks) { attributes: { name: 'host test', type: 'plugin', - authorized_actions: [], - dns_names: [], - ip_addresses: [], + authorized_actions: ['no-op', 'read'], + dns_names: [ + { value: 'test.example.internal' }, + { value: 'test.example.external' }, + ], + ip_addresses: [ + { value: '10.0.0.1' }, + { value: '10.0.0.2' }, + { value: '10.0.0.3' }, + ], plugin: { name: 'aws' }, }, relationships: {}, From 46246441aaa44285212bb073ce33046fbc23f32c Mon Sep 17 00:00:00 2001 From: Carlos Alcaide Corvo Date: Wed, 9 Feb 2022 08:54:32 -0800 Subject: [PATCH 4/5] Refactor some readOnly attributes to have no type. Stop nesting plugin type fields. Refactor serializer/normalizer tests --- addons/api/addon/models/host.js | 21 ++++---- .../api/tests/unit/serializers/host-test.js | 54 ++++--------------- 2 files changed, 19 insertions(+), 56 deletions(-) diff --git a/addons/api/addon/models/host.js b/addons/api/addon/models/host.js index 6ff2022e4..035db1cd9 100644 --- a/addons/api/addon/models/host.js +++ b/addons/api/addon/models/host.js @@ -1,34 +1,33 @@ import GeneratedHostModel from '../generated/models/host'; import { attr } from '@ember-data/model'; -import { fragmentArray } from 'ember-data-model-fragments/attributes'; export default class HostModel extends GeneratedHostModel { - // =attributes (nested under attributes) - @fragmentArray('fragment-string', { - readOnly: true, - emptyArrayIfMissing: true, - isNestedAttribute: true, - }) - ip_addresses; + // =attributes + // Static specific fields @attr('string', { description: 'The address (DNS or IP name) used to reach the host', isNestedAttribute: true, }) address; + // Plugin specific fields + @attr({ + readOnly: true, + emptyArrayIfMissing: true, + }) + ip_addresses; + @attr('string', { description: '', readOnly: true, - isNestedAttribute: true, }) external_id; // Aws specific - @fragmentArray('fragment-string', { + @attr({ readOnly: true, emptyArrayIfMissing: true, - isNestedAttribute: true, }) dns_names; diff --git a/addons/api/tests/unit/serializers/host-test.js b/addons/api/tests/unit/serializers/host-test.js index 7252ebc9c..42d3aede0 100644 --- a/addons/api/tests/unit/serializers/host-test.js +++ b/addons/api/tests/unit/serializers/host-test.js @@ -27,29 +27,7 @@ module('Unit | Serializer | host', function (hooks) { }); }); - test('it serializes host with type plugin as expected', function (assert) { - assert.expect(1); - const store = this.owner.lookup('service:store'); - const recordAws = store.createRecord('host', { - name: 'Host 2', - description: 'Description', - host_catalog_id: '123', - compositeType: 'aws', - address: '188e:68a9:b342:c05c:2595:2f46:499c:759f', - }); - - assert.deepEqual(recordAws.serialize(), { - name: 'Host 2', - description: 'Description', - host_catalog_id: '123', - type: 'plugin', - attributes: { - address: '188e:68a9:b342:c05c:2595:2f46:499c:759f', - }, - }); - }); - - test('it normalizes type static record', function (assert) { + test('it normalizes type static record as expected', function (assert) { assert.expect(1); const store = this.owner.lookup('service:store'); const serializer = store.serializerFor('host'); @@ -58,9 +36,8 @@ module('Unit | Serializer | host', function (hooks) { id: '1', name: 'host test', type: 'static', - ip_addresses: ['10.0.0.1', '10.0.0.2', '10.0.0.3'], authorized_actions: ['no-op', 'read'], - dns_names: ['test.example.internal', 'test.example.external'], + address: '10.0.0.1', }; const normalized = serializer.normalizeSingleResponse(store, host, payload); assert.deepEqual(normalized, { @@ -72,22 +49,16 @@ module('Unit | Serializer | host', function (hooks) { name: 'host test', type: 'static', authorized_actions: ['no-op', 'read'], - dns_names: [ - { value: 'test.example.internal' }, - { value: 'test.example.external' }, - ], - ip_addresses: [ - { value: '10.0.0.1' }, - { value: '10.0.0.2' }, - { value: '10.0.0.3' }, - ], + address: '10.0.0.1', + dns_names: [], + ip_addresses: [], }, relationships: {}, }, }); }); - test('it normalizes type plugin (aws) record', function (assert) { + test('it normalizes type plugin (aws) record as expected', function (assert) { assert.expect(1); const store = this.owner.lookup('service:store'); const serializer = store.serializerFor('host'); @@ -99,8 +70,8 @@ module('Unit | Serializer | host', function (hooks) { plugin: { name: 'aws', }, - ip_addresses: ['10.0.0.1', '10.0.0.2', '10.0.0.3'], authorized_actions: ['no-op', 'read'], + ip_addresses: ['10.0.0.1', '10.0.0.2', '10.0.0.3'], dns_names: ['test.example.internal', 'test.example.external'], }; const normalized = serializer.normalizeSingleResponse(store, host, payload); @@ -113,15 +84,8 @@ module('Unit | Serializer | host', function (hooks) { name: 'host test', type: 'plugin', authorized_actions: ['no-op', 'read'], - dns_names: [ - { value: 'test.example.internal' }, - { value: 'test.example.external' }, - ], - ip_addresses: [ - { value: '10.0.0.1' }, - { value: '10.0.0.2' }, - { value: '10.0.0.3' }, - ], + ip_addresses: ['10.0.0.1', '10.0.0.2', '10.0.0.3'], + dns_names: ['test.example.internal', 'test.example.external'], plugin: { name: 'aws' }, }, relationships: {}, From 74d1b352b1ef6ec5e0fd0179c4bf1a3799dedcfb Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Wed, 9 Feb 2022 13:51:38 -0500 Subject: [PATCH 5/5] =?UTF-8?q?chore:=20=F0=9F=A4=96=20reuse=20name/descri?= =?UTF-8?q?ption=20when=20changing=20instance=20type?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../routes/scopes/scope/host-catalogs/new.js | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) 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 588ecac90..1e4932280 100644 --- a/ui/admin/app/routes/scopes/scope/host-catalogs/new.js +++ b/ui/admin/app/routes/scopes/scope/host-catalogs/new.js @@ -17,21 +17,24 @@ export default class ScopesScopeHostCatalogsNewRoute extends Route { // =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. + * Also rollback/destroy any new, unsaved instances from this route before + * creating another, but reuse name/description if available. * @return {HostCatalogModel} */ model() { const scopeModel = this.modelFor('scopes.scope'); - return this.store.createRecord('host-catalog', { scopeModel }); + let name, description; + if (this.currentModel?.isNew) { + ({ name, description } = this.currentModel); + this.currentModel.rollbackAttributes(); + } + return this.store.createRecord('host-catalog', { + scopeModel, + name, + description, + }); } afterModel(model, transition) {