From 31b0a57639ada4b37e513249f8fe1fd96c3bf8ad Mon Sep 17 00:00:00 2001 From: Zhihe Li Date: Wed, 22 Mar 2023 10:52:33 -0400 Subject: [PATCH] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20Add=20new=20target=20fie?= =?UTF-8?q?ld=20for=20storage=20bucket=20(#133)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ✅ Closes: ICU-8077 --- addons/api/addon/generated/models/target.js | 5 ++ addons/api/addon/models/target.js | 39 +++++++++ addons/api/addon/serializers/target.js | 23 ++++++ addons/api/tests/unit/models/target-test.js | 67 ++++++++++++++++ .../api/tests/unit/serializers/target-test.js | 80 +++++++++++++++++++ 5 files changed, 214 insertions(+) diff --git a/addons/api/addon/generated/models/target.js b/addons/api/addon/generated/models/target.js index 2c7e3ae14..0cc726387 100644 --- a/addons/api/addon/generated/models/target.js +++ b/addons/api/addon/generated/models/target.js @@ -83,4 +83,9 @@ export default class GeneratedTargetModel extends BaseModel { 'An IP address or DNS name for the session to connect to. Cannot be used in conjunction with host sources.', }) address; + + @attr('string', { + description: 'The storage bucket associated with this target.', + }) + storage_bucket_id; } diff --git a/addons/api/addon/models/target.js b/addons/api/addon/models/target.js index 79a784e5e..bea1c006b 100644 --- a/addons/api/addon/models/target.js +++ b/addons/api/addon/models/target.js @@ -323,6 +323,45 @@ export default class TargetModel extends GeneratedTargetModel { options ); } + + /** + * @param {object} options + * @param {object} options.adapterOptions + * @returns {Promise} + */ + setStorageBucket(options = { adapterOptions: {} }) { + const defaultAdapterOptions = { + method: 'set-storage-bucket', + }; + + return this.save({ + ...options, + adapterOptions: { + ...defaultAdapterOptions, + ...options.adapterOptions, + }, + }); + } + + /** + * @param {object} options + * @param {object} options.adapterOptions + * @returns {Promise} + */ + removeStorageBucket(options = { adapterOptions: {} }) { + const defaultAdapterOptions = { + method: 'remove-storage-bucket', + }; + + return this.save({ + ...options, + adapterOptions: { + ...defaultAdapterOptions, + ...options.adapterOptions, + }, + }); + } + /** * True if the target type is tcp. * @type {boolean} diff --git a/addons/api/addon/serializers/target.js b/addons/api/addon/serializers/target.js index d79cb3799..25a952cfc 100644 --- a/addons/api/addon/serializers/target.js +++ b/addons/api/addon/serializers/target.js @@ -41,6 +41,16 @@ export default class TargetSerializer extends ApplicationSerializer { snapshot, injectedApplicationCredentialSourceIDs ); + + if (snapshot?.adapterOptions?.method === 'set-storage-bucket') { + serialized = this.serializeWithStorageBucket(snapshot); + } + if (snapshot?.adapterOptions?.method === 'remove-storage-bucket') { + return { + version: snapshot.attr('version'), + }; + } + return serialized; } @@ -91,4 +101,17 @@ export default class TargetSerializer extends ApplicationSerializer { injected_application_credential_source_ids, }; } + + /** + * Returns a payload containing only version and storage bucket id, + * rather than existing instances on the model. + * @param {Snapshot} snapshot + * @returns {object} + */ + serializeWithStorageBucket(snapshot) { + return { + version: snapshot.attr('version'), + storage_bucket_id: snapshot?.attr('storage_bucket_id'), + }; + } } diff --git a/addons/api/tests/unit/models/target-test.js b/addons/api/tests/unit/models/target-test.js index 32555baa3..680ae429f 100644 --- a/addons/api/tests/unit/models/target-test.js +++ b/addons/api/tests/unit/models/target-test.js @@ -404,4 +404,71 @@ module('Unit | Model | target', function (hooks) { assert.true(modelTCP.isTCP); assert.false(modelTCP.isSSH); }); + + test('it has a `setStorageBucket` method that targets a specific POST API endpoint and serialization', async function (assert) { + assert.expect(1); + this.server.post( + '/v1/targets/123abc:set-storage-bucket', + (schema, request) => { + const body = JSON.parse(request.requestBody); + assert.deepEqual(body, { + storage_bucket_id: 'bucketID', + version: 1, + }); + return { id: '123abc' }; + } + ); + const store = this.owner.lookup('service:store'); + store.push({ + data: { + id: '123abc', + type: 'target', + attributes: { + name: 'Target', + description: 'Description', + storage_bucket_id: 'bucketID', + version: 1, + scope: { + scope_id: 'o_1', + type: 'scope', + }, + }, + }, + }); + const model = store.peekRecord('target', '123abc'); + await model.setStorageBucket(); + }); + + test('it has a `removeStorageBucket` method that targets a specific POST API endpoint and serialization', async function (assert) { + assert.expect(1); + this.server.post( + '/v1/targets/123abc:remove-storage-bucket', + (schema, request) => { + const body = JSON.parse(request.requestBody); + assert.deepEqual(body, { + version: 1, + }); + return { id: '123abc' }; + } + ); + const store = this.owner.lookup('service:store'); + store.push({ + data: { + id: '123abc', + type: 'target', + attributes: { + name: 'Target', + description: 'Description', + storage_bucket_id: null, + version: 1, + scope: { + scope_id: 'o_1', + type: 'scope', + }, + }, + }, + }); + const model = store.peekRecord('target', '123abc'); + await model.removeStorageBucket(); + }); }); diff --git a/addons/api/tests/unit/serializers/target-test.js b/addons/api/tests/unit/serializers/target-test.js index c531a311a..f81809c66 100644 --- a/addons/api/tests/unit/serializers/target-test.js +++ b/addons/api/tests/unit/serializers/target-test.js @@ -49,6 +49,7 @@ module('Unit | Serializer | target', function (hooks) { worker_filter: null, egress_worker_filter: null, ingress_worker_filter: null, + storage_bucket_id: null, attributes: { default_port: 1234, }, @@ -127,6 +128,49 @@ module('Unit | Serializer | target', function (hooks) { }); }); + test('it serializes only storage_bucket_id and version when an `adapterOptions.method` is passed for set storage bucket', function (assert) { + assert.expect(1); + const store = this.owner.lookup('service:store'); + const serializer = store.serializerFor('target'); + const record = store.createRecord('target', { + name: 'User', + description: 'Description', + storage_bucket_id: 'bucketID', + version: 1, + }); + const snapshot = record._createSnapshot(); + snapshot.adapterOptions = { + method: 'set-storage-bucket', + }; + const serializedRecord = serializer.serialize(snapshot); + + assert.deepEqual(serializedRecord, { + storage_bucket_id: 'bucketID', + version: 1, + }); + }); + + test('it serializes only version when an `adapterOptions.method` is passed for remove storage bucket', function (assert) { + assert.expect(1); + const store = this.owner.lookup('service:store'); + const serializer = store.serializerFor('target'); + const record = store.createRecord('target', { + name: 'User', + description: 'Description', + storage_bucket_id: 'bucketID', + version: 1, + }); + const snapshot = record._createSnapshot(); + snapshot.adapterOptions = { + method: 'remove-storage-bucket', + }; + const serializedRecord = serializer.serialize(snapshot); + + assert.deepEqual(serializedRecord, { + version: 1, + }); + }); + test('it serializes the worker_filter attribute if present', function (assert) { assert.expect(1); const store = this.owner.lookup('service:store'); @@ -155,6 +199,7 @@ module('Unit | Serializer | target', function (hooks) { worker_filter: 'worker', egress_worker_filter: null, ingress_worker_filter: null, + storage_bucket_id: null, attributes: { default_port: 1234, }, @@ -190,6 +235,7 @@ module('Unit | Serializer | target', function (hooks) { worker_filter: null, egress_worker_filter: 'egress worker', ingress_worker_filter: null, + storage_bucket_id: null, attributes: { default_port: 1234, }, @@ -225,6 +271,7 @@ module('Unit | Serializer | target', function (hooks) { worker_filter: null, egress_worker_filter: null, ingress_worker_filter: 'ingress worker', + storage_bucket_id: null, attributes: { default_port: 1234, }, @@ -438,4 +485,37 @@ module('Unit | Serializer | target', function (hooks) { }, }); }); + + test('it normalizes the storage_bucket_id attribute if present', function (assert) { + assert.expect(1); + const store = this.owner.lookup('service:store'); + const serializer = store.serializerFor('target'); + const target = store.createRecord('target').constructor; + const payload = { + id: '1', + name: 'Target 1', + storage_bucket_id: 'bucketID', + }; + const normalized = serializer.normalizeSingleResponse( + store, + target, + payload + ); + assert.deepEqual(normalized, { + included: [], + data: { + id: '1', + type: 'target', + attributes: { + authorized_actions: [], + name: 'Target 1', + host_sources: [], + brokered_credential_source_ids: [], + injected_application_credential_source_ids: [], + storage_bucket_id: 'bucketID', + }, + relationships: {}, + }, + }); + }); });