chore: 🤖 Update storage buckets to handle new api changes (#266)

This commit is contained in:
Zhihe Li authored and GitHub committed 2023-05-22 17:28:48 -04:00
1 parent 1a57e2b96d
commit 55f7406576
14 files changed
+66 -251

No files matched your search

-14
View File
@@ -53,18 +53,4 @@ export default class TargetAbility extends ModelAbility {
get canRemoveCredentialSources() {
return this.hasAuthorizedAction('remove-credential-sources');
}
/**
* @type {boolean}
*/
get canSetStorageBucket() {
return this.model.isSSH && this.hasAuthorizedAction('set-storage-bucket');
}
/**
* @type {boolean}
*/
get canRemoveStorageBucket() {
return this.hasAuthorizedAction('remove-storage-bucket');
}
}
@@ -38,7 +38,7 @@ export default class SessionRecordingAdapter extends ApplicationAdapter {
mime_type: mimeType,
});
const response = await fetch(url);
const response = await fetch(url, { headers: this.headers });
return response.text();
}
@@ -86,6 +86,13 @@ export default class GeneratedTargetModel extends BaseModel {
@attr('string', {
description: 'The storage bucket associated with this target.',
isNestedAttribute: true,
})
storage_bucket_id;
@attr('boolean', {
description: 'Whether session recording is enabled on the target.',
isNestedAttribute: true,
})
enable_session_recording;
}
-38
View File
@@ -324,44 +324,6 @@ export default class TargetModel extends GeneratedTargetModel {
);
}
/**
* @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}
+4 -1
View File
@@ -74,7 +74,10 @@ export default factory.extend({
);
if (randomlySelectedStorageBucket && target.type === TYPE_TARGET_SSH) {
target.update({ storage_bucket_id: randomlySelectedStorageBucket.id });
target.update({
storage_bucket_id: randomlySelectedStorageBucket.id,
enable_session_recording: true,
});
}
target.update({
@@ -101,13 +101,5 @@ export function targetHandler({ targets }, { params: { idMethod } }) {
}
}
if (method === 'set-storage-bucket') {
updatedAttrs.storage_bucket_id = attrs.storageBucketId;
}
if (method === 'remove-storage-bucket') {
updatedAttrs.storage_bucket_id = null;
}
return target.update(updatedAttrs);
}
@@ -95,46 +95,4 @@ module('Unit | Abilities | Target', function (hooks) {
)
);
});
test('it reflects when a given ssh target may set a storage bucket', function (assert) {
assert.expect(3);
const model = {
authorized_actions: ['set-storage-bucket'],
isSSH: true,
};
const modelWithoutAuthorizedActions = {
authorized_actions: [],
isSSH: true,
};
const modelWithoutSSHType = {
authorized_actions: ['set-storage-bucket'],
};
assert.true(canService.can('setStorageBucket target', model));
assert.false(
canService.can('setStorageBucket target', modelWithoutAuthorizedActions)
);
assert.false(
canService.can('setStorageBucket target', modelWithoutSSHType)
);
});
test('it reflects when a given target may remove a storage bucket', function (assert) {
assert.expect(2);
const model = {
authorized_actions: ['remove-storage-bucket'],
};
const modelWithoutAuthorizedActions = {
authorized_actions: [],
isSSH: true,
};
assert.true(canService.can('removeStorageBucket target', model));
assert.false(
canService.can(
'removeStorageBucket target',
modelWithoutAuthorizedActions
)
);
});
});
@@ -404,71 +404,4 @@ 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();
});
});
@@ -49,9 +49,10 @@ 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,
enable_session_recording: false,
storage_bucket_id: null,
},
address: '0.0.0.0',
});
@@ -128,46 +129,41 @@ 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) {
test('it serializes 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 record = store.createRecord('target', {
name: 'User',
description: 'Description',
storage_bucket_id: 'bucketID',
version: 1,
type: TYPE_TARGET_SSH,
enable_session_recording: true,
storage_bucket_id: 'bucketID',
default_port: 1234,
scope: {
scope_id: 'org_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,
type: TYPE_TARGET_SSH,
scope_id: 'org_1',
session_max_seconds: 28800,
session_connection_limit: null,
worker_filter: null,
egress_worker_filter: null,
ingress_worker_filter: null,
attributes: {
default_port: 1234,
enable_session_recording: true,
storage_bucket_id: 'bucketID',
},
address: null,
});
});
@@ -199,9 +195,10 @@ 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,
enable_session_recording: false,
storage_bucket_id: null,
},
address: null,
});
@@ -235,9 +232,10 @@ 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,
enable_session_recording: false,
storage_bucket_id: null,
},
address: null,
});
@@ -271,9 +269,10 @@ 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,
enable_session_recording: false,
storage_bucket_id: null,
},
address: null,
});
@@ -495,6 +494,7 @@ module('Unit | Serializer | target', function (hooks) {
id: '1',
name: 'Target 1',
storage_bucket_id: 'bucketID',
enable_session_recording: true,
};
const normalized = serializer.normalizeSingleResponse(
store,
@@ -513,6 +513,7 @@ module('Unit | Serializer | target', function (hooks) {
brokered_credential_source_ids: [],
injected_application_credential_source_ids: [],
storage_bucket_id: 'bucketID',
enable_session_recording: true,
},
relationships: {},
},
@@ -6,7 +6,7 @@
<div class='enable-session-recording-toggle'>
<Hds::Form::Toggle::Field
name='target-enable-session-recording'
checked={{this.toggleEnabled}}
checked={{@model.enable_session_recording}}
{{on 'change' this.toggleSessionRecording}}
as |F|
>
@@ -14,7 +14,7 @@
'resources.target.enable-session-recording.form.filter.label'
}}</F.Label>
</Hds::Form::Toggle::Field>
{{#if this.toggleEnabled}}
{{#if @model.enable_session_recording}}
<Hds::Form::Select::Field
@isRequired={{true}}
@isInvalid={{@model.errors.storage_bucket_id}}
@@ -1,18 +1,15 @@
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { action } from '@ember/object';
export default class FormEnableSessionRecordingIndexComponent extends Component {
// =attributes
@tracked toggleEnabled = true;
//actions
/**
* toggle to enable session recording for the target
*/
@action
toggleSessionRecording() {
this.toggleEnabled = !this.toggleEnabled;
this.args.model.enable_session_recording =
!this.args.model.enable_session_recording;
}
@action
@@ -1,9 +1,6 @@
import Route from '@ember/routing/route';
import { inject as service } from '@ember/service';
import { action } from '@ember/object';
import { loading } from 'ember-loading';
import { notifySuccess, notifyError } from 'core/decorators/notify';
import { set } from '@ember/object';
export default class ScopesScopeTargetsTargetEnableSessionRecordingRoute extends Route {
// =services
@@ -45,37 +42,15 @@ export default class ScopesScopeTargetsTargetEnableSessionRecordingRoute extends
}
// =actions
/**
* Add storage bucket to target if the toggle is enabled, else remove it
* @param {TargetModel} target
* @param {boolean} isToggleEnabled
*/
@action
@loading
@notifyError(({ message }) => message, { catch: true })
@notifySuccess('notifications.add-success')
async save(target, isToggleEnabled) {
const { id } = target;
if (isToggleEnabled) {
await target.setStorageBucket();
} else {
await target.removeStorageBucket();
}
this.router.replaceWith('scopes.scope.targets.target', id);
}
/**
* reset the selected storage bucket and redirect to target
* Reset the selected storage bucket and redirect to target
* @param {TargetModel} target
*/
@action
cancel(target) {
const { id } = target;
const changedAttributes = target.changedAttributes();
if (Object.keys(changedAttributes).length) {
const existingAttributes = changedAttributes?.['storage_bucket_id']?.[0];
set(target, 'storage_bucket_id', existingAttributes);
}
target.rollbackAttributes();
this.router.replaceWith('scopes.scope.targets.target', id);
}
}
@@ -30,19 +30,14 @@
@cancel={{route-action 'cancel' @model}}
/>
</sb.Body>
{{#if
(and
(can 'setStorageBucket target' @model)
(feature-flag 'ssh-session-recording')
)
}}
{{#if (and @model.isSSH (feature-flag 'ssh-session-recording'))}}
<sb.Sidebar>
<div class='target-sidebar'>
<h3>{{t 'resources.target.form.sidebar.label'}}</h3>
<p class='description'>
{{t 'resources.target.form.sidebar.help'}}
</p>
{{#if @model.storage_bucket_id}}
{{#if @model.enable_session_recording}}
<LinkListPanel as |P|>
<P.Item
@icon='cloud-upload'
@@ -113,6 +113,7 @@ module('Acceptance | targets | enable session recording', function (hooks) {
assert.expect(1);
instances.target.update({
storageBucketId: storageBucketOne.id,
enableSessionRecording: true,
});
await visit(urls.target);
await click(SETTINGS_LINK_SELECTOR);
@@ -122,20 +123,23 @@ module('Acceptance | targets | enable session recording', function (hooks) {
test('users can click on associated storage bucket card on an ssh target', async function (assert) {
featuresService.enable('ssh-session-recording');
assert.expect(1);
instances.target.update({ storageBucketId: storageBucketOne.id });
instances.target.update({
storageBucketId: storageBucketOne.id,
enableSessionRecording: true,
});
await visit(urls.target);
await click(LINK_LIST_SELECTOR);
assert.strictEqual(currentURL(), urls.storageBucket);
});
test('toggle should be enabled and storage buckets list should be shown when enable session recording button is clicked', async function (assert) {
test('toggle should be disabled and storage buckets list should be shown when enable session recording button is clicked', async function (assert) {
featuresService.enable('ssh-session-recording');
assert.expect(3);
await visit(urls.target);
await click(ENABLE_BUTTON_SELECTOR);
assert.strictEqual(currentURL(), urls.enableSessionRecording);
assert.dom(TOGGLE_SELECTOR).isVisible();
assert.dom(DROPDOWN_SELECTOR).isVisible();
assert.dom(DROPDOWN_SELECTOR).isNotVisible();
});
test('storage buckets list is hidden when toggle is disabled', async function (assert) {
@@ -145,9 +149,9 @@ module('Acceptance | targets | enable session recording', function (hooks) {
await click(ENABLE_BUTTON_SELECTOR);
assert.strictEqual(currentURL(), urls.enableSessionRecording);
assert.dom(TOGGLE_SELECTOR).isVisible();
assert.dom(DROPDOWN_SELECTOR).isVisible();
await click(TOGGLE_SELECTOR);
assert.dom(DROPDOWN_SELECTOR).isNotVisible();
await click(TOGGLE_SELECTOR);
assert.dom(DROPDOWN_SELECTOR).isVisible();
});
test('link to add new storage bucket should be displayed and redirect to new storage buckets form', async function (assert) {
@@ -166,6 +170,7 @@ module('Acceptance | targets | enable session recording', function (hooks) {
assert.expect(3);
instances.target.update({
storageBucketId: storageBucketOne.id,
enable_session_recording: true,
});
await visit(urls.target);
await click(SETTINGS_LINK_SELECTOR);
@@ -188,12 +193,12 @@ module('Acceptance | targets | enable session recording', function (hooks) {
assert.expect(5);
await visit(urls.target);
await click(ENABLE_BUTTON_SELECTOR);
assert.strictEqual(currentURL(), urls.enableSessionRecording);
assert.dom(DROPDOWN_SELECTOR).hasNoValue();
assert.strictEqual(currentURL(), urls.enableSessionRecording);
await click(TOGGLE_SELECTOR);
assert.dom(DROPDOWN_SELECTOR).hasNoValue();
await select(DROPDOWN_SELECTOR, storageBucketTwo.id);
assert.dom(DROPDOWN_SELECTOR).hasValue();
await click(SAVE_BTN_SELECTOR);
assert.strictEqual(currentURL(), urls.target);
@@ -204,11 +209,12 @@ module('Acceptance | targets | enable session recording', function (hooks) {
);
});
test('can cancel changes to an exisiting storage bucket selection', async function (assert) {
test('can cancel changes to an existing storage bucket selection', async function (assert) {
featuresService.enable('ssh-session-recording');
assert.expect(3);
instances.target.update({
storageBucketId: storageBucketOne.id,
enable_session_recording: true,
});
await visit(urls.target);