🥐Transform-edit-base: remove use in transform-template-edit and alphabet-edit (#29803)

* remove transform-edit-base from alaphabet and transform edit components

* cleanup documentation

* move hbs file to be next to js

* update this.displayErrors to this.errorMessage

---------

Co-authored-by: Shannon Roberts <shannon.roberts@hashicorp.com>
This commit is contained in:
Angel Garbarino 2025-03-04 09:35:34 -07:00 committed by GitHub
parent 49eda90dcd
commit 72cf813cbd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 236 additions and 111 deletions

View File

@ -5,44 +5,38 @@
<PageHeader as |p|>
<p.top>
<KeyValueHeader
@baseKey={{hash display=this.model.id id=this.model.idForNav}}
@path="vault.cluster.secrets.backend.list"
@mode={{this.mode}}
@root={{this.root}}
@showCurrent={{true}}
/>
<Page::Breadcrumbs @breadcrumbs={{this.breadcrumbs}} />
</p.top>
<p.levelLeft>
<h1 class="title is-3" data-test-secret-header="true">
{{#if (eq this.mode "create")}}
{{#if (eq @mode "create")}}
Create Alphabet
{{else if (eq this.mode "edit")}}
{{else if (eq @mode "edit")}}
Edit Alphabet
{{else}}
Alphabet
<code>{{this.model.id}}</code>
<code>{{@model.id}}</code>
{{/if}}
</h1>
</p.levelLeft>
</PageHeader>
{{#if (eq this.mode "show")}}
{{#if (eq @mode "show")}}
<Toolbar>
<ToolbarActions>
{{#if this.model.updatePath.canDelete}}
{{#if @model.updatePath.canDelete}}
<Hds::Button
@text="Delete alphabet"
@color="secondary"
class="toolbar-button"
{{on "click" (action "delete")}}
{{on "click" this.onDelete}}
data-test-transformation-alphabet-delete
/>
<div class="toolbar-separator"></div>
{{/if}}
{{#if this.model.updatePath.canUpdate}}
{{#if @model.updatePath.canUpdate}}
<ToolbarSecretLink
@secret={{concat this.model.idPrefix this.model.id}}
@secret={{concat @model.idPrefix @model.id}}
@mode="edit"
data-test-edit-link={{true}}
@replace={{true}}
@ -54,13 +48,13 @@
</Toolbar>
{{/if}}
{{#if (or (eq this.mode "edit") (eq this.mode "create"))}}
<form onsubmit={{action "createOrUpdate" this.mode}}>
{{#if (or (eq @mode "edit") (eq @mode "create"))}}
<form onsubmit={{this.createOrUpdate}}>
<div class="box is-sideless is-fullwidth is-marginless">
<MessageError @model={{this.model}} />
<NamespaceReminder @mode={{this.mode}} @noun="transform alphabet" />
{{#each this.model.attrs as |attr|}}
{{#if (and (eq attr.name "name") (eq this.mode "edit"))}}
<MessageError @errorMessage={{this.errorMessage}} />
<NamespaceReminder @mode={{@mode}} @noun="transform alphabet" />
{{#each @model.attrs as |attr|}}
{{#if (and (eq attr.name "name") (eq @mode "edit"))}}
<label for={{attr.name}} class="is-label">
{{attr.options.label}}
</label>
@ -72,29 +66,29 @@
id={{attr.name}}
autocomplete="off"
spellcheck="false"
value={{or (get this.model attr.name) this.model.id}}
value={{or (get @model attr.name) @model.id}}
readonly
class="field input is-readOnly"
type={{attr.type}}
/>
{{else}}
<FormField data-test-field @attr={{attr}} @model={{this.model}} />
<FormField data-test-field @attr={{attr}} @model={{@model}} />
{{/if}}
{{/each}}
</div>
<div class="field is-grouped-split box is-fullwidth is-bottomless">
<Hds::ButtonSet>
<Hds::Button
@text={{if (eq this.mode "create") "Create alphabet" "Save"}}
@text={{if (eq @mode "create") "Create alphabet" "Save"}}
type="submit"
data-test-alphabet-transform-create
/>
{{#if (eq this.mode "create")}}
{{#if (eq @mode "create")}}
<Hds::Button
@text="Cancel"
@color="secondary"
@route="vault.cluster.secrets.backend.list-root"
@model={{this.model.backend}}
@model={{@model.backend}}
@query={{hash tab="alphabet"}}
/>
{{else}}
@ -102,7 +96,7 @@
@text="Cancel"
@color="secondary"
@route="vault.cluster.secrets.backend.show"
@models={{array this.model.backend (concat "alphabet/" this.model.id)}}
@models={{array @model.backend (concat "alphabet/" @model.id)}}
@query={{hash tab="alphabet"}}
/>
{{/if}}
@ -110,29 +104,24 @@
</div>
</form>
{{else}}
{{#if this.model.displayErrors}}
<div class="has-top-margin-s">
<MessageError @model={{this.model}} />
</div>
{{/if}}
<div class="box is-fullwidth is-sideless is-paddingless is-marginless">
{{#each this.model.attrs as |attr|}}
{{#each @model.attrs as |attr|}}
{{#if (eq attr.type "object")}}
<InfoTableRow
@label={{capitalize (or attr.options.label (humanize (dasherize attr.name)))}}
@value={{stringify (get this.model attr.name)}}
@value={{stringify (get @model attr.name)}}
/>
{{else if (eq attr.type "array")}}
<InfoTableRow
@label={{capitalize (or attr.options.label (humanize (dasherize attr.name)))}}
@value={{get this.model attr.name}}
@value={{get @model attr.name}}
@type={{attr.type}}
@isLink={{eq attr.name "transformations"}}
/>
{{else}}
<InfoTableRow
@label={{capitalize (or attr.options.label (humanize (dasherize attr.name)))}}
@value={{get this.model attr.name}}
@value={{get @model attr.name}}
/>
{{/if}}
{{/each}}

View File

@ -3,6 +3,82 @@
* SPDX-License-Identifier: BUSL-1.1
*/
import TransformBase from './transform-edit-base';
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { action } from '@ember/object';
import { service } from '@ember/service';
import errorMessage from 'vault/utils/error-message';
export default TransformBase;
/**
* @module AlphabetEdit
* `AlphabetEdit` is a component that allows you to create/edit or view an alphabet.
*
* @example
* ```js
* <AlphabetEdit @model={{this.model}} @mode={{this.mode}} />
* ```
* @param {object} model - This is the transform alphabet model.
* @param {string} mode - Is either show, create or edit.
*/
export default class AlphabetEditComponent extends Component {
@service flashMessages;
@service router;
@tracked errorMessage = '';
get breadcrumbs() {
// ideally this is created on the controller in the parent route but this is a generic route and adding breadcrumbs to the controller requires a larger refactor.
const { backend } = this.args.model;
return [
{
label: backend,
route: 'vault.cluster.secrets.backend.list-root',
model: backend,
query: { tab: 'alphabet' },
},
{ label: 'Alphabet' },
];
}
transition(route = 'show') {
this.errorMessage = '';
const { backend, id } = this.args.model;
if (route === 'list') {
this.router.transitionTo('vault.cluster.secrets.backend.list-root', backend, {
queryParams: { tab: 'alphabet' },
});
return;
} else {
this.router.transitionTo('vault.cluster.secrets.backend.show', `alphabet/${id}`);
}
}
@action async createOrUpdate(event) {
event.preventDefault();
if (!this.args.model.hasDirtyAttributes) {
this.flashMessages.info('No changes detected.');
this.transition();
return;
}
try {
await this.args.model.save();
this.flashMessages.success('Alphabet saved.');
this.transition();
} catch (e) {
this.errorMessage = errorMessage(e);
}
}
@action async onDelete() {
try {
await this.args.model.destroyRecord();
this.flashMessages.success('Alphabet deleted.');
this.transition('list');
} catch (e) {
this.errorMessage = errorMessage(e);
}
}
}

View File

@ -5,62 +5,51 @@
<PageHeader as |p|>
<p.top>
<KeyValueHeader
@baseKey={{hash display=this.model.id id=this.model.idForNav}}
@path="vault.cluster.secrets.backend.list"
@mode={{this.mode}}
@root={{this.root}}
@showCurrent={{true}}
/>
<Page::Breadcrumbs @breadcrumbs={{this.breadcrumbs}} />
</p.top>
<p.levelLeft>
<h1 class="title is-3" data-test-secret-header="true">
{{#if (eq this.mode "create")}}
{{#if (eq @mode "create")}}
Create Template
{{else if (eq this.mode "edit")}}
{{else if (eq @mode "edit")}}
Edit Template
{{else}}
Template
<code>{{this.model.id}}</code>
<code>{{@model.id}}</code>
{{/if}}
</h1>
</p.levelLeft>
</PageHeader>
{{#if (eq this.mode "show")}}
{{#if (eq @mode "show")}}
<Toolbar>
<ToolbarActions>
{{#if this.model.updatePath.canDelete}}
{{#if @model.updatePath.canDelete}}
<Hds::Button
@text="Delete template"
@color="secondary"
class="toolbar-button"
{{on "click" (action "delete")}}
{{on "click" this.onDelete}}
data-test-transformation-template-delete
/>
<div class="toolbar-separator"></div>
{{/if}}
{{#if this.model.updatePath.canUpdate}}
<ToolbarSecretLink
@secret={{concat this.model.idPrefix this.model.id}}
@mode="edit"
data-test-edit-link={{true}}
@replace={{true}}
>
{{#if @model.updatePath.canUpdate}}
<ToolbarLink @route="vault.cluster.secrets.backend.edit" @model={{concat "template/" @model.id}} data-test-edit-link>
Edit template
</ToolbarSecretLink>
</ToolbarLink>
{{/if}}
</ToolbarActions>
</Toolbar>
{{/if}}
{{#if (or (eq this.mode "edit") (eq this.mode "create"))}}
<form onsubmit={{action "createOrUpdate" this.mode}}>
{{#if (or (eq @mode "edit") (eq @mode "create"))}}
<form onsubmit={{this.createOrUpdate}}>
<div class="box is-sideless is-fullwidth is-marginless">
<MessageError @model={{this.model}} />
<NamespaceReminder @mode={{this.mode}} @noun="transform template" />
{{#each this.model.writeAttrs as |attr|}}
{{#if (and (eq attr.name "name") (eq this.mode "edit"))}}
<NamespaceReminder @mode={{@mode}} @noun="transform template" />
<MessageError @errorMessage={{this.errorMessage}} />
{{#each @model.writeAttrs as |attr|}}
{{#if (and (eq attr.name "name") (eq @mode "edit"))}}
<label for={{attr.name}} class="is-label">
{{attr.options.label}}
</label>
@ -72,32 +61,32 @@
id={{attr.name}}
autocomplete="off"
spellcheck="false"
value={{or (get this.model attr.name) attr.options.defaultValue}}
value={{or (get @model attr.name) attr.options.defaultValue}}
readonly={{true}}
class="field input is-readOnly"
type={{attr.type}}
/>
{{else}}
{{#if (eq attr.name "alphabet")}}
<TransformAdvancedTemplating @model={{this.model}} />
<TransformAdvancedTemplating @model={{@model}} />
{{/if}}
<FormField data-test-field @attr={{attr}} @model={{this.model}} />
<FormField data-test-field @attr={{attr}} @model={{@model}} />
{{/if}}
{{/each}}
</div>
<div class="field is-grouped-split box is-fullwidth is-bottomless">
<Hds::ButtonSet>
<Hds::Button
@text={{if (eq this.mode "create") "Create template" "Save"}}
@text={{if (eq @mode "create") "Create template" "Save"}}
type="submit"
data-test-template-transform-create
/>
{{#if (eq this.mode "create")}}
{{#if (eq @mode "create")}}
<Hds::Button
@text="Cancel"
@color="secondary"
@route="vault.cluster.secrets.backend.list-root"
@model={{this.model.backend}}
@model={{@model.backend}}
@query={{hash tab="template"}}
/>
{{else}}
@ -105,7 +94,7 @@
@text="Cancel"
@color="secondary"
@route="vault.cluster.secrets.backend.show"
@models={{array this.model.backend (concat "template/" this.model.id)}}
@models={{array @model.backend (concat "template/" @model.id)}}
@query={{hash tab="template"}}
/>
{{/if}}
@ -113,19 +102,14 @@
</div>
</form>
{{else}}
{{#if this.model.displayErrors}}
<div class="has-top-margin-s">
<MessageError @model={{this.model}} />
</div>
{{/if}}
<div class="box is-fullwidth is-sideless is-paddingless is-marginless">
{{#each this.model.readAttrs as |attr|}}
{{#each @model.readAttrs as |attr|}}
{{#let (capitalize (or attr.options.label (humanize (dasherize attr.name)))) as |label|}}
{{#if (eq attr.name "decodeFormats")}}
{{#if (not (is-empty-value this.model.decodeFormats))}}
{{#if (not (is-empty-value @model.decodeFormats))}}
<InfoTableRow @label={{label}}>
<div>
{{#each-in this.model.decodeFormats as |key value|}}
{{#each-in @model.decodeFormats as |key value|}}
<div class="transform-decode-formats">
<p class="is-label has-text-grey-light">{{key}}</p>
<p>{{value}}</p>
@ -137,7 +121,7 @@
{{else}}
<InfoTableRow
@label={{label}}
@value={{get this.model attr.name}}
@value={{get @model attr.name}}
class={{if (eq attr.name "pattern") "transform-pattern-text"}}
/>
{{/if}}

View File

@ -3,6 +3,82 @@
* SPDX-License-Identifier: BUSL-1.1
*/
import TransformBase from './transform-edit-base';
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { action } from '@ember/object';
import { service } from '@ember/service';
import errorMessage from 'vault/utils/error-message';
export default TransformBase;
/**
* @module TransformTemplateEdit
* `TransformTemplateEdit` is a component that allows you to create/edit or view a transform template.
*
* @example
* ```js
* <TransformTemplateEdit }} />
* ```
* @param {object} model - This is the transform template model.
* @param {string} mode - Is either show, create or edit.
*/
export default class TransformTemplateEditComponent extends Component {
@service flashMessages;
@service router;
@tracked errorMessage = '';
get breadcrumbs() {
// ideally this is created on the controller in the parent route but this is a generic route and adding breadcrumbs to the controller requires a larger refactor.
const { backend } = this.args.model;
return [
{
label: backend,
route: 'vault.cluster.secrets.backend.list-root',
model: backend,
query: { tab: 'template' },
},
{ label: 'Template' },
];
}
transition(route = 'show') {
this.errorMessage = '';
const { backend, id } = this.args.model;
if (route === 'list') {
this.router.transitionTo('vault.cluster.secrets.backend.list-root', backend, {
queryParams: { tab: 'template' },
});
return;
} else {
this.router.transitionTo('vault.cluster.secrets.backend.show', `template/${id}`);
}
}
@action async createOrUpdate(event) {
event.preventDefault();
if (!this.args.model.hasDirtyAttributes) {
this.flashMessages.info('No changes detected.');
this.transition();
return;
}
try {
await this.args.model.save();
this.flashMessages.success('Transform template saved.');
this.transition();
} catch (e) {
this.errorMessage = errorMessage(e);
}
}
@action async onDelete() {
try {
await this.args.model.destroyRecord();
this.flashMessages.success('Transform template deleted.');
this.transition('list');
} catch (e) {
this.errorMessage = errorMessage(e);
}
}
}

View File

@ -10,11 +10,6 @@ import { expandAttributeMeta } from 'vault/utils/field-to-attrs';
export default class Alphabet extends Model {
idPrefix = 'alphabet/';
get idForNav() {
const modelId = this.id || '';
return `${this.idPrefix}${modelId}`;
}
@attr('string', {
readOnly: true,
subText: 'The alphabet name. Keep in mind that spaces are not allowed and this cannot be edited later.',

View File

@ -4,28 +4,28 @@
*/
import Model, { attr } from '@ember-data/model';
import { computed } from '@ember/object';
import lazyCapabilities, { apiPath } from 'vault/macros/lazy-capabilities';
import { expandAttributeMeta } from 'vault/utils/field-to-attrs';
export default Model.extend({
idPrefix: 'template/',
idForNav: computed('id', 'idPrefix', function () {
const modelId = this.id || '';
return `${this.idPrefix}${modelId}`;
}),
export default class TransformTemplate extends Model {
idPrefix = 'template/';
name: attr('string', {
@attr('string', {
readOnly: true,
subText:
'Templates allow Vault to determine what and how to capture the value to be transformed. This cannot be edited later.',
}),
type: attr('string', { defaultValue: 'regex' }),
pattern: attr('string', {
})
name;
@attr('string', { defaultValue: 'regex' }) type;
@attr('string', {
editType: 'regex',
subText: 'The templates pattern defines the data format. Expressed in regex.',
}),
alphabet: attr('array', {
})
pattern;
@attr('array', {
subText:
'Alphabet defines a set of characters (UTF-8) that is used for FPE to determine the validity of plaintext and ciphertext values. You can choose a built-in one, or create your own.',
editType: 'searchSelect',
@ -34,17 +34,22 @@ export default Model.extend({
label: 'Alphabet',
models: ['transform/alphabet'],
selectLimit: 1,
}),
encodeFormat: attr('string'),
decodeFormats: attr(),
backend: attr('string', { readOnly: true }),
})
alphabet;
readAttrs: computed(function () {
@attr('string') encodeFormat;
@attr('') decodeFormats;
@attr('string', { readOnly: true }) backend;
get readAttrs() {
const keys = ['name', 'pattern', 'encodeFormat', 'decodeFormats', 'alphabet'];
return expandAttributeMeta(this, keys);
}),
writeAttrs: computed(function () {
}
get writeAttrs() {
return expandAttributeMeta(this, ['name', 'pattern', 'alphabet']);
}),
updatePath: lazyCapabilities(apiPath`${'backend'}/template/${'id'}`, 'backend', 'id'),
});
}
@lazyCapabilities(apiPath`${'backend'}/template/${'id'}`, 'backend') updatePath;
}