From c81e3357d7d082260a145ef4f3c9e1c1022c5b9a Mon Sep 17 00:00:00 2001 From: lisbet-alvarez <107949262+lisbet-alvarez@users.noreply.github.com> Date: Fri, 17 Mar 2023 08:31:51 -0700 Subject: [PATCH] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20create=20form=20componen?= =?UTF-8?q?t=20for=20storage=20bucket=20(#118)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 🎸 create form component for storage bucket ✅ Closes: ICU-8235 --- addons/core/translations/resources/en-us.yaml | 26 +- .../components/form/storage-bucket/index.hbs | 249 ++++++++++++++++++ .../components/form/storage-bucket/index.js | 20 ++ .../scopes/scope/storage-buckets/new.js | 21 ++ .../routes/scopes/scope/storage-buckets.js | 38 ++- .../scopes/scope/storage-buckets/new.js | 35 ++- .../scopes/scope/storage-buckets/index.hbs | 4 +- .../scopes/scope/storage-buckets/new.hbs | 26 +- .../scopes/scope/storage-buckets/new-test.js | 16 ++ 9 files changed, 427 insertions(+), 8 deletions(-) create mode 100644 ui/admin/app/components/form/storage-bucket/index.hbs create mode 100644 ui/admin/app/components/form/storage-bucket/index.js create mode 100644 ui/admin/app/controllers/scopes/scope/storage-buckets/new.js create mode 100644 ui/admin/tests/unit/controllers/scopes/scope/storage-buckets/new-test.js diff --git a/addons/core/translations/resources/en-us.yaml b/addons/core/translations/resources/en-us.yaml index 3e6ee2b6b..9f2fb54f8 100644 --- a/addons/core/translations/resources/en-us.yaml +++ b/addons/core/translations/resources/en-us.yaml @@ -238,8 +238,30 @@ storage-bucket: actions: delete: Delete Storage Bucket form: - scope: Scope - provider: Provider + scope: + label: Scope + help: You will be able to associate this storage bucket with any target. + provider: + label: Provider + help: Boundary currently only supports Amazon S3 storage. + bucket_name: + label: Bucket name + help: The name of this bucket in AWS + bucket_prefix: + label: Bucket prefix + help: The base path where session recordings will be stored. + region: + label: Region + help: The AWS region to use + access_key_id: + label: Access key ID + help: The AWS access key ID to use. + secret_access_key: + label: Secret access key + help: The secret access key to use. + worker_filter: + label: Worker filter + help: Filter for Boundary workers that have network access to your storage. Boolean expression. project: title: Project title_plural: Projects diff --git a/ui/admin/app/components/form/storage-bucket/index.hbs b/ui/admin/app/components/form/storage-bucket/index.hbs new file mode 100644 index 000000000..2413fd29f --- /dev/null +++ b/ui/admin/app/components/form/storage-bucket/index.hbs @@ -0,0 +1,249 @@ +{{! + Copyright (c) HashiCorp, Inc. + SPDX-License-Identifier: MPL-2.0 +}} + + + + {{t 'form.name.label'}} + {{t 'form.name.help'}} + {{#if @model.errors.name}} + + {{#each @model.errors.name as |error|}} + {{error.message}} + {{/each}} + + {{/if}} + + + + {{t 'form.description.label'}} + {{t 'form.description.help'}} + {{#if @model.errors.description}} + + {{#each @model.errors.description as |error|}} + {{error.message}} + {{/each}} + + {{/if}} + + + {{#if (or @model.isNew form.isEditable)}} + + {{t 'resources.storage-bucket.form.scope.label'}} + {{t + 'resources.storage-bucket.form.scope.help' + }} + + {{#each @scopes as |scope|}} + + {{/each}} + + + {{else if @model.scope}} + + {{t 'resources.storage-bucket.form.scope.label'}} + {{t + 'resources.storage-bucket.form.scope.help' + }} + + {{/if}} + + + {{t 'titles.provider'}} + {{t + 'resources.storage-bucket.form.provider.help' + }} + + + + {{t 'resources.storage-bucket.form.bucket_name.label'}} + {{t + 'resources.storage-bucket.form.bucket_name.help' + }} + {{#if @model.errors.bucket_name}} + + {{#each @model.errors.bucket_name as |error|}} + {{error.message}} + {{/each}} + + {{/if}} + + + + {{t 'resources.storage-bucket.form.bucket_prefix.label'}} + {{t + 'resources.storage-bucket.form.bucket_prefix.help' + }} + {{#if @model.errors.bucket_prefix}} + + {{#each @model.errors.bucket_prefix as |error|}} + {{error.message}} + {{/each}} + + {{/if}} + + + + {{t 'resources.storage-bucket.form.region.label'}} + {{t + 'resources.storage-bucket.form.region.help' + }} + {{#if @model.errors.region}} + + {{#each @model.errors.region as |error|}} + {{error.message}} + {{/each}} + + {{/if}} + + + + {{t 'resources.storage-bucket.form.access_key_id.label'}} + {{t + 'resources.storage-bucket.form.access_key_id.help' + }} + {{#if @model.errors.access_key_id}} + + {{#each @model.errors.access_key_id as |error|}} + {{error.message}} + {{/each}} + + {{/if}} + + + + {{t + 'resources.storage-bucket.form.secret_access_key.label' + }} + {{t + 'resources.storage-bucket.form.secret_access_key.help' + }} + {{#if @model.errors.secret_access_key}} + + {{#each @model.errors.secret_access_key as |error|}} + {{error.message}} + {{/each}} + + {{/if}} + + + + {{t 'resources.storage-bucket.form.worker_filter.label'}} + + {{t 'resources.storage-bucket.form.worker_filter.help'}} + {{! TODO: Link to the correct url for worker-filters }} + {{t + 'actions.learn-more' + }} + + {{#if @model.errors.worker_filter}} + + {{#each @model.errors.worker_filter as |error|}} + {{error.message}} + {{/each}} + + {{/if}} + + + {{#if (can 'save model' @model)}} + + {{/if}} + \ No newline at end of file diff --git a/ui/admin/app/components/form/storage-bucket/index.js b/ui/admin/app/components/form/storage-bucket/index.js new file mode 100644 index 000000000..108ee5fb9 --- /dev/null +++ b/ui/admin/app/components/form/storage-bucket/index.js @@ -0,0 +1,20 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: MPL-2.0 + */ + +import Component from '@glimmer/component'; +import { action } from '@ember/object'; + +export default class FormStorageBucketComponent extends Component { + // =actions + + @action + updateScope(event) { + const selectedScopeId = event.target.value; + const selectedScopeModel = this.args.scopes.find( + (element) => element.model.id === selectedScopeId + ).model; + this.args.model.scopeModel = selectedScopeModel; + } +} diff --git a/ui/admin/app/controllers/scopes/scope/storage-buckets/new.js b/ui/admin/app/controllers/scopes/scope/storage-buckets/new.js new file mode 100644 index 000000000..40f8fcfdc --- /dev/null +++ b/ui/admin/app/controllers/scopes/scope/storage-buckets/new.js @@ -0,0 +1,21 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: MPL-2.0 + */ + +import Controller from '@ember/controller'; +import { inject as service } from '@ember/service'; + +export default class ScopesScopeStorageBucketsNewController extends Controller { + // =services + + @service intl; + + /** + * Breadcrumb for new route + * @type {string} + */ + get breadCrumb() { + return this.intl.t('resources.storage-bucket.titles.new'); + } +} diff --git a/ui/admin/app/routes/scopes/scope/storage-buckets.js b/ui/admin/app/routes/scopes/scope/storage-buckets.js index 56b2632b6..ca713dd12 100644 --- a/ui/admin/app/routes/scopes/scope/storage-buckets.js +++ b/ui/admin/app/routes/scopes/scope/storage-buckets.js @@ -5,12 +5,14 @@ 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'; export default class ScopesScopeStorageBucketsRoute extends Route { // =services @service store; - @service intl; @service session; @service can; @service router; @@ -31,10 +33,42 @@ export default class ScopesScopeStorageBucketsRoute extends Route { async model() { const scope = this.modelFor('scopes.scope'); const { id: scope_id } = scope; - if (this.can.can('list storage-bucket', scope, { collection: 'storage-buckets' })) { + if ( + this.can.can('list storage-bucket', scope, { + collection: 'storage-buckets', + }) + ) { return this.store.query('storage-bucket', { scope_id }); } } // =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'); + } } diff --git a/ui/admin/app/routes/scopes/scope/storage-buckets/new.js b/ui/admin/app/routes/scopes/scope/storage-buckets/new.js index d318e1a66..130dfe198 100644 --- a/ui/admin/app/routes/scopes/scope/storage-buckets/new.js +++ b/ui/admin/app/routes/scopes/scope/storage-buckets/new.js @@ -5,6 +5,10 @@ import Route from '@ember/routing/route'; import { inject as service } from '@ember/service'; +import { + TYPE_STORAGE_BUCKET_PLUGIN, + TYPE_STORAGE_BUCKET_PLUGIN_AWS_S3, +} from 'api/models/storage-bucket'; export default class ScopesScopeStorageBucketsNewRoute extends Route { // =services @@ -19,8 +23,37 @@ export default class ScopesScopeStorageBucketsNewRoute extends Route { */ model() { const scopeModel = this.modelFor('scopes.scope'); - const record = this.store.createRecord('storage-bucket'); + const record = this.store.createRecord('storage-bucket', { + type: TYPE_STORAGE_BUCKET_PLUGIN, + compositeType: TYPE_STORAGE_BUCKET_PLUGIN_AWS_S3, + }); record.scopeModel = scopeModel; return record; } + + async afterModel() { + let scopes; + const currentScope = this.modelFor('scopes.scope'); + const orgScopes = ( + await this.store.query('scope', { scope_id: 'global' }) + ).map((scope) => ({ model: scope })); + if (currentScope.id === 'global') { + scopes = [{ model: currentScope }, ...orgScopes]; + } else { + scopes = [ + { model: this.store.peekRecord('scope', 'global') }, + ...orgScopes, + ]; + } + this.scopes = scopes; + } + + /** + * Adds available global and org scopes to the context. + * @param {Controller} controller + */ + setupController(controller) { + super.setupController(...arguments); + controller.set('scopes', this.scopes); + } } diff --git a/ui/admin/app/templates/scopes/scope/storage-buckets/index.hbs b/ui/admin/app/templates/scopes/scope/storage-buckets/index.hbs index 01c093f4c..fab3a2a7f 100644 --- a/ui/admin/app/templates/scopes/scope/storage-buckets/index.hbs +++ b/ui/admin/app/templates/scopes/scope/storage-buckets/index.hbs @@ -37,8 +37,8 @@ @columns={{array (hash label=(t 'form.name.label')) (hash label=(t 'form.id.label')) - (hash label=(t 'resources.storage-bucket.form.provider')) - (hash label=(t 'resources.storage-bucket.form.scope')) + (hash label=(t 'resources.storage-bucket.form.provider.label')) + (hash label=(t 'resources.storage-bucket.form.scope.label')) (hash label='') }} > diff --git a/ui/admin/app/templates/scopes/scope/storage-buckets/new.hbs b/ui/admin/app/templates/scopes/scope/storage-buckets/new.hbs index 233691fac..e1b859492 100644 --- a/ui/admin/app/templates/scopes/scope/storage-buckets/new.hbs +++ b/ui/admin/app/templates/scopes/scope/storage-buckets/new.hbs @@ -3,4 +3,28 @@ SPDX-License-Identifier: MPL-2.0 }} -{{page-title (t 'titles.new')}} \ No newline at end of file +{{page-title (t 'titles.new')}} + + + + + + + + {{! TODO: Add valid doc link and description}} +

+ {{t 'resources.storage-bucket.titles.new'}} + +

+
+ + + + + +
\ No newline at end of file diff --git a/ui/admin/tests/unit/controllers/scopes/scope/storage-buckets/new-test.js b/ui/admin/tests/unit/controllers/scopes/scope/storage-buckets/new-test.js new file mode 100644 index 000000000..3f12de7c5 --- /dev/null +++ b/ui/admin/tests/unit/controllers/scopes/scope/storage-buckets/new-test.js @@ -0,0 +1,16 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'admin/tests/helpers'; + +module( + 'Unit | Controller | scopes/scope/storage-buckets/new', + function (hooks) { + setupTest(hooks); + + test('it exists', function (assert) { + let controller = this.owner.lookup( + 'controller:scopes/scope/storage-buckets/new' + ); + assert.ok(controller); + }); + } +);