MinIO support for Admin UI (#2248)
Add support for MinIO creation form and list
This commit is contained in:
1 parent
98c16ad422
commit
0870a8f623
26 files changed
+1332
-597
No files matched your search
@@ -73,6 +73,12 @@ export default class GeneratedStorageBucketModel extends BaseModel {
|
||||
})
|
||||
region;
|
||||
|
||||
@attr('string', {
|
||||
isNestedAttribute: true,
|
||||
description: 'The MinIO location server.',
|
||||
})
|
||||
endpoint_url;
|
||||
|
||||
//this field is not used in the UI, but this is kept here to replicate the API
|
||||
@attr('string', {
|
||||
readOnly: true,
|
||||
|
||||
@@ -9,8 +9,10 @@ export const TYPE_STORAGE_BUCKET_PLUGIN = 'plugin';
|
||||
export const TYPES_STORAGE_BUCKET = Object.freeze([TYPE_STORAGE_BUCKET_PLUGIN]);
|
||||
|
||||
export const TYPE_STORAGE_BUCKET_PLUGIN_AWS_S3 = 'aws';
|
||||
export const TYPE_STORAGE_BUCKET_PLUGIN_MINIO = 'minio';
|
||||
export const TYPES_STORAGE_BUCKET_PLUGIN = Object.freeze([
|
||||
TYPE_STORAGE_BUCKET_PLUGIN_AWS_S3,
|
||||
TYPE_STORAGE_BUCKET_PLUGIN_MINIO,
|
||||
]);
|
||||
|
||||
export const TYPE_CREDENTIAL_STATIC = 'static';
|
||||
@@ -75,6 +77,14 @@ export default class StorageBucketModel extends GeneratedStorageBucketModel {
|
||||
return this.compositeType === TYPE_STORAGE_BUCKET_PLUGIN_AWS_S3;
|
||||
}
|
||||
|
||||
/**
|
||||
* True if the storage bucket plugin type is MinIO
|
||||
* @type {boolean}
|
||||
*/
|
||||
get isMinIO() {
|
||||
return this.compositeType === TYPE_STORAGE_BUCKET_PLUGIN_MINIO;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the storage bucket is a plugin return `plugin.name`,
|
||||
* otherwise return the storage bucket type.
|
||||
|
||||
@@ -4,7 +4,10 @@
|
||||
*/
|
||||
|
||||
import ApplicationSerializer from './application';
|
||||
import { TYPE_CREDENTIAL_DYNAMIC } from '../models/storage-bucket';
|
||||
import {
|
||||
TYPE_CREDENTIAL_DYNAMIC,
|
||||
TYPE_STORAGE_BUCKET_PLUGIN_AWS_S3,
|
||||
} from '../models/storage-bucket';
|
||||
|
||||
const fieldsByCredentialType = {
|
||||
static: [
|
||||
@@ -12,27 +15,55 @@ const fieldsByCredentialType = {
|
||||
'secret_access_key',
|
||||
'region',
|
||||
'disable_credential_rotation',
|
||||
'endpoint_url',
|
||||
],
|
||||
dynamic: [
|
||||
'role_arn',
|
||||
'role_external_id',
|
||||
'role_session_name',
|
||||
'role_tags',
|
||||
'disable_credential_rotation',
|
||||
'region',
|
||||
'disable_credential_rotation',
|
||||
'endpoint_url',
|
||||
],
|
||||
};
|
||||
export default class StorageBucketSerializer extends ApplicationSerializer {
|
||||
/**
|
||||
* Serializes storage-bucket. ATM just AWS needs specific serialization,
|
||||
* for the other plugin types use default.
|
||||
* @param {object} snapshot
|
||||
* @returns
|
||||
*/
|
||||
serialize(snapshot) {
|
||||
const serialized = super.serialize(...arguments);
|
||||
const { credentialType } = snapshot.record;
|
||||
const pluginType = snapshot.record.plugin?.name;
|
||||
const serialized = super.serialize(...arguments);
|
||||
|
||||
// Common for all pluginTypes
|
||||
if (!snapshot.record.isNew) {
|
||||
delete serialized.bucket_name;
|
||||
delete serialized.bucket_prefix;
|
||||
}
|
||||
|
||||
if (credentialType === TYPE_CREDENTIAL_DYNAMIC) {
|
||||
serialized.attributes.disable_credential_rotation = true;
|
||||
if (pluginType === TYPE_STORAGE_BUCKET_PLUGIN_AWS_S3) {
|
||||
return this.serializeAws(serialized, credentialType);
|
||||
}
|
||||
|
||||
return serialized;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a storage bucket serialized object specific for aws
|
||||
* @param {object} serialized
|
||||
* @param {string} credentialType
|
||||
* @returns
|
||||
*/
|
||||
serializeAws(serialized, credentialType) {
|
||||
if (serialized.attributes) {
|
||||
if (credentialType === TYPE_CREDENTIAL_DYNAMIC) {
|
||||
serialized.attributes.disable_credential_rotation = true;
|
||||
}
|
||||
delete serialized.attributes.endpoint_url;
|
||||
}
|
||||
return serialized;
|
||||
}
|
||||
@@ -45,7 +76,7 @@ export default class StorageBucketSerializer extends ApplicationSerializer {
|
||||
if (options.isNestedAttribute && json.attributes) {
|
||||
// The key must be included in the fieldsByType list above
|
||||
if (!fieldsByCredentialType[credentialType].includes(key))
|
||||
//API requires these fields to be null
|
||||
// API requires these fields to be null
|
||||
json.attributes[key] = null;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
TYPES_STORAGE_BUCKET as types,
|
||||
TYPES_STORAGE_BUCKET_PLUGIN,
|
||||
TYPE_STORAGE_BUCKET_PLUGIN_AWS_S3,
|
||||
TYPE_STORAGE_BUCKET_PLUGIN_MINIO,
|
||||
} from 'api/models/storage-bucket';
|
||||
|
||||
// Represents known plugin types, except "foobar" which models the possibility
|
||||
@@ -50,6 +51,10 @@ export default factory.extend({
|
||||
)}`,
|
||||
disable_credential_rotation: faker.datatype.boolean(),
|
||||
};
|
||||
case TYPE_STORAGE_BUCKET_PLUGIN_MINIO:
|
||||
return {
|
||||
endpoint_url: faker.internet.url(),
|
||||
};
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -7,6 +7,7 @@ import { module, test } from 'qunit';
|
||||
import { setupTest } from 'ember-qunit';
|
||||
import {
|
||||
TYPE_STORAGE_BUCKET_PLUGIN_AWS_S3,
|
||||
TYPE_STORAGE_BUCKET_PLUGIN_MINIO,
|
||||
TYPE_STORAGE_BUCKET_PLUGIN,
|
||||
TYPE_CREDENTIAL_STATIC,
|
||||
TYPE_CREDENTIAL_DYNAMIC,
|
||||
@@ -15,15 +16,6 @@ import {
|
||||
module('Unit | Serializer | storage bucket', function (hooks) {
|
||||
setupTest(hooks);
|
||||
|
||||
test('it serializes records', function (assert) {
|
||||
let store = this.owner.lookup('service:store');
|
||||
let record = store.createRecord('storage-bucket', {});
|
||||
|
||||
let serializedRecord = record.serialize();
|
||||
|
||||
assert.ok(serializedRecord);
|
||||
});
|
||||
|
||||
test('it serializes a new static aws plugin as expected', async function (assert) {
|
||||
const store = this.owner.lookup('service:store');
|
||||
const record = store.createRecord('storage-bucket', {
|
||||
@@ -70,6 +62,47 @@ module('Unit | Serializer | storage bucket', function (hooks) {
|
||||
assert.deepEqual(record.serialize(), expectedResult);
|
||||
});
|
||||
|
||||
test('it serializes a new minio (static by default) plugin as expected', async function (assert) {
|
||||
const store = this.owner.lookup('service:store');
|
||||
const record = store.createRecord('storage-bucket', {
|
||||
compositeType: TYPE_STORAGE_BUCKET_PLUGIN_MINIO,
|
||||
credentialType: TYPE_CREDENTIAL_STATIC,
|
||||
name: 'minio',
|
||||
description: 'this has a minio plugin',
|
||||
bucket_name: 'bucketname',
|
||||
bucket_prefix: 'bucketprefix',
|
||||
worker_filter: 'workerfilter',
|
||||
endpoint_url: 'http://hashicorp.com',
|
||||
region: 'eu-west-1',
|
||||
access_key_id: 'foobars',
|
||||
secret_access_key: 'testing',
|
||||
disable_credential_rotation: true,
|
||||
});
|
||||
const expectedResult = {
|
||||
name: 'minio',
|
||||
description: 'this has a minio plugin',
|
||||
type: TYPE_STORAGE_BUCKET_PLUGIN,
|
||||
bucket_name: 'bucketname',
|
||||
bucket_prefix: 'bucketprefix',
|
||||
worker_filter: 'workerfilter',
|
||||
attributes: {
|
||||
region: 'eu-west-1',
|
||||
disable_credential_rotation: true,
|
||||
endpoint_url: 'http://hashicorp.com',
|
||||
role_arn: null,
|
||||
role_external_id: null,
|
||||
role_session_name: null,
|
||||
role_tags: null,
|
||||
secrets_hmac: null,
|
||||
},
|
||||
secrets: {
|
||||
access_key_id: 'foobars',
|
||||
secret_access_key: 'testing',
|
||||
},
|
||||
};
|
||||
assert.deepEqual(record.serialize(), expectedResult);
|
||||
});
|
||||
|
||||
test('it serializes a new aws plugin with dynamic credentials as expected', async function (assert) {
|
||||
const store = this.owner.lookup('service:store');
|
||||
const record = store.createRecord('storage-bucket', {
|
||||
@@ -126,6 +159,7 @@ module('Unit | Serializer | storage bucket', function (hooks) {
|
||||
bucket_name: 'bucketname',
|
||||
bucket_prefix: 'bucketprefix',
|
||||
worker_filter: 'workerfilter',
|
||||
plugin: { name: 'aws' },
|
||||
region: 'eu-west-1',
|
||||
access_key_id: 'foobars',
|
||||
secret_access_key: 'testing',
|
||||
@@ -183,6 +217,7 @@ module('Unit | Serializer | storage bucket', function (hooks) {
|
||||
bucket_name: 'bucketname',
|
||||
bucket_prefix: 'bucketprefix',
|
||||
worker_filter: 'workerfilter',
|
||||
plugin: { name: 'aws' },
|
||||
region: 'eu-west-1',
|
||||
disable_credential_rotation: true,
|
||||
access_key_id: 'test',
|
||||
@@ -222,6 +257,61 @@ module('Unit | Serializer | storage bucket', function (hooks) {
|
||||
assert.deepEqual(serializedRecord, expectedResult);
|
||||
});
|
||||
|
||||
test('it serializes when updating a MinIO (static credential by default) storage bucket correctly', async function (assert) {
|
||||
const store = this.owner.lookup('service:store');
|
||||
const serializer = store.serializerFor('storage-bucket');
|
||||
store.push({
|
||||
data: {
|
||||
id: '3',
|
||||
type: 'storage-bucket',
|
||||
attributes: {
|
||||
type: TYPE_STORAGE_BUCKET_PLUGIN,
|
||||
plugin: { name: 'minio' },
|
||||
name: 'minio',
|
||||
description: 'this has a minio plugin',
|
||||
bucket_name: 'bucketname',
|
||||
bucket_prefix: 'bucketprefix',
|
||||
worker_filter: 'workerfilter',
|
||||
endpoint_url: 'http://hashicorp.com',
|
||||
region: 'eu-west-1',
|
||||
disable_credential_rotation: true,
|
||||
access_key_id: 'test',
|
||||
secret_access_key: 'test',
|
||||
version: 1,
|
||||
},
|
||||
},
|
||||
});
|
||||
const record = store.peekRecord('storage-bucket', '3');
|
||||
record.description = 'This is new description';
|
||||
record.name = 'This is the new name';
|
||||
record.endpoint_url = 'http://developer.hashicorp.com';
|
||||
const snapshot = record._createSnapshot();
|
||||
const serializedRecord = serializer.serialize(snapshot);
|
||||
|
||||
const expectedResult = {
|
||||
name: 'This is the new name',
|
||||
description: 'This is new description',
|
||||
type: TYPE_STORAGE_BUCKET_PLUGIN,
|
||||
worker_filter: 'workerfilter',
|
||||
attributes: {
|
||||
region: 'eu-west-1',
|
||||
disable_credential_rotation: true,
|
||||
endpoint_url: 'http://developer.hashicorp.com',
|
||||
role_arn: null,
|
||||
role_external_id: null,
|
||||
role_session_name: null,
|
||||
role_tags: null,
|
||||
secrets_hmac: null,
|
||||
},
|
||||
secrets: {
|
||||
access_key_id: 'test',
|
||||
secret_access_key: 'test',
|
||||
},
|
||||
version: 1,
|
||||
};
|
||||
assert.deepEqual(serializedRecord, expectedResult);
|
||||
});
|
||||
|
||||
test('it normalizes correctly', function (assert) {
|
||||
const store = this.owner.lookup('service:store');
|
||||
const serializer = store.serializerFor('storage-bucket');
|
||||
|
||||
@@ -424,6 +424,7 @@ storage-bucket:
|
||||
titles:
|
||||
new: New Storage Bucket
|
||||
amazon_s3: Amazon S3
|
||||
minio: MinIO
|
||||
actions:
|
||||
delete: Delete Storage Bucket
|
||||
types:
|
||||
@@ -435,6 +436,9 @@ storage-bucket:
|
||||
dynamic:
|
||||
title: Dynamic
|
||||
description: Specify an Assume Role used to generate credentials to use with this AWS bucket
|
||||
plugin-types:
|
||||
aws: Amazon S3
|
||||
minio: MinIO
|
||||
form:
|
||||
scope:
|
||||
label: Scope
|
||||
@@ -444,33 +448,36 @@ storage-bucket:
|
||||
provider:
|
||||
label: Provider
|
||||
help: Boundary currently only supports Amazon S3 storage.
|
||||
endpoint_url:
|
||||
label: Endpoint URL
|
||||
help: Cannot be changed later.
|
||||
bucket_name:
|
||||
label: Bucket name
|
||||
help: The name of this bucket in AWS
|
||||
help: The name of this storage bucket.
|
||||
bucket_prefix:
|
||||
label: Bucket prefix
|
||||
help: The base path where session recordings will be stored.
|
||||
role_arn:
|
||||
label: Role ARN
|
||||
help: The ARN (Assume Role Name) used by this instance that the worker runs on
|
||||
help: The ARN (Assume Role Name) used by this instance that the worker runs on.
|
||||
role_external_id:
|
||||
label: Role external ID
|
||||
help: Used to provide access to this account if you have this configured
|
||||
help: Used to provide access to this account if you have this configured.
|
||||
role_tags:
|
||||
label: Role tags
|
||||
help: Attributes to pass to AWS
|
||||
help: Attributes to pass to AWS.
|
||||
role_session_name:
|
||||
label: Role session name
|
||||
help: Unique identifier for the AWS session to help identify it in your logs
|
||||
help: Unique identifier for the AWS session to help identify it in your logs.
|
||||
region:
|
||||
label: Region
|
||||
help: The AWS region to use
|
||||
help: The region that this storage bucket is in.
|
||||
access_key_id:
|
||||
label: Access key ID
|
||||
help: The Access key ID generated by AWS for the IAM User to use with this storage bucket. We will not show this data after it is saved, but you can replace it.
|
||||
help: The Access key ID generated by your storage provider for the IAM User to use with this storage bucket. We will not show this data after it is saved, but you can replace it.
|
||||
secret_access_key:
|
||||
label: Secret access key
|
||||
help: The secret access key generated by AWS for the IAM user to use with this storage bucket. We will not show this data after it is saved, but you can replace it.
|
||||
help: The secret access key generated by your storage provider for the IAM user to use with this storage bucket. We will not show this data after it is saved, but you can replace it.
|
||||
worker_filter:
|
||||
label: Worker filter
|
||||
help: Filter for Boundary workers that have network access to your storage. Boolean expression.
|
||||
|
||||
@@ -0,0 +1,491 @@
|
||||
<Rose::Form
|
||||
@onSubmit={{@submit}}
|
||||
@cancel={{@cancel}}
|
||||
@disabled={{@model.isSaving}}
|
||||
@showEditToggle={{if @model.isNew false true}}
|
||||
as |form|
|
||||
>
|
||||
{{! name }}
|
||||
<Hds::Form::TextInput::Field
|
||||
name='name'
|
||||
@isOptional={{true}}
|
||||
@value={{@model.name}}
|
||||
@isInvalid={{@model.errors.name}}
|
||||
@type='text'
|
||||
disabled={{form.disabled}}
|
||||
{{on 'input' (set-from-event @model 'name')}}
|
||||
as |F|
|
||||
>
|
||||
<F.Label>{{t 'form.name.label'}}</F.Label>
|
||||
<F.HelperText>{{t 'form.name.help'}}</F.HelperText>
|
||||
{{#if @model.errors.name}}
|
||||
<F.Error as |E|>
|
||||
{{#each @model.errors.name as |error|}}
|
||||
<E.Message>{{error.message}}</E.Message>
|
||||
{{/each}}
|
||||
</F.Error>
|
||||
{{/if}}
|
||||
</Hds::Form::TextInput::Field>
|
||||
|
||||
{{! description }}
|
||||
<Hds::Form::Textarea::Field
|
||||
name='description'
|
||||
@isOptional={{true}}
|
||||
@value={{@model.description}}
|
||||
@isInvalid={{@model.errors.description}}
|
||||
disabled={{form.disabled}}
|
||||
as |F|
|
||||
>
|
||||
<F.Label>{{t 'form.description.label'}}</F.Label>
|
||||
<F.HelperText>{{t 'form.description.help'}}</F.HelperText>
|
||||
{{#if @model.errors.description}}
|
||||
<F.Error as |E|>
|
||||
{{#each @model.errors.description as |error|}}
|
||||
<E.Message>{{error.message}}</E.Message>
|
||||
{{/each}}
|
||||
</F.Error>
|
||||
{{/if}}
|
||||
</Hds::Form::Textarea::Field>
|
||||
|
||||
{{! scope }}
|
||||
{{#if @model.isNew}}
|
||||
<Hds::Form::Select::Field
|
||||
name='scope'
|
||||
@isRequired={{true}}
|
||||
@width='100%'
|
||||
disabled={{form.disabled}}
|
||||
{{on 'change' @updateScope}}
|
||||
as |F|
|
||||
>
|
||||
<F.Label>{{t 'resources.storage-bucket.form.scope.label'}}</F.Label>
|
||||
<F.HelperText>
|
||||
{{#if @model.scope}}
|
||||
{{#if @model.scope.isGlobal}}
|
||||
{{t 'resources.storage-bucket.form.scope.help_global'}}
|
||||
{{else if @model.scope.isOrg}}
|
||||
{{t 'resources.storage-bucket.form.scope.help_org'}}
|
||||
{{/if}}
|
||||
{{else}}
|
||||
{{t 'resources.storage-bucket.form.scope.help'}}
|
||||
{{/if}}
|
||||
</F.HelperText>
|
||||
<F.Options>
|
||||
<option disabled selected={{not @model.scope}} value=''>
|
||||
{{t 'titles.choose-an-option'}}
|
||||
</option>
|
||||
{{#each @scopes as |scope|}}
|
||||
<option
|
||||
value={{scope.model.id}}
|
||||
selected={{eq scope.model.id @model.scope.scope_id}}
|
||||
>
|
||||
{{scope.model.displayName}}
|
||||
</option>
|
||||
{{/each}}
|
||||
</F.Options>
|
||||
</Hds::Form::Select::Field>
|
||||
{{else}}
|
||||
<InfoField
|
||||
@value={{@model.scopeModel.displayName}}
|
||||
@icon={{if @model.scopeModel.isGlobal 'globe' 'org'}}
|
||||
disabled={{form.disabled}}
|
||||
as |F|
|
||||
>
|
||||
<F.Label>{{t 'resources.storage-bucket.form.scope.label'}}</F.Label>
|
||||
<F.HelperText>
|
||||
{{#if @model.scopeModel.isGlobal}}
|
||||
{{t 'resources.storage-bucket.form.scope.help_global'}}
|
||||
{{else}}
|
||||
{{t 'resources.storage-bucket.form.scope.help_org'}}
|
||||
{{/if}}
|
||||
</F.HelperText>
|
||||
</InfoField>
|
||||
{{/if}}
|
||||
|
||||
{{! Provider / plugin.name }}
|
||||
<Hds::Form::RadioCard::Group @name='plugin_type' @alignment='center' as |G|>
|
||||
<G.Legend>{{t 'titles.provider'}}</G.Legend>
|
||||
<G.HelperText>{{t 'descriptions.choose-a-provider'}}</G.HelperText>
|
||||
|
||||
{{#each @pluginTypes as |pluginType|}}
|
||||
<G.RadioCard
|
||||
@value={{pluginType}}
|
||||
@checked={{eq pluginType @model.compositeType}}
|
||||
@disabled={{(not @model.isNew)}}
|
||||
{{on 'input' (fn @changePluginType pluginType)}}
|
||||
as |R|
|
||||
>
|
||||
<R.Label>{{t
|
||||
(concat 'resources.storage-bucket.plugin-types.' pluginType)
|
||||
}}</R.Label>
|
||||
{{#if (eq pluginType 'aws')}}
|
||||
<R.Icon @name='aws-color' />
|
||||
{{else}}
|
||||
<R.Icon @name='cloud-upload' />
|
||||
{{/if}}
|
||||
</G.RadioCard>
|
||||
{{/each}}
|
||||
</Hds::Form::RadioCard::Group>
|
||||
|
||||
{{! bucket_name }}
|
||||
<Hds::Form::TextInput::Field
|
||||
name='bucket_name'
|
||||
@isRequired={{true}}
|
||||
@value={{@model.bucket_name}}
|
||||
@isInvalid={{@model.errors.bucket_name}}
|
||||
disabled={{form.disabled}}
|
||||
readOnly={{not @model.isNew}}
|
||||
{{on 'input' (set-from-event @model 'bucket_name')}}
|
||||
as |F|
|
||||
>
|
||||
<F.Label>{{t 'resources.storage-bucket.form.bucket_name.label'}}</F.Label>
|
||||
<F.HelperText>{{t
|
||||
'resources.storage-bucket.form.bucket_name.help'
|
||||
}}</F.HelperText>
|
||||
{{#if @model.errors.bucket_name}}
|
||||
<F.Error as |E|>
|
||||
{{#each @model.errors.bucket_name as |error|}}
|
||||
<E.Message>{{error.message}}</E.Message>
|
||||
{{/each}}
|
||||
</F.Error>
|
||||
{{/if}}
|
||||
</Hds::Form::TextInput::Field>
|
||||
|
||||
{{! bucket_prefix }}
|
||||
<Hds::Form::TextInput::Field
|
||||
name='bucket_prefix'
|
||||
@isOptional={{true}}
|
||||
@value={{@model.bucket_prefix}}
|
||||
@isInvalid={{@model.errors.bucket_prefix}}
|
||||
disabled={{form.disabled}}
|
||||
readOnly={{not @model.isNew}}
|
||||
{{on 'input' (set-from-event @model 'bucket_prefix')}}
|
||||
as |F|
|
||||
>
|
||||
<F.Label>{{t 'resources.storage-bucket.form.bucket_prefix.label'}}</F.Label>
|
||||
<F.HelperText>{{t
|
||||
'resources.storage-bucket.form.bucket_prefix.help'
|
||||
}}</F.HelperText>
|
||||
{{#if @model.errors.bucket_prefix}}
|
||||
<F.Error as |E|>
|
||||
{{#each @model.errors.bucket_prefix as |error|}}
|
||||
<E.Message>{{error.message}}</E.Message>
|
||||
{{/each}}
|
||||
</F.Error>
|
||||
{{/if}}
|
||||
</Hds::Form::TextInput::Field>
|
||||
|
||||
{{! region }}
|
||||
<Hds::Form::TextInput::Field
|
||||
name='region'
|
||||
@isRequired={{true}}
|
||||
@value={{@model.region}}
|
||||
@isInvalid={{@model.errors.region}}
|
||||
disabled={{form.disabled}}
|
||||
readOnly={{not @model.isNew}}
|
||||
{{on 'input' (set-from-event @model 'region')}}
|
||||
as |F|
|
||||
>
|
||||
<F.Label>{{t 'resources.storage-bucket.form.region.label'}}</F.Label>
|
||||
<F.HelperText>{{t
|
||||
'resources.storage-bucket.form.region.help'
|
||||
}}</F.HelperText>
|
||||
{{#if @model.errors.region}}
|
||||
<F.Error as |E|>
|
||||
{{#each @model.errors.region as |error|}}
|
||||
<E.Message>{{error.message}}</E.Message>
|
||||
{{/each}}
|
||||
</F.Error>
|
||||
{{/if}}
|
||||
</Hds::Form::TextInput::Field>
|
||||
|
||||
{{! credential_type }}
|
||||
<Hds::Form::RadioCard::Group name='credential_type' as |G|>
|
||||
<G.Legend>{{t 'resources.storage-bucket.types.credential'}}</G.Legend>
|
||||
{{#each this.credentials as |credentialType|}}
|
||||
<G.RadioCard
|
||||
@name={{credentialType}}
|
||||
@value={{credentialType}}
|
||||
@checked={{eq credentialType this.selectedCredentialType}}
|
||||
@disabled={{form.disabled}}
|
||||
{{on 'input' (fn this.updateCredentialTypeSelection credentialType)}}
|
||||
as |R|
|
||||
>
|
||||
<R.Label>{{t
|
||||
(concat 'resources.storage-bucket.types.' credentialType '.title')
|
||||
}}</R.Label>
|
||||
<R.Description>{{t
|
||||
(concat
|
||||
'resources.storage-bucket.types.' credentialType '.description'
|
||||
)
|
||||
}}</R.Description>
|
||||
</G.RadioCard>
|
||||
{{/each}}
|
||||
</Hds::Form::RadioCard::Group>
|
||||
|
||||
{{! static credentials fields }}
|
||||
{{#unless this.showDynamicCredentials}}
|
||||
{{! Creation }}
|
||||
{{#if @model.isNew}}
|
||||
{{! access_key }}
|
||||
<Hds::Form::TextInput::Field
|
||||
name='access_key_id'
|
||||
@type='password'
|
||||
@isRequired={{true}}
|
||||
@value={{@model.access_key_id}}
|
||||
@isInvalid={{@model.errors.secrets}}
|
||||
@hasVisibilityToggle={{false}}
|
||||
{{on 'input' (set-from-event @model 'access_key_id')}}
|
||||
as |F|
|
||||
>
|
||||
<F.Label>{{t
|
||||
'resources.storage-bucket.form.access_key_id.label'
|
||||
}}</F.Label>
|
||||
<F.HelperText>{{t
|
||||
'resources.storage-bucket.form.access_key_id.help'
|
||||
}}</F.HelperText>
|
||||
{{#if @model.errors.secrets}}
|
||||
<F.Error as |E|>
|
||||
{{#each @model.errors.secrets as |error|}}
|
||||
<E.Message>{{error.message}}</E.Message>
|
||||
{{/each}}
|
||||
</F.Error>
|
||||
{{/if}}
|
||||
</Hds::Form::TextInput::Field>
|
||||
|
||||
{{! secret_access_key }}
|
||||
<Hds::Form::TextInput::Field
|
||||
name='secret_access_key'
|
||||
@type='password'
|
||||
@isRequired={{true}}
|
||||
@value={{@model.secret_access_key}}
|
||||
@isInvalid={{@model.errors.secrets}}
|
||||
disabled={{form.disabled}}
|
||||
@hasVisibilityToggle={{false}}
|
||||
{{on 'input' (set-from-event @model 'secret_access_key')}}
|
||||
as |F|
|
||||
>
|
||||
<F.Label>{{t
|
||||
'resources.storage-bucket.form.secret_access_key.label'
|
||||
}}</F.Label>
|
||||
<F.HelperText>{{t
|
||||
'resources.storage-bucket.form.secret_access_key.help'
|
||||
}}</F.HelperText>
|
||||
{{#if @model.errors.secrets}}
|
||||
<F.Error as |E|>
|
||||
{{#each @model.errors.secrets as |error|}}
|
||||
<E.Message>{{error.message}}</E.Message>
|
||||
{{/each}}
|
||||
</F.Error>
|
||||
{{/if}}
|
||||
</Hds::Form::TextInput::Field>
|
||||
{{! Edit }}
|
||||
{{else}}
|
||||
{{! access_key }}
|
||||
<Form::Field::SecretInput
|
||||
@name='access_key_id'
|
||||
@isRequired={{true}}
|
||||
@value={{@model.access_key_id}}
|
||||
@isInvalid={{@model.errors.access_key_id}}
|
||||
@isDisabled={{form.disabled}}
|
||||
@showEditButton={{true}}
|
||||
@cancel={{fn @rollbackSecretAttrs 'access_key_id'}}
|
||||
{{on 'input' (set-from-event @model 'access_key_id')}}
|
||||
as |F|
|
||||
>
|
||||
<F.Label>{{t
|
||||
'resources.storage-bucket.form.access_key_id.label'
|
||||
}}</F.Label>
|
||||
<F.HelperText>{{t
|
||||
'resources.storage-bucket.form.access_key_id.help'
|
||||
}}</F.HelperText>
|
||||
</Form::Field::SecretInput>
|
||||
|
||||
{{! secret_access_key }}
|
||||
<Form::Field::SecretInput
|
||||
@name='secret_access_key'
|
||||
@isRequired={{true}}
|
||||
@value={{@model.secret_access_key}}
|
||||
@isInvalid={{@model.errors.secret_access_key}}
|
||||
@isDisabled={{form.disabled}}
|
||||
@showEditButton={{true}}
|
||||
@cancel={{fn @rollbackSecretAttrs 'secret_access_key'}}
|
||||
{{on 'input' (set-from-event @model 'secret_access_key')}}
|
||||
as |F|
|
||||
>
|
||||
<F.Label>{{t
|
||||
'resources.storage-bucket.form.secret_access_key.label'
|
||||
}}</F.Label>
|
||||
<F.HelperText>{{t
|
||||
'resources.storage-bucket.form.secret_access_key.help'
|
||||
}}</F.HelperText>
|
||||
</Form::Field::SecretInput>
|
||||
{{/if}}
|
||||
|
||||
{{/unless}}
|
||||
|
||||
{{! dynamic credentials }}
|
||||
{{#if this.showDynamicCredentials}}
|
||||
{{! role_arn }}
|
||||
<Hds::Form::TextInput::Field
|
||||
name='role_arn'
|
||||
@isRequired={{true}}
|
||||
@value={{@model.role_arn}}
|
||||
@isInvalid={{@model.errors.role_arn}}
|
||||
disabled={{form.disabled}}
|
||||
{{on 'input' (set-from-event @model 'role_arn')}}
|
||||
as |F|
|
||||
>
|
||||
<F.Label>{{t 'resources.storage-bucket.form.role_arn.label'}}</F.Label>
|
||||
<F.HelperText>
|
||||
{{t 'resources.storage-bucket.form.role_arn.help'}}
|
||||
</F.HelperText>
|
||||
{{#if @model.errors.role_arn}}
|
||||
<F.Error as |E|>
|
||||
{{#each @model.errors.role_arn as |error|}}
|
||||
<E.Message>{{error.message}}</E.Message>
|
||||
{{/each}}
|
||||
</F.Error>
|
||||
{{/if}}
|
||||
</Hds::Form::TextInput::Field>
|
||||
|
||||
{{! role_external_id }}
|
||||
<Hds::Form::TextInput::Field
|
||||
name='role_external_id'
|
||||
@isOptional={{true}}
|
||||
@value={{@model.role_external_id}}
|
||||
@isInvalid={{@model.errors.role_external_id}}
|
||||
disabled={{form.disabled}}
|
||||
{{on 'input' (set-from-event @model 'role_external_id')}}
|
||||
as |F|
|
||||
>
|
||||
<F.Label>
|
||||
{{t 'resources.storage-bucket.form.role_external_id.label'}}
|
||||
</F.Label>
|
||||
<F.HelperText>
|
||||
{{t 'resources.storage-bucket.form.role_external_id.help'}}
|
||||
</F.HelperText>
|
||||
{{#if @model.errors.role_external_id}}
|
||||
<F.Error as |E|>
|
||||
{{#each @model.errors.role_external_id as |error|}}
|
||||
<E.Message>{{error.message}}</E.Message>
|
||||
{{/each}}
|
||||
</F.Error>
|
||||
{{/if}}
|
||||
</Hds::Form::TextInput::Field>
|
||||
|
||||
{{! role_session_name }}
|
||||
<Hds::Form::TextInput::Field
|
||||
name='role_session_name'
|
||||
@isOptional={{true}}
|
||||
@value={{@model.role_session_name}}
|
||||
@isInvalid={{@model.errors.role_session_name}}
|
||||
disabled={{form.disabled}}
|
||||
{{on 'input' (set-from-event @model 'role_session_name')}}
|
||||
as |F|
|
||||
>
|
||||
<F.Label>
|
||||
{{t 'resources.storage-bucket.form.role_session_name.label'}}
|
||||
</F.Label>
|
||||
<F.HelperText>
|
||||
{{t 'resources.storage-bucket.form.role_session_name.help'}}
|
||||
</F.HelperText>
|
||||
{{#if @model.errors.role_session_name}}
|
||||
<F.Error as |E|>
|
||||
{{#each @model.errors.role_session_name as |error|}}
|
||||
<E.Message>{{error.message}}</E.Message>
|
||||
{{/each}}
|
||||
</F.Error>
|
||||
{{/if}}
|
||||
</Hds::Form::TextInput::Field>
|
||||
|
||||
{{! role_tags }}
|
||||
<Form::Field::ListWrapper
|
||||
@layout='horizontal'
|
||||
@isOptional={{true}}
|
||||
@disabled={{form.disabled}}
|
||||
>
|
||||
<:fieldset as |F|>
|
||||
<F.Legend>
|
||||
{{t 'resources.storage-bucket.form.role_tags.label'}}
|
||||
</F.Legend>
|
||||
<F.HelperText>
|
||||
{{t 'resources.storage-bucket.form.role_tags.label'}}
|
||||
</F.HelperText>
|
||||
|
||||
{{#if @model.errors.role_tags}}
|
||||
<F.Error as |E|>
|
||||
{{#each @model.errors.role_tags as |error|}}
|
||||
<E.Message>{{error.message}}</E.Message>
|
||||
{{/each}}
|
||||
</F.Error>
|
||||
{{/if}}
|
||||
</:fieldset>
|
||||
<:field as |F|>
|
||||
<F.TextInput
|
||||
@name='role_tags'
|
||||
@options={{@model.role_tags}}
|
||||
@model={{@model}}
|
||||
/>
|
||||
</:field>
|
||||
</Form::Field::ListWrapper>
|
||||
{{/if}}
|
||||
|
||||
{{! worker_filter }}
|
||||
<Hds::Form::TextInput::Field
|
||||
name='worker_filter'
|
||||
@isRequired={{true}}
|
||||
@value={{@model.worker_filter}}
|
||||
@isInvalid={{@model.errors.worker_filter}}
|
||||
disabled={{form.disabled}}
|
||||
{{on 'input' (set-from-event @model 'worker_filter')}}
|
||||
as |F|
|
||||
>
|
||||
<F.Label>{{t 'resources.storage-bucket.form.worker_filter.label'}}</F.Label>
|
||||
<F.HelperText>
|
||||
{{t 'resources.storage-bucket.form.worker_filter.help'}}
|
||||
<Hds::Link::Inline @href={{doc-url 'storage-bucket.worker-filter'}}>
|
||||
{{t 'actions.learn-more'}}
|
||||
</Hds::Link::Inline>
|
||||
</F.HelperText>
|
||||
{{#if @model.errors.worker_filter}}
|
||||
<F.Error as |E|>
|
||||
{{#each @model.errors.worker_filter as |error|}}
|
||||
<E.Message>{{error.message}}</E.Message>
|
||||
{{/each}}
|
||||
</F.Error>
|
||||
{{/if}}
|
||||
</Hds::Form::TextInput::Field>
|
||||
|
||||
{{! JUST for static credentials }}
|
||||
{{#unless this.showDynamicCredentials}}
|
||||
{{! disable_credential_rotation }}
|
||||
<Hds::Form::Checkbox::Field
|
||||
name='disable_credential_rotation'
|
||||
disabled={{form.disabled}}
|
||||
checked={{@model.disable_credential_rotation}}
|
||||
{{on 'change' @toggleDisableCredentialRotation}}
|
||||
as |F|
|
||||
>
|
||||
<F.Label>{{t
|
||||
'resources.storage-bucket.form.disable_credential_rotation.label'
|
||||
}}</F.Label>
|
||||
<F.HelperText>
|
||||
{{t 'resources.storage-bucket.form.disable_credential_rotation.help'}}
|
||||
<Hds::Link::Inline
|
||||
@href={{doc-url 'storage-bucket.disable-credential-rotation'}}
|
||||
>
|
||||
{{t 'actions.learn-more'}}
|
||||
</Hds::Link::Inline>
|
||||
</F.HelperText>
|
||||
</Hds::Form::Checkbox::Field>
|
||||
{{/unless}}
|
||||
|
||||
{{! Actions }}
|
||||
{{#if (can 'save model' @model)}}
|
||||
<form.actions
|
||||
@enableEditText={{t 'actions.edit-form'}}
|
||||
@submitText={{t 'actions.save'}}
|
||||
@cancelText={{t 'actions.cancel'}}
|
||||
/>
|
||||
{{/if}}
|
||||
</Rose::Form>
|
||||
@@ -0,0 +1,38 @@
|
||||
import Component from '@glimmer/component';
|
||||
import { action } from '@ember/object';
|
||||
import {
|
||||
TYPES_CREDENTIALS,
|
||||
TYPE_CREDENTIAL_DYNAMIC,
|
||||
} from 'api/models/storage-bucket';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
|
||||
export default class FormStorageBucketAwsIndexComponent extends Component {
|
||||
// =attributes
|
||||
@tracked selectedCredentialType = this.args.model.credentialType;
|
||||
|
||||
@tracked showDynamicCredentials =
|
||||
this.args.model.credentialType === TYPE_CREDENTIAL_DYNAMIC;
|
||||
/**
|
||||
* returns an array of available credential types
|
||||
* @type {array}
|
||||
*/
|
||||
get credentials() {
|
||||
return TYPES_CREDENTIALS;
|
||||
}
|
||||
|
||||
// =actions
|
||||
/**
|
||||
* Allows to update the credential type
|
||||
* @param type {string}
|
||||
*/
|
||||
@action
|
||||
updateCredentialTypeSelection(type) {
|
||||
this.selectedCredentialType = type;
|
||||
if (type === TYPE_CREDENTIAL_DYNAMIC) {
|
||||
this.showDynamicCredentials = true;
|
||||
} else {
|
||||
this.showDynamicCredentials = false;
|
||||
}
|
||||
this.args.changeCredentialType(type);
|
||||
}
|
||||
}
|
||||
@@ -2,455 +2,18 @@
|
||||
Copyright (c) HashiCorp, Inc.
|
||||
SPDX-License-Identifier: MPL-2.0
|
||||
}}
|
||||
|
||||
<Rose::Form
|
||||
@onSubmit={{@submit}}
|
||||
@cancel={{this.cancel}}
|
||||
@disabled={{@model.isSaving}}
|
||||
@showEditToggle={{if @model.isNew false true}}
|
||||
as |form|
|
||||
>
|
||||
<Hds::Form::TextInput::Field
|
||||
@isOptional={{true}}
|
||||
@value={{@model.name}}
|
||||
@isInvalid={{@model.errors.name}}
|
||||
@type='text'
|
||||
name='name'
|
||||
disabled={{form.disabled}}
|
||||
{{on 'input' (set-from-event @model 'name')}}
|
||||
as |F|
|
||||
>
|
||||
<F.Label>{{t 'form.name.label'}}</F.Label>
|
||||
<F.HelperText>{{t 'form.name.help'}}</F.HelperText>
|
||||
{{#if @model.errors.name}}
|
||||
<F.Error as |E|>
|
||||
{{#each @model.errors.name as |error|}}
|
||||
<E.Message>{{error.message}}</E.Message>
|
||||
{{/each}}
|
||||
</F.Error>
|
||||
{{/if}}
|
||||
</Hds::Form::TextInput::Field>
|
||||
|
||||
<Hds::Form::Textarea::Field
|
||||
@isOptional={{true}}
|
||||
@value={{@model.description}}
|
||||
@isInvalid={{@model.errors.description}}
|
||||
name='description'
|
||||
disabled={{form.disabled}}
|
||||
as |F|
|
||||
>
|
||||
<F.Label>{{t 'form.description.label'}}</F.Label>
|
||||
<F.HelperText>{{t 'form.description.help'}}</F.HelperText>
|
||||
{{#if @model.errors.description}}
|
||||
<F.Error as |E|>
|
||||
{{#each @model.errors.description as |error|}}
|
||||
<E.Message>{{error.message}}</E.Message>
|
||||
{{/each}}
|
||||
</F.Error>
|
||||
{{/if}}
|
||||
</Hds::Form::Textarea::Field>
|
||||
|
||||
{{#if @model.isNew}}
|
||||
<Hds::Form::Select::Field
|
||||
@isRequired={{true}}
|
||||
@width='100%'
|
||||
disabled={{form.disabled}}
|
||||
name='scope'
|
||||
{{on 'change' this.updateScope}}
|
||||
as |F|
|
||||
>
|
||||
<F.Label>{{t 'resources.storage-bucket.form.scope.label'}}</F.Label>
|
||||
<F.HelperText>
|
||||
{{this.scopeFieldDescription}}
|
||||
</F.HelperText>
|
||||
<F.Options>
|
||||
<option disabled hidden selected value=''>
|
||||
{{t 'titles.choose-an-option'}}
|
||||
</option>
|
||||
{{#each @scopes as |scope|}}
|
||||
<option value={{scope.model.id}}>
|
||||
{{scope.model.displayName}}
|
||||
</option>
|
||||
{{/each}}
|
||||
</F.Options>
|
||||
</Hds::Form::Select::Field>
|
||||
{{else}}
|
||||
<InfoField
|
||||
@value={{@model.scopeModel.displayName}}
|
||||
@icon={{if @model.scopeModel.isGlobal 'globe' 'org'}}
|
||||
disabled={{form.disabled}}
|
||||
as |F|
|
||||
>
|
||||
<F.Label>{{t 'resources.storage-bucket.form.scope.label'}}</F.Label>
|
||||
<F.HelperText>
|
||||
{{#if @model.scopeModel.isGlobal}}
|
||||
{{t 'resources.storage-bucket.form.scope.help_global'}}
|
||||
{{else}}
|
||||
{{t 'resources.storage-bucket.form.scope.help_org'}}
|
||||
{{/if}}
|
||||
</F.HelperText>
|
||||
</InfoField>
|
||||
{{/if}}
|
||||
|
||||
<InfoField
|
||||
@value={{t 'resources.storage-bucket.titles.amazon_s3'}}
|
||||
@icon='aws-color'
|
||||
disabled={{form.disabled}}
|
||||
as |F|
|
||||
>
|
||||
<F.Label>{{t 'titles.provider'}}</F.Label>
|
||||
<F.HelperText>{{t
|
||||
'resources.storage-bucket.form.provider.help'
|
||||
}}</F.HelperText>
|
||||
</InfoField>
|
||||
|
||||
<Hds::Form::TextInput::Field
|
||||
@isRequired={{true}}
|
||||
@value={{@model.bucket_name}}
|
||||
@isInvalid={{@model.errors.bucket_name}}
|
||||
@type='text'
|
||||
name='bucket_name'
|
||||
disabled={{form.disabled}}
|
||||
readOnly={{not @model.isNew}}
|
||||
{{on 'input' (set-from-event @model 'bucket_name')}}
|
||||
as |F|
|
||||
>
|
||||
<F.Label>{{t 'resources.storage-bucket.form.bucket_name.label'}}</F.Label>
|
||||
<F.HelperText>{{t
|
||||
'resources.storage-bucket.form.bucket_name.help'
|
||||
}}</F.HelperText>
|
||||
{{#if @model.errors.bucket_name}}
|
||||
<F.Error as |E|>
|
||||
{{#each @model.errors.bucket_name as |error|}}
|
||||
<E.Message>{{error.message}}</E.Message>
|
||||
{{/each}}
|
||||
</F.Error>
|
||||
{{/if}}
|
||||
</Hds::Form::TextInput::Field>
|
||||
|
||||
<Hds::Form::TextInput::Field
|
||||
@isOptional={{true}}
|
||||
@value={{@model.bucket_prefix}}
|
||||
@isInvalid={{@model.errors.bucket_prefix}}
|
||||
@type='text'
|
||||
name='bucket_prefix'
|
||||
disabled={{form.disabled}}
|
||||
readOnly={{not @model.isNew}}
|
||||
{{on 'input' (set-from-event @model 'bucket_prefix')}}
|
||||
as |F|
|
||||
>
|
||||
<F.Label>{{t 'resources.storage-bucket.form.bucket_prefix.label'}}</F.Label>
|
||||
<F.HelperText>{{t
|
||||
'resources.storage-bucket.form.bucket_prefix.help'
|
||||
}}</F.HelperText>
|
||||
{{#if @model.errors.bucket_prefix}}
|
||||
<F.Error as |E|>
|
||||
{{#each @model.errors.bucket_prefix as |error|}}
|
||||
<E.Message>{{error.message}}</E.Message>
|
||||
{{/each}}
|
||||
</F.Error>
|
||||
{{/if}}
|
||||
</Hds::Form::TextInput::Field>
|
||||
|
||||
<Hds::Form::TextInput::Field
|
||||
@isRequired={{true}}
|
||||
@value={{@model.region}}
|
||||
@isInvalid={{@model.errors.region}}
|
||||
@type='text'
|
||||
name='region'
|
||||
disabled={{form.disabled}}
|
||||
readOnly={{not @model.isNew}}
|
||||
{{on 'input' (set-from-event @model 'region')}}
|
||||
as |F|
|
||||
>
|
||||
<F.Label>{{t 'resources.storage-bucket.form.region.label'}}</F.Label>
|
||||
<F.HelperText>{{t
|
||||
'resources.storage-bucket.form.region.help'
|
||||
}}</F.HelperText>
|
||||
{{#if @model.errors.region}}
|
||||
<F.Error as |E|>
|
||||
{{#each @model.errors.region as |error|}}
|
||||
<E.Message>{{error.message}}</E.Message>
|
||||
{{/each}}
|
||||
</F.Error>
|
||||
{{/if}}
|
||||
</Hds::Form::TextInput::Field>
|
||||
|
||||
<Hds::Form::RadioCard::Group @name='credential_type' as |G|>
|
||||
<G.Legend>{{t 'resources.storage-bucket.types.credential'}}</G.Legend>
|
||||
{{#each this.credentials as |credentialType|}}
|
||||
<G.RadioCard
|
||||
@name={{credentialType}}
|
||||
@value={{credentialType}}
|
||||
@checked={{eq credentialType this.selectedCredentialType}}
|
||||
@disabled={{form.disabled}}
|
||||
{{on 'input' (fn this.updateTypeSelection credentialType)}}
|
||||
as |R|
|
||||
>
|
||||
<R.Label>{{t
|
||||
(concat 'resources.storage-bucket.types.' credentialType '.title')
|
||||
}}</R.Label>
|
||||
<R.Description>{{t
|
||||
(concat
|
||||
'resources.storage-bucket.types.' credentialType '.description'
|
||||
)
|
||||
}}</R.Description>
|
||||
</G.RadioCard>
|
||||
|
||||
{{/each}}
|
||||
</Hds::Form::RadioCard::Group>
|
||||
|
||||
{{#unless this.showDynamicCredentials}}
|
||||
|
||||
{{#if @model.isNew}}
|
||||
<Hds::Form::TextInput::Field
|
||||
@isRequired={{true}}
|
||||
@value={{@model.access_key_id}}
|
||||
@isInvalid={{@model.errors.secrets}}
|
||||
@type='password'
|
||||
name='access_key_id'
|
||||
@hasVisibilityToggle={{false}}
|
||||
{{on 'input' (set-from-event @model 'access_key_id')}}
|
||||
as |F|
|
||||
>
|
||||
<F.Label>{{t
|
||||
'resources.storage-bucket.form.access_key_id.label'
|
||||
}}</F.Label>
|
||||
<F.HelperText>{{t
|
||||
'resources.storage-bucket.form.access_key_id.help'
|
||||
}}</F.HelperText>
|
||||
{{#if @model.errors.secrets}}
|
||||
<F.Error as |E|>
|
||||
{{#each @model.errors.secrets as |error|}}
|
||||
<E.Message>{{error.message}}</E.Message>
|
||||
{{/each}}
|
||||
</F.Error>
|
||||
{{/if}}
|
||||
</Hds::Form::TextInput::Field>
|
||||
|
||||
<Hds::Form::TextInput::Field
|
||||
@isRequired={{true}}
|
||||
@value={{@model.secret_access_key}}
|
||||
@isInvalid={{@model.errors.secrets}}
|
||||
@type='password'
|
||||
name='secret_access_key'
|
||||
disabled={{form.disabled}}
|
||||
@hasVisibilityToggle={{false}}
|
||||
{{on 'input' (set-from-event @model 'secret_access_key')}}
|
||||
as |F|
|
||||
>
|
||||
<F.Label>{{t
|
||||
'resources.storage-bucket.form.secret_access_key.label'
|
||||
}}</F.Label>
|
||||
<F.HelperText>{{t
|
||||
'resources.storage-bucket.form.secret_access_key.help'
|
||||
}}</F.HelperText>
|
||||
{{#if @model.errors.secrets}}
|
||||
<F.Error as |E|>
|
||||
{{#each @model.errors.secrets as |error|}}
|
||||
<E.Message>{{error.message}}</E.Message>
|
||||
{{/each}}
|
||||
</F.Error>
|
||||
{{/if}}
|
||||
</Hds::Form::TextInput::Field>
|
||||
{{else}}
|
||||
<Form::Field::SecretInput
|
||||
@isRequired={{true}}
|
||||
@value={{@model.access_key_id}}
|
||||
@isInvalid={{@model.errors.access_key_id}}
|
||||
@name='access_key_id'
|
||||
@isDisabled={{form.disabled}}
|
||||
@showEditButton={{true}}
|
||||
@cancel={{fn this.rollbackSecretAttrs 'access_key_id'}}
|
||||
{{on 'input' (set-from-event @model 'access_key_id')}}
|
||||
as |F|
|
||||
>
|
||||
<F.Label>{{t
|
||||
'resources.storage-bucket.form.access_key_id.label'
|
||||
}}</F.Label>
|
||||
<F.HelperText>{{t
|
||||
'resources.storage-bucket.form.access_key_id.help'
|
||||
}}</F.HelperText>
|
||||
</Form::Field::SecretInput>
|
||||
|
||||
<Form::Field::SecretInput
|
||||
@isRequired={{true}}
|
||||
@value={{@model.secret_access_key}}
|
||||
@isInvalid={{@model.errors.secret_access_key}}
|
||||
@name='secret_access_key'
|
||||
@isDisabled={{form.disabled}}
|
||||
@showEditButton={{true}}
|
||||
@cancel={{fn this.rollbackSecretAttrs 'secret_access_key'}}
|
||||
{{on 'input' (set-from-event @model 'secret_access_key')}}
|
||||
as |F|
|
||||
>
|
||||
<F.Label>{{t
|
||||
'resources.storage-bucket.form.secret_access_key.label'
|
||||
}}</F.Label>
|
||||
<F.HelperText>{{t
|
||||
'resources.storage-bucket.form.secret_access_key.help'
|
||||
}}</F.HelperText>
|
||||
</Form::Field::SecretInput>
|
||||
{{/if}}
|
||||
{{/unless}}
|
||||
|
||||
{{#if this.showDynamicCredentials}}
|
||||
<Hds::Form::TextInput::Field
|
||||
@isRequired={{true}}
|
||||
@value={{@model.role_arn}}
|
||||
@isInvalid={{@model.errors.role_arn}}
|
||||
@type='text'
|
||||
name='role_arn'
|
||||
disabled={{form.disabled}}
|
||||
{{on 'input' (set-from-event @model 'role_arn')}}
|
||||
as |F|
|
||||
>
|
||||
<F.Label>{{t 'resources.storage-bucket.form.role_arn.label'}}</F.Label>
|
||||
<F.HelperText>
|
||||
{{t 'resources.storage-bucket.form.role_arn.help'}}
|
||||
</F.HelperText>
|
||||
{{#if @model.errors.role_arn}}
|
||||
<F.Error as |E|>
|
||||
{{#each @model.errors.role_arn as |error|}}
|
||||
<E.Message>{{error.message}}</E.Message>
|
||||
{{/each}}
|
||||
</F.Error>
|
||||
{{/if}}
|
||||
</Hds::Form::TextInput::Field>
|
||||
|
||||
<Hds::Form::TextInput::Field
|
||||
@isOptional={{true}}
|
||||
@value={{@model.role_external_id}}
|
||||
@isInvalid={{@model.errors.role_external_id}}
|
||||
@type='text'
|
||||
name='role_external_id'
|
||||
disabled={{form.disabled}}
|
||||
{{on 'input' (set-from-event @model 'role_external_id')}}
|
||||
as |F|
|
||||
>
|
||||
<F.Label>
|
||||
{{t 'resources.storage-bucket.form.role_external_id.label'}}
|
||||
</F.Label>
|
||||
<F.HelperText>
|
||||
{{t 'resources.storage-bucket.form.role_external_id.help'}}
|
||||
</F.HelperText>
|
||||
{{#if @model.errors.role_external_id}}
|
||||
<F.Error as |E|>
|
||||
{{#each @model.errors.role_external_id as |error|}}
|
||||
<E.Message>{{error.message}}</E.Message>
|
||||
{{/each}}
|
||||
</F.Error>
|
||||
{{/if}}
|
||||
</Hds::Form::TextInput::Field>
|
||||
|
||||
<Hds::Form::TextInput::Field
|
||||
@isOptional={{true}}
|
||||
@value={{@model.role_session_name}}
|
||||
@isInvalid={{@model.errors.role_session_name}}
|
||||
@type='text'
|
||||
name='role_session_name'
|
||||
disabled={{form.disabled}}
|
||||
{{on 'input' (set-from-event @model 'role_session_name')}}
|
||||
as |F|
|
||||
>
|
||||
<F.Label>
|
||||
{{t 'resources.storage-bucket.form.role_session_name.label'}}
|
||||
</F.Label>
|
||||
<F.HelperText>
|
||||
{{t 'resources.storage-bucket.form.role_session_name.help'}}
|
||||
</F.HelperText>
|
||||
{{#if @model.errors.role_session_name}}
|
||||
<F.Error as |E|>
|
||||
{{#each @model.errors.role_session_name as |error|}}
|
||||
<E.Message>{{error.message}}</E.Message>
|
||||
{{/each}}
|
||||
</F.Error>
|
||||
{{/if}}
|
||||
</Hds::Form::TextInput::Field>
|
||||
|
||||
<Form::Field::ListWrapper
|
||||
@layout='horizontal'
|
||||
@isOptional={{true}}
|
||||
@disabled={{form.disabled}}
|
||||
>
|
||||
<:fieldset as |F|>
|
||||
<F.Legend>
|
||||
{{t 'resources.storage-bucket.form.role_tags.label'}}
|
||||
</F.Legend>
|
||||
<F.HelperText>
|
||||
{{t 'resources.storage-bucket.form.role_tags.label'}}
|
||||
</F.HelperText>
|
||||
|
||||
{{#if @model.errors.role_tags}}
|
||||
<F.Error as |E|>
|
||||
{{#each @model.errors.role_tags as |error|}}
|
||||
<E.Message>{{error.message}}</E.Message>
|
||||
{{/each}}
|
||||
</F.Error>
|
||||
{{/if}}
|
||||
</:fieldset>
|
||||
<:field as |F|>
|
||||
<F.TextInput
|
||||
@name='role_tags'
|
||||
@options={{@model.role_tags}}
|
||||
@model={{@model}}
|
||||
/>
|
||||
</:field>
|
||||
</Form::Field::ListWrapper>
|
||||
{{/if}}
|
||||
|
||||
<Hds::Form::TextInput::Field
|
||||
@isRequired={{true}}
|
||||
@value={{@model.worker_filter}}
|
||||
@isInvalid={{@model.errors.worker_filter}}
|
||||
@type='text'
|
||||
name='worker_filter'
|
||||
disabled={{form.disabled}}
|
||||
{{on 'input' (set-from-event @model 'worker_filter')}}
|
||||
as |F|
|
||||
>
|
||||
<F.Label>{{t 'resources.storage-bucket.form.worker_filter.label'}}</F.Label>
|
||||
<F.HelperText>
|
||||
{{t 'resources.storage-bucket.form.worker_filter.help'}}
|
||||
<Hds::Link::Inline @href={{doc-url 'storage-bucket.worker-filter'}}>
|
||||
{{t 'actions.learn-more'}}
|
||||
</Hds::Link::Inline>
|
||||
</F.HelperText>
|
||||
{{#if @model.errors.worker_filter}}
|
||||
<F.Error as |E|>
|
||||
{{#each @model.errors.worker_filter as |error|}}
|
||||
<E.Message>{{error.message}}</E.Message>
|
||||
{{/each}}
|
||||
</F.Error>
|
||||
{{/if}}
|
||||
</Hds::Form::TextInput::Field>
|
||||
|
||||
{{#unless this.showDynamicCredentials}}
|
||||
<Hds::Form::Checkbox::Field
|
||||
name='disable_credential_rotation'
|
||||
disabled={{form.disabled}}
|
||||
checked={{@model.disable_credential_rotation}}
|
||||
{{on 'change' this.toggleDisableCredentialRotation}}
|
||||
as |F|
|
||||
>
|
||||
<F.Label>{{t
|
||||
'resources.storage-bucket.form.disable_credential_rotation.label'
|
||||
}}</F.Label>
|
||||
<F.HelperText>
|
||||
{{t 'resources.storage-bucket.form.disable_credential_rotation.help'}}
|
||||
<Hds::Link::Inline
|
||||
@href={{doc-url 'storage-bucket.disable-credential-rotation'}}
|
||||
>
|
||||
{{t 'actions.learn-more'}}
|
||||
</Hds::Link::Inline>
|
||||
</F.HelperText>
|
||||
</Hds::Form::Checkbox::Field>
|
||||
{{/unless}}
|
||||
{{#if (can 'save model' @model)}}
|
||||
<form.actions
|
||||
@enableEditText={{t 'actions.edit-form'}}
|
||||
@submitText={{t 'actions.save'}}
|
||||
@cancelText={{t 'actions.cancel'}}
|
||||
/>
|
||||
{{/if}}
|
||||
</Rose::Form>
|
||||
{{#if @model.type}}
|
||||
{{component
|
||||
(concat 'form/storage-bucket/' @model.compositeType)
|
||||
model=@model
|
||||
submit=@submit
|
||||
cancel=@cancel
|
||||
updateScope=this.updateScope
|
||||
scopes=@scopes
|
||||
pluginTypes=this.pluginTypes
|
||||
changePluginType=@changePluginType
|
||||
changeCredentialType=@changeCredentialType
|
||||
rollbackSecretAttrs=this.rollbackSecretAttrs
|
||||
toggleDisableCredentialRotation=this.toggleDisableCredentialRotation
|
||||
}}
|
||||
{{/if}}
|
||||
@@ -5,64 +5,26 @@
|
||||
|
||||
import Component from '@glimmer/component';
|
||||
import { action } from '@ember/object';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { set } from '@ember/object';
|
||||
|
||||
import {
|
||||
TYPES_CREDENTIALS,
|
||||
TYPE_CREDENTIAL_DYNAMIC,
|
||||
TYPES_STORAGE_BUCKET_PLUGIN,
|
||||
} from 'api/models/storage-bucket';
|
||||
|
||||
export default class FormStorageBucketComponent extends Component {
|
||||
// =attributes
|
||||
@service intl;
|
||||
|
||||
@tracked scopeFieldDescription = this.intl.t(
|
||||
'resources.storage-bucket.form.scope.help',
|
||||
);
|
||||
|
||||
@tracked selectedCredentialType = this.args.model.credentialType;
|
||||
|
||||
@tracked showDynamicCredentials =
|
||||
this.args.model.credentialType === TYPE_CREDENTIAL_DYNAMIC;
|
||||
|
||||
/**
|
||||
* returns an array of available credential types
|
||||
* @type {object}
|
||||
* returns an array of available storage bucket plugin types
|
||||
*/
|
||||
get credentials() {
|
||||
return TYPES_CREDENTIALS;
|
||||
get pluginTypes() {
|
||||
return TYPES_STORAGE_BUCKET_PLUGIN;
|
||||
}
|
||||
|
||||
// =actions
|
||||
/**
|
||||
* Allows to update the credential type
|
||||
* @param type {string}
|
||||
*/
|
||||
@action
|
||||
updateTypeSelection(type) {
|
||||
this.selectedCredentialType = type;
|
||||
if (type === TYPE_CREDENTIAL_DYNAMIC) {
|
||||
this.showDynamicCredentials = true;
|
||||
} else {
|
||||
this.showDynamicCredentials = false;
|
||||
}
|
||||
this.args.changeType(type);
|
||||
}
|
||||
|
||||
updateScopeFieldDescription() {
|
||||
if (this.args.model.scopeModel.isGlobal) {
|
||||
this.scopeFieldDescription = this.intl.t(
|
||||
'resources.storage-bucket.form.scope.help_global',
|
||||
);
|
||||
} else {
|
||||
this.scopeFieldDescription = this.intl.t(
|
||||
'resources.storage-bucket.form.scope.help_org',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
updateScope(event) {
|
||||
const selectedScopeId = event.target.value;
|
||||
@@ -70,7 +32,6 @@ export default class FormStorageBucketComponent extends Component {
|
||||
(element) => element.model.id === selectedScopeId,
|
||||
).model;
|
||||
this.args.model.scopeModel = selectedScopeModel;
|
||||
this.updateScopeFieldDescription();
|
||||
}
|
||||
|
||||
@action
|
||||
|
||||
@@ -0,0 +1,353 @@
|
||||
<Rose::Form
|
||||
@onSubmit={{@submit}}
|
||||
@cancel={{@cancel}}
|
||||
@disabled={{@model.isSaving}}
|
||||
@showEditToggle={{if @model.isNew false true}}
|
||||
as |form|
|
||||
>
|
||||
{{! name }}
|
||||
<Hds::Form::TextInput::Field
|
||||
name='name'
|
||||
@isOptional={{true}}
|
||||
@value={{@model.name}}
|
||||
@isInvalid={{@model.errors.name}}
|
||||
@type='text'
|
||||
disabled={{form.disabled}}
|
||||
{{on 'input' (set-from-event @model 'name')}}
|
||||
as |F|
|
||||
>
|
||||
<F.Label>{{t 'form.name.label'}}</F.Label>
|
||||
<F.HelperText>{{t 'form.name.help'}}</F.HelperText>
|
||||
{{#if @model.errors.name}}
|
||||
<F.Error as |E|>
|
||||
{{#each @model.errors.name as |error|}}
|
||||
<E.Message>{{error.message}}</E.Message>
|
||||
{{/each}}
|
||||
</F.Error>
|
||||
{{/if}}
|
||||
</Hds::Form::TextInput::Field>
|
||||
|
||||
{{! description }}
|
||||
<Hds::Form::Textarea::Field
|
||||
name='description'
|
||||
@isOptional={{true}}
|
||||
@value={{@model.description}}
|
||||
@isInvalid={{@model.errors.description}}
|
||||
disabled={{form.disabled}}
|
||||
as |F|
|
||||
>
|
||||
<F.Label>{{t 'form.description.label'}}</F.Label>
|
||||
<F.HelperText>{{t 'form.description.help'}}</F.HelperText>
|
||||
{{#if @model.errors.description}}
|
||||
<F.Error as |E|>
|
||||
{{#each @model.errors.description as |error|}}
|
||||
<E.Message>{{error.message}}</E.Message>
|
||||
{{/each}}
|
||||
</F.Error>
|
||||
{{/if}}
|
||||
</Hds::Form::Textarea::Field>
|
||||
|
||||
{{! scope }}
|
||||
{{#if @model.isNew}}
|
||||
<Hds::Form::Select::Field
|
||||
name='scope'
|
||||
@isRequired={{true}}
|
||||
@width='100%'
|
||||
disabled={{form.disabled}}
|
||||
{{on 'change' @updateScope}}
|
||||
as |F|
|
||||
>
|
||||
<F.Label>{{t 'resources.storage-bucket.form.scope.label'}}</F.Label>
|
||||
<F.HelperText>
|
||||
{{#if @model.scope}}
|
||||
{{#if @model.scope.isGlobal}}
|
||||
{{t 'resources.storage-bucket.form.scope.help_global'}}
|
||||
{{else if @model.scope.isOrg}}
|
||||
{{t 'resources.storage-bucket.form.scope.help_org'}}
|
||||
{{/if}}
|
||||
{{else}}
|
||||
{{t 'resources.storage-bucket.form.scope.help'}}
|
||||
{{/if}}
|
||||
</F.HelperText>
|
||||
<F.Options>
|
||||
<option disabled selected={{not @model.scope}} value=''>
|
||||
{{t 'titles.choose-an-option'}}
|
||||
</option>
|
||||
{{#each @scopes as |scope|}}
|
||||
<option
|
||||
value={{scope.model.id}}
|
||||
selected={{eq scope.model.id @model.scope.scope_id}}
|
||||
>
|
||||
{{scope.model.displayName}}
|
||||
</option>
|
||||
{{/each}}
|
||||
</F.Options>
|
||||
</Hds::Form::Select::Field>
|
||||
{{else}}
|
||||
<InfoField
|
||||
@value={{@model.scopeModel.displayName}}
|
||||
@icon={{if @model.scopeModel.isGlobal 'globe' 'org'}}
|
||||
disabled={{form.disabled}}
|
||||
as |F|
|
||||
>
|
||||
<F.Label>{{t 'resources.storage-bucket.form.scope.label'}}</F.Label>
|
||||
<F.HelperText>
|
||||
{{#if @model.scopeModel.isGlobal}}
|
||||
{{t 'resources.storage-bucket.form.scope.help_global'}}
|
||||
{{else}}
|
||||
{{t 'resources.storage-bucket.form.scope.help_org'}}
|
||||
{{/if}}
|
||||
</F.HelperText>
|
||||
</InfoField>
|
||||
{{/if}}
|
||||
|
||||
{{! Provider / plugin.name }}
|
||||
<Hds::Form::RadioCard::Group @name='plugin_type' @alignment='center' as |G|>
|
||||
<G.Legend>{{t 'titles.provider'}}</G.Legend>
|
||||
<G.HelperText>{{t 'descriptions.choose-a-provider'}}</G.HelperText>
|
||||
|
||||
{{#each @pluginTypes as |pluginType|}}
|
||||
<G.RadioCard
|
||||
@value={{pluginType}}
|
||||
@checked={{eq pluginType @model.compositeType}}
|
||||
@disabled={{(not @model.isNew)}}
|
||||
{{on 'input' (fn @changePluginType pluginType)}}
|
||||
as |R|
|
||||
>
|
||||
<R.Label>{{t
|
||||
(concat 'resources.storage-bucket.plugin-types.' pluginType)
|
||||
}}</R.Label>
|
||||
{{#if (eq pluginType 'aws')}}
|
||||
<R.Icon @name='aws-color' />
|
||||
{{else}}
|
||||
<R.Icon @name='cloud-upload' />
|
||||
{{/if}}
|
||||
</G.RadioCard>
|
||||
{{/each}}
|
||||
</Hds::Form::RadioCard::Group>
|
||||
|
||||
{{! endpoint_url }}
|
||||
<Hds::Form::TextInput::Field
|
||||
name='endpoint_url'
|
||||
@isRequired={{true}}
|
||||
@value={{@model.endpoint_url}}
|
||||
@isInvalid={{@model.errors.endpoint_url}}
|
||||
disabled={{form.disabled}}
|
||||
readOnly={{not @model.isNew}}
|
||||
{{on 'input' (set-from-event @model 'endpoint_url')}}
|
||||
as |F|
|
||||
>
|
||||
<F.Label>{{t 'resources.storage-bucket.form.endpoint_url.label'}}</F.Label>
|
||||
<F.HelperText>{{t
|
||||
'resources.storage-bucket.form.endpoint_url.help'
|
||||
}}</F.HelperText>
|
||||
{{#if @model.errors.endpoint_url}}
|
||||
<F.Error as |E|>
|
||||
{{#each @model.errors.endpoint_url as |error|}}
|
||||
<E.Message>{{error.message}}</E.Message>
|
||||
{{/each}}
|
||||
</F.Error>
|
||||
{{/if}}
|
||||
</Hds::Form::TextInput::Field>
|
||||
|
||||
{{! bucket_name }}
|
||||
<Hds::Form::TextInput::Field
|
||||
name='bucket_name'
|
||||
@isRequired={{true}}
|
||||
@value={{@model.bucket_name}}
|
||||
@isInvalid={{@model.errors.bucket_name}}
|
||||
disabled={{form.disabled}}
|
||||
readOnly={{not @model.isNew}}
|
||||
{{on 'input' (set-from-event @model 'bucket_name')}}
|
||||
as |F|
|
||||
>
|
||||
<F.Label>{{t 'resources.storage-bucket.form.bucket_name.label'}}</F.Label>
|
||||
<F.HelperText>{{t
|
||||
'resources.storage-bucket.form.bucket_name.help'
|
||||
}}</F.HelperText>
|
||||
{{#if @model.errors.bucket_name}}
|
||||
<F.Error as |E|>
|
||||
{{#each @model.errors.bucket_name as |error|}}
|
||||
<E.Message>{{error.message}}</E.Message>
|
||||
{{/each}}
|
||||
</F.Error>
|
||||
{{/if}}
|
||||
</Hds::Form::TextInput::Field>
|
||||
|
||||
{{! region }}
|
||||
<Hds::Form::TextInput::Field
|
||||
name='region'
|
||||
@isOptional={{true}}
|
||||
@value={{@model.region}}
|
||||
@isInvalid={{@model.errors.region}}
|
||||
disabled={{form.disabled}}
|
||||
readOnly={{not @model.isNew}}
|
||||
{{on 'input' (set-from-event @model 'region')}}
|
||||
as |F|
|
||||
>
|
||||
<F.Label>{{t 'resources.storage-bucket.form.region.label'}}</F.Label>
|
||||
<F.HelperText>{{t
|
||||
'resources.storage-bucket.form.region.help'
|
||||
}}</F.HelperText>
|
||||
{{#if @model.errors.region}}
|
||||
<F.Error as |E|>
|
||||
{{#each @model.errors.region as |error|}}
|
||||
<E.Message>{{error.message}}</E.Message>
|
||||
{{/each}}
|
||||
</F.Error>
|
||||
{{/if}}
|
||||
</Hds::Form::TextInput::Field>
|
||||
|
||||
{{! Creation }}
|
||||
{{#if @model.isNew}}
|
||||
{{! access_key }}
|
||||
<Hds::Form::TextInput::Field
|
||||
name='access_key_id'
|
||||
@type='password'
|
||||
@isRequired={{true}}
|
||||
@value={{@model.access_key_id}}
|
||||
@isInvalid={{@model.errors.secrets}}
|
||||
@hasVisibilityToggle={{false}}
|
||||
{{on 'input' (set-from-event @model 'access_key_id')}}
|
||||
as |F|
|
||||
>
|
||||
<F.Label>
|
||||
{{t 'resources.storage-bucket.form.access_key_id.label'}}
|
||||
</F.Label>
|
||||
<F.HelperText>{{t
|
||||
'resources.storage-bucket.form.access_key_id.help'
|
||||
}}</F.HelperText>
|
||||
{{#if @model.errors.secrets}}
|
||||
<F.Error as |E|>
|
||||
{{#each @model.errors.secrets as |error|}}
|
||||
<E.Message>{{error.message}}</E.Message>
|
||||
{{/each}}
|
||||
</F.Error>
|
||||
{{/if}}
|
||||
</Hds::Form::TextInput::Field>
|
||||
|
||||
{{! secret_access_key }}
|
||||
<Hds::Form::TextInput::Field
|
||||
name='secret_access_key'
|
||||
@type='password'
|
||||
@isRequired={{true}}
|
||||
@value={{@model.secret_access_key}}
|
||||
@isInvalid={{@model.errors.secrets}}
|
||||
disabled={{form.disabled}}
|
||||
@hasVisibilityToggle={{false}}
|
||||
{{on 'input' (set-from-event @model 'secret_access_key')}}
|
||||
as |F|
|
||||
>
|
||||
<F.Label>{{t
|
||||
'resources.storage-bucket.form.secret_access_key.label'
|
||||
}}</F.Label>
|
||||
<F.HelperText>{{t
|
||||
'resources.storage-bucket.form.secret_access_key.help'
|
||||
}}</F.HelperText>
|
||||
{{#if @model.errors.secrets}}
|
||||
<F.Error as |E|>
|
||||
{{#each @model.errors.secrets as |error|}}
|
||||
<E.Message>{{error.message}}</E.Message>
|
||||
{{/each}}
|
||||
</F.Error>
|
||||
{{/if}}
|
||||
</Hds::Form::TextInput::Field>
|
||||
{{! Edit }}
|
||||
{{else}}
|
||||
{{! access_key }}
|
||||
<Form::Field::SecretInput
|
||||
@name='access_key_id'
|
||||
@isRequired={{true}}
|
||||
@value={{@model.access_key_id}}
|
||||
@isInvalid={{@model.errors.access_key_id}}
|
||||
@isDisabled={{form.disabled}}
|
||||
@showEditButton={{true}}
|
||||
@cancel={{fn @rollbackSecretAttrs 'access_key_id'}}
|
||||
{{on 'input' (set-from-event @model 'access_key_id')}}
|
||||
as |F|
|
||||
>
|
||||
<F.Label>{{t
|
||||
'resources.storage-bucket.form.access_key_id.label'
|
||||
}}</F.Label>
|
||||
<F.HelperText>{{t
|
||||
'resources.storage-bucket.form.access_key_id.help'
|
||||
}}</F.HelperText>
|
||||
</Form::Field::SecretInput>
|
||||
|
||||
{{! secret_access_key }}
|
||||
<Form::Field::SecretInput
|
||||
@name='secret_access_key'
|
||||
@isRequired={{true}}
|
||||
@value={{@model.secret_access_key}}
|
||||
@isInvalid={{@model.errors.secret_access_key}}
|
||||
@isDisabled={{form.disabled}}
|
||||
@showEditButton={{true}}
|
||||
@cancel={{fn @rollbackSecretAttrs 'secret_access_key'}}
|
||||
{{on 'input' (set-from-event @model 'secret_access_key')}}
|
||||
as |F|
|
||||
>
|
||||
<F.Label>{{t
|
||||
'resources.storage-bucket.form.secret_access_key.label'
|
||||
}}</F.Label>
|
||||
<F.HelperText>{{t
|
||||
'resources.storage-bucket.form.secret_access_key.help'
|
||||
}}</F.HelperText>
|
||||
</Form::Field::SecretInput>
|
||||
{{/if}}
|
||||
|
||||
{{! worker_filter }}
|
||||
<Hds::Form::TextInput::Field
|
||||
name='worker_filter'
|
||||
@isRequired={{true}}
|
||||
@value={{@model.worker_filter}}
|
||||
@isInvalid={{@model.errors.worker_filter}}
|
||||
disabled={{form.disabled}}
|
||||
{{on 'input' (set-from-event @model 'worker_filter')}}
|
||||
as |F|
|
||||
>
|
||||
<F.Label>{{t 'resources.storage-bucket.form.worker_filter.label'}}</F.Label>
|
||||
<F.HelperText>
|
||||
{{t 'resources.storage-bucket.form.worker_filter.help'}}
|
||||
<Hds::Link::Inline @href={{doc-url 'storage-bucket.worker-filter'}}>
|
||||
{{t 'actions.learn-more'}}
|
||||
</Hds::Link::Inline>
|
||||
</F.HelperText>
|
||||
{{#if @model.errors.worker_filter}}
|
||||
<F.Error as |E|>
|
||||
{{#each @model.errors.worker_filter as |error|}}
|
||||
<E.Message>{{error.message}}</E.Message>
|
||||
{{/each}}
|
||||
</F.Error>
|
||||
{{/if}}
|
||||
</Hds::Form::TextInput::Field>
|
||||
|
||||
{{! disable_credential_rotation }}
|
||||
<Hds::Form::Checkbox::Field
|
||||
name='disable_credential_rotation'
|
||||
disabled={{form.disabled}}
|
||||
checked={{@model.disable_credential_rotation}}
|
||||
{{on 'change' @toggleDisableCredentialRotation}}
|
||||
as |F|
|
||||
>
|
||||
<F.Label>{{t
|
||||
'resources.storage-bucket.form.disable_credential_rotation.label'
|
||||
}}</F.Label>
|
||||
<F.HelperText>
|
||||
{{t 'resources.storage-bucket.form.disable_credential_rotation.help'}}
|
||||
<Hds::Link::Inline
|
||||
@href={{doc-url 'storage-bucket.disable-credential-rotation'}}
|
||||
>
|
||||
{{t 'actions.learn-more'}}
|
||||
</Hds::Link::Inline>
|
||||
</F.HelperText>
|
||||
</Hds::Form::Checkbox::Field>
|
||||
|
||||
{{! Actions }}
|
||||
{{#if (can 'save model' @model)}}
|
||||
<form.actions
|
||||
@enableEditText={{t 'actions.edit-form'}}
|
||||
@submitText={{t 'actions.save'}}
|
||||
@cancelText={{t 'actions.cancel'}}
|
||||
/>
|
||||
{{/if}}
|
||||
</Rose::Form>
|
||||
@@ -0,0 +1,78 @@
|
||||
import Controller from '@ember/controller';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { action } from '@ember/object';
|
||||
import { loading } from 'ember-loading';
|
||||
import { confirm } from 'core/decorators/confirm';
|
||||
import { notifySuccess, notifyError } from 'core/decorators/notify';
|
||||
|
||||
export default class ScopesScopeStorageBucketsIndexController extends Controller {
|
||||
// =services
|
||||
@service router;
|
||||
|
||||
// =actions
|
||||
|
||||
/**
|
||||
* Handle save
|
||||
* @param {StorageBucketModel} storageBucket
|
||||
*/
|
||||
@action
|
||||
@loading
|
||||
@notifyError(({ message }) => message)
|
||||
@notifySuccess('notifications.save-success')
|
||||
async save(storageBucket) {
|
||||
await storageBucket.save();
|
||||
await this.router.transitionTo(
|
||||
'scopes.scope.storage-buckets.storage-bucket',
|
||||
storageBucket,
|
||||
);
|
||||
await this.router.refresh();
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the storage bucket.
|
||||
* @param {StorageBucketModel} storageBucket
|
||||
*/
|
||||
@action
|
||||
@loading
|
||||
@confirm('resources.storage-bucket.questions.delete-storage-bucket.message', {
|
||||
title: 'resources.storage-bucket.questions.delete-storage-bucket.title',
|
||||
confirm: 'resources.storage-bucket.actions.delete',
|
||||
})
|
||||
@notifyError(({ message }) => message, { catch: true })
|
||||
@notifySuccess('notifications.delete-success')
|
||||
async delete(storageBucket) {
|
||||
await storageBucket.destroyRecord();
|
||||
}
|
||||
|
||||
/**
|
||||
* Rollback changes on storage buckets.
|
||||
* @param {StorageBucketModel} storageBucket
|
||||
*/
|
||||
@action
|
||||
cancel(storageBucket) {
|
||||
const { isNew } = storageBucket;
|
||||
storageBucket.rollbackAttributes();
|
||||
if (isNew) this.router.transitionTo('scopes.scope.storage-buckets');
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates credentil type
|
||||
* @param {object} storageBucket
|
||||
* @param {string} credentialType
|
||||
*/
|
||||
@action
|
||||
changeCredentialType(storageBucket, credentialType) {
|
||||
storageBucket.credentialType = credentialType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the plugin type.
|
||||
* @param {*} pluginType
|
||||
*/
|
||||
@action
|
||||
async changePluginType(pluginType) {
|
||||
await this.router.replaceWith({
|
||||
queryParams: { compositeType: pluginType },
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import Controller, { inject as controller } from '@ember/controller';
|
||||
import { inject as service } from '@ember/service';
|
||||
|
||||
export default class ScopesScopeStorageBucketsNewController extends Controller {
|
||||
@controller('scopes/scope/storage-buckets/index')
|
||||
storageBuckets;
|
||||
|
||||
// =services
|
||||
@service router;
|
||||
|
||||
// =attributes
|
||||
queryParams = ['compositeType'];
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import Controller, { inject as controller } from '@ember/controller';
|
||||
|
||||
export default class ScopesScopeStorageBucketsStorageBucketIndexController extends Controller {
|
||||
@controller('scopes/scope/storage-buckets/index')
|
||||
storageBuckets;
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
import Controller, { inject as controller } from '@ember/controller';
|
||||
|
||||
export default class ScopesScopeTargetsTargetEnableSessionRecordingCreateStorageBucketController extends Controller {
|
||||
@controller('scopes/scope/storage-buckets/index')
|
||||
storageBuckets;
|
||||
}
|
||||
@@ -5,10 +5,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 { confirm } from 'core/decorators/confirm';
|
||||
import { notifySuccess, notifyError } from 'core/decorators/notify';
|
||||
|
||||
export default class ScopesScopeStorageBucketsRoute extends Route {
|
||||
// =services
|
||||
@@ -18,8 +14,6 @@ export default class ScopesScopeStorageBucketsRoute extends Route {
|
||||
@service can;
|
||||
@service router;
|
||||
|
||||
// =attributes
|
||||
|
||||
// =methods
|
||||
|
||||
/**
|
||||
@@ -44,59 +38,4 @@ export default class ScopesScopeStorageBucketsRoute extends Route {
|
||||
return this.store.query('storage-bucket', { scope_id, recursive: true });
|
||||
}
|
||||
}
|
||||
|
||||
// =actions
|
||||
|
||||
/**
|
||||
* Handle save
|
||||
* @param {StorageBucketModel} storageBucket
|
||||
*/
|
||||
@action
|
||||
@loading
|
||||
@notifyError(({ message }) => message)
|
||||
@notifySuccess('notifications.save-success')
|
||||
async save(storageBucket) {
|
||||
await storageBucket.save();
|
||||
await this.router.transitionTo(
|
||||
'scopes.scope.storage-buckets.storage-bucket',
|
||||
storageBucket,
|
||||
);
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
/**
|
||||
* Rollback changes on storage buckets.
|
||||
* @param {StorageBucketModel} storageBucket
|
||||
*/
|
||||
@action
|
||||
cancel(storageBucket) {
|
||||
const { isNew } = storageBucket;
|
||||
storageBucket.rollbackAttributes();
|
||||
if (isNew) this.router.transitionTo('scopes.scope.storage-buckets');
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the storage bucket.
|
||||
* @param {StorageBucketModel} storageBucket
|
||||
*/
|
||||
@action
|
||||
@loading
|
||||
@confirm('resources.storage-bucket.questions.delete-storage-bucket.message', {
|
||||
title: 'resources.storage-bucket.questions.delete-storage-bucket.title',
|
||||
confirm: 'resources.storage-bucket.actions.delete',
|
||||
})
|
||||
@notifyError(({ message }) => message, { catch: true })
|
||||
@notifySuccess('notifications.delete-success')
|
||||
async delete(storageBucket) {
|
||||
await storageBucket.destroyRecord();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates type of credential
|
||||
* @param {string} credentialType
|
||||
*/
|
||||
@action
|
||||
changeType(storageBucket, credentialType) {
|
||||
storageBucket.credentialType = credentialType;
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,13 @@ export default class ScopesScopeStorageBucketsNewRoute extends Route {
|
||||
@service can;
|
||||
@service router;
|
||||
|
||||
// =attributes
|
||||
queryParams = {
|
||||
compositeType: {
|
||||
refreshModel: true,
|
||||
},
|
||||
};
|
||||
|
||||
// =methods
|
||||
|
||||
/**
|
||||
@@ -39,8 +46,7 @@ export default class ScopesScopeStorageBucketsNewRoute extends Route {
|
||||
* creating another, but reuse name/description/other fields if available.
|
||||
* @return {StorageBucket}
|
||||
*/
|
||||
model() {
|
||||
const scopeModel = this.store.peekRecord('scope', 'global');
|
||||
model({ compositeType = TYPE_STORAGE_BUCKET_PLUGIN_AWS_S3 }) {
|
||||
let name,
|
||||
description,
|
||||
scope,
|
||||
@@ -48,6 +54,7 @@ export default class ScopesScopeStorageBucketsNewRoute extends Route {
|
||||
bucket_prefix,
|
||||
region,
|
||||
worker_filter;
|
||||
|
||||
if (this.currentModel?.isNew) {
|
||||
({
|
||||
name,
|
||||
@@ -60,9 +67,10 @@ export default class ScopesScopeStorageBucketsNewRoute extends Route {
|
||||
} = this.currentModel);
|
||||
this.currentModel.rollbackAttributes();
|
||||
}
|
||||
|
||||
const record = this.store.createRecord('storage-bucket', {
|
||||
type: TYPE_STORAGE_BUCKET_PLUGIN,
|
||||
compositeType: TYPE_STORAGE_BUCKET_PLUGIN_AWS_S3,
|
||||
compositeType,
|
||||
name,
|
||||
description,
|
||||
scope,
|
||||
@@ -71,7 +79,6 @@ export default class ScopesScopeStorageBucketsNewRoute extends Route {
|
||||
region,
|
||||
worker_filter,
|
||||
});
|
||||
record.scopeModel = scopeModel;
|
||||
return record;
|
||||
}
|
||||
|
||||
|
||||
@@ -69,6 +69,12 @@
|
||||
@text={{t 'resources.storage-bucket.titles.amazon_s3'}}
|
||||
@icon='aws-color'
|
||||
/>
|
||||
|
||||
{{else if B.data.isMinIO}}
|
||||
<Hds::Badge
|
||||
@text={{t 'resources.storage-bucket.titles.minio'}}
|
||||
@icon='cloud-upload'
|
||||
/>
|
||||
{{else}}
|
||||
{{B.data.plugin.name}}
|
||||
{{/if}}
|
||||
@@ -113,7 +119,7 @@
|
||||
<dd.Interactive
|
||||
@text={{t 'resources.storage-bucket.actions.delete'}}
|
||||
@color='critical'
|
||||
{{on 'click' (route-action 'delete' B.data)}}
|
||||
{{on 'click' (fn this.delete B.data)}}
|
||||
/>
|
||||
{{/if}}
|
||||
</Hds::Dropdown>
|
||||
|
||||
@@ -24,10 +24,14 @@
|
||||
<page.body>
|
||||
<Form::StorageBucket
|
||||
@model={{@model}}
|
||||
@submit={{route-action 'save' @model}}
|
||||
@cancel={{route-action 'cancel' @model}}
|
||||
@submit={{fn this.storageBuckets.save @model}}
|
||||
@cancel={{fn this.storageBuckets.cancel @model}}
|
||||
@scopes={{this.scopes}}
|
||||
@changeType={{route-action 'changeType' @model}}
|
||||
@changeCredentialType={{fn
|
||||
this.storageBuckets.changeCredentialType
|
||||
@model
|
||||
}}
|
||||
@changePluginType={{this.storageBuckets.changePluginType}}
|
||||
/>
|
||||
</page.body>
|
||||
|
||||
|
||||
@@ -27,10 +27,14 @@
|
||||
<page.body>
|
||||
<Form::StorageBucket
|
||||
@model={{@model}}
|
||||
@submit={{route-action 'save' @model}}
|
||||
@cancel={{route-action 'cancel' @model}}
|
||||
@submit={{fn this.storageBuckets.save @model}}
|
||||
@cancel={{fn this.storageBuckets.cancel @model}}
|
||||
@scopes={{this.scopes}}
|
||||
@changeType={{route-action 'changeType' @model}}
|
||||
@changeCredentialType={{fn
|
||||
this.storageBuckets.changeCredentialType
|
||||
@model
|
||||
}}
|
||||
@changePluginType={{this.storageBuckets.changePluginType}}
|
||||
/>
|
||||
</page.body>
|
||||
</Rose::Layout::Page>
|
||||
+5
@@ -27,6 +27,11 @@
|
||||
@submit={{route-action 'save' @model}}
|
||||
@cancel={{route-action 'cancel' @model}}
|
||||
@scopes={{this.scopes}}
|
||||
@changeCredentialType={{fn
|
||||
this.storageBuckets.changeCredentialType
|
||||
@model
|
||||
}}
|
||||
@changePluginType={{this.storageBuckets.changePluginType}}
|
||||
/>
|
||||
</page.body>
|
||||
|
||||
|
||||
@@ -32,6 +32,10 @@ module('Acceptance | storage-buckets | create', function (hooks) {
|
||||
const ACCESS_KEY_SELECTOR = '[name="access_key_id"]';
|
||||
const SECRET_KEY_SELECTOR = '[name="secret_access_key"]';
|
||||
const SCOPE_SELECTOR = '[name=scope]';
|
||||
const CREDENTIAL_TYPE_GROUP_SELECTOR = '[name=credential_type]';
|
||||
const MINIO_PLUGIN_TYPE_SELECTOR = '[value=minio]';
|
||||
const ENDPOINT_URL_SELECTOR = '[name=endpoint_url]';
|
||||
const ENDPOINT_URL_TEXT = 'http://www.hashicorp.com';
|
||||
|
||||
const instances = {
|
||||
scopes: {
|
||||
@@ -63,13 +67,13 @@ module('Acceptance | storage-buckets | create', function (hooks) {
|
||||
authenticateSession({});
|
||||
});
|
||||
|
||||
test('users can create a new storage bucket with global scope', async function (assert) {
|
||||
test('users can create a new storage bucket aws plugin type with global scope', async function (assert) {
|
||||
const storageBucketCount = getStorageBucketCount();
|
||||
await visit(urls.storageBuckets);
|
||||
|
||||
await click(`[href="${urls.newStorageBucket}"]`);
|
||||
await fillIn(NAME_FIELD_SELECTOR, NAME_FIELD_TEXT);
|
||||
await click('[value="global"]');
|
||||
await select(SCOPE_SELECTOR, 'global');
|
||||
|
||||
assert.dom(BUCKET_NAME_FIELD_SELECTOR).isNotDisabled();
|
||||
assert.dom(BUCKET_PREFIX_FIELD_SELECTOR).isNotDisabled();
|
||||
@@ -86,7 +90,7 @@ module('Acceptance | storage-buckets | create', function (hooks) {
|
||||
assert.strictEqual(getStorageBucketCount(), storageBucketCount + 1);
|
||||
});
|
||||
|
||||
test('users can create a new storage bucket with org scope', async function (assert) {
|
||||
test('users can create a new storage bucket aws plugin type with org scope', async function (assert) {
|
||||
const storageBucketCount = getStorageBucketCount();
|
||||
await visit(urls.storageBuckets);
|
||||
|
||||
@@ -110,13 +114,17 @@ module('Acceptance | storage-buckets | create', function (hooks) {
|
||||
assert.strictEqual(getStorageBucketCount(), storageBucketCount + 1);
|
||||
});
|
||||
|
||||
test('users can create a new storage bucket with dynamic credentials', async function (assert) {
|
||||
test('users can create a new storage bucket aws plugin type with dynamic credentials', async function (assert) {
|
||||
const storageBucketCount = getStorageBucketCount();
|
||||
await visit(urls.storageBuckets);
|
||||
|
||||
await click(`[href="${urls.newStorageBucket}"]`);
|
||||
await fillIn(NAME_FIELD_SELECTOR, NAME_FIELD_TEXT);
|
||||
assert.dom('.hds-form-radio-card').exists({ count: 2 });
|
||||
|
||||
// There are 2 credential types
|
||||
assert
|
||||
.dom(`${CREDENTIAL_TYPE_GROUP_SELECTOR} .hds-form-radio-card`)
|
||||
.exists({ count: 2 });
|
||||
|
||||
await click(DYNAMIC_CREDENTIAL_SELECTOR);
|
||||
await fillIn(ROLE_ARN_SELECTOR, 'test-arn-id');
|
||||
@@ -132,13 +140,15 @@ module('Acceptance | storage-buckets | create', function (hooks) {
|
||||
assert.strictEqual(getStorageBucketCount(), storageBucketCount + 1);
|
||||
});
|
||||
|
||||
test('users can create a new storage bucket with static credentials', async function (assert) {
|
||||
test('users can create a new storage bucket aws plugin type with static credentials', async function (assert) {
|
||||
const storageBucketCount = getStorageBucketCount();
|
||||
await visit(urls.storageBuckets);
|
||||
|
||||
await click(`[href="${urls.newStorageBucket}"]`);
|
||||
await fillIn(NAME_FIELD_SELECTOR, NAME_FIELD_TEXT);
|
||||
assert.dom('.hds-form-radio-card').exists({ count: 2 });
|
||||
assert
|
||||
.dom(`${CREDENTIAL_TYPE_GROUP_SELECTOR} .hds-form-radio-card`)
|
||||
.exists({ count: 2 });
|
||||
|
||||
await click(STATIC_CREDENTIAL_SELECTOR);
|
||||
await fillIn(ACCESS_KEY_SELECTOR, 'access_key_id');
|
||||
@@ -156,6 +166,40 @@ module('Acceptance | storage-buckets | create', function (hooks) {
|
||||
assert.strictEqual(getStorageBucketCount(), storageBucketCount + 1);
|
||||
});
|
||||
|
||||
test('users can create a new storage bucket minio plugin type', async function (assert) {
|
||||
const storageBucketCount = getStorageBucketCount();
|
||||
|
||||
// Navigate to new storage bucket
|
||||
await visit(urls.storageBuckets);
|
||||
await click(`[href="${urls.newStorageBucket}"]`);
|
||||
|
||||
// Fill the form
|
||||
await fillIn(NAME_FIELD_SELECTOR, NAME_FIELD_TEXT);
|
||||
await click(MINIO_PLUGIN_TYPE_SELECTOR);
|
||||
await fillIn(ENDPOINT_URL_SELECTOR, ENDPOINT_URL_TEXT);
|
||||
await fillIn(BUCKET_NAME_FIELD_SELECTOR, 'Test SB');
|
||||
await fillIn(ACCESS_KEY_SELECTOR, 'access_key_id');
|
||||
await fillIn(SECRET_KEY_SELECTOR, 'secret_access_key');
|
||||
|
||||
await click(SAVE_BTN_SELECTOR);
|
||||
|
||||
// Retrieve recently created SB
|
||||
const storageBucket = await this.server.schema.storageBuckets.findBy({
|
||||
name: NAME_FIELD_TEXT,
|
||||
});
|
||||
|
||||
// Assertions
|
||||
assert.strictEqual(storageBucket.name, NAME_FIELD_TEXT);
|
||||
assert.strictEqual(
|
||||
storageBucket.attributes.endpoint_url,
|
||||
ENDPOINT_URL_TEXT,
|
||||
);
|
||||
assert.strictEqual(storageBucket.attributes.role_arn, null);
|
||||
assert.strictEqual(storageBucket.plugin.name, 'minio');
|
||||
assert.strictEqual(storageBucket.type, 'plugin');
|
||||
assert.strictEqual(getStorageBucketCount(), storageBucketCount + 1);
|
||||
});
|
||||
|
||||
test('user can cancel new storage bucket creation', async function (assert) {
|
||||
const storageBucketCount = getStorageBucketCount();
|
||||
await visit(urls.storageBuckets);
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupTest } from 'admin/tests/helpers';
|
||||
|
||||
module(
|
||||
'Unit | Controller | scopes/scope/storage-buckets/index',
|
||||
function (hooks) {
|
||||
setupTest(hooks);
|
||||
|
||||
// TODO: Replace this with your real tests.
|
||||
test('it exists', function (assert) {
|
||||
let controller = this.owner.lookup(
|
||||
'controller:scopes/scope/storage-buckets/index',
|
||||
);
|
||||
assert.ok(controller);
|
||||
});
|
||||
},
|
||||
);
|
||||
@@ -0,0 +1,17 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupTest } from 'admin/tests/helpers';
|
||||
|
||||
module(
|
||||
'Unit | Controller | scopes/scope/storage-buckets/new',
|
||||
function (hooks) {
|
||||
setupTest(hooks);
|
||||
|
||||
// TODO: Replace this with your real tests.
|
||||
test('it exists', function (assert) {
|
||||
let controller = this.owner.lookup(
|
||||
'controller:scopes/scope/storage-buckets/new',
|
||||
);
|
||||
assert.ok(controller);
|
||||
});
|
||||
},
|
||||
);
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupTest } from 'admin/tests/helpers';
|
||||
|
||||
module(
|
||||
'Unit | Controller | scopes/scope/storage-buckets/storage-bucket/index',
|
||||
function (hooks) {
|
||||
setupTest(hooks);
|
||||
|
||||
// TODO: Replace this with your real tests.
|
||||
test('it exists', function (assert) {
|
||||
let controller = this.owner.lookup(
|
||||
'controller:scopes/scope/storage-buckets/storage-bucket/index',
|
||||
);
|
||||
assert.ok(controller);
|
||||
});
|
||||
},
|
||||
);
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupTest } from 'admin/tests/helpers';
|
||||
|
||||
module(
|
||||
'Unit | Controller | scopes/scope/targets/target/enable-session-recording/create-storage-bucket',
|
||||
function (hooks) {
|
||||
setupTest(hooks);
|
||||
|
||||
// TODO: Replace this with your real tests.
|
||||
test('it exists', function (assert) {
|
||||
let controller = this.owner.lookup(
|
||||
'controller:scopes/scope/targets/target/enable-session-recording/create-storage-bucket',
|
||||
);
|
||||
assert.ok(controller);
|
||||
});
|
||||
},
|
||||
);
|
||||
Reference in new issue
Block a user