diff --git a/changelog/24290.txt b/changelog/24290.txt new file mode 100644 index 000000000000..3533146b7d23 --- /dev/null +++ b/changelog/24290.txt @@ -0,0 +1,3 @@ +```release-note:bug +ui: When Kv v2 secret is an object, fix so details view defaults to readOnly JSON editor. +``` \ No newline at end of file diff --git a/ui/lib/kv/addon/components/kv-data-fields.hbs b/ui/lib/kv/addon/components/kv-data-fields.hbs index b384d3e4c2e3..f981b2b8c412 100644 --- a/ui/lib/kv/addon/components/kv-data-fields.hbs +++ b/ui/lib/kv/addon/components/kv-data-fields.hbs @@ -4,9 +4,9 @@ ~}} {{#let (find-by "name" "path" @secret.allFields) as |attr|}} - {{#if @isEdit}} + {{#if (eq @type "edit")}} - {{else}} + {{else if (eq @type "create")}} {{/if}} {{/let}} @@ -14,9 +14,10 @@
{{#if @showJson}} {{#if (or @modelValidations.secretData.errors this.lintingErrors)}} @@ -27,10 +28,18 @@ {{/if}} {{/if}} +{{else if (eq @type "details")}} + {{#each-in @secret.secretData as |key value|}} + + + + {{else}} + + {{/each-in}} {{else}} @@ -23,7 +23,7 @@ import { stringify } from 'core/helpers/stringify'; * @param {boolean} showJson - boolean passed from parent to hide/show json editor * @param {object} [modelValidations] - object of errors. If attr.name is in object and has error message display in AlertInline. * @param {callback} [pathValidations] - callback function fired for the path input on key up - * @param {boolean} [isEdit=false] - if true, this is a new secret version rather than a new secret. Used to change text for some form labels + * @param {boolean} [type=null] - can be edit, create, or details. Used to change text for some form labels */ export default class KvDataFields extends Component { diff --git a/ui/lib/kv/addon/components/page/secret/details.hbs b/ui/lib/kv/addon/components/page/secret/details.hbs index 6c2dd065f2f5..7aee68f28f1b 100644 --- a/ui/lib/kv/addon/components/page/secret/details.hbs +++ b/ui/lib/kv/addon/components/page/secret/details.hbs @@ -15,7 +15,12 @@ <:toolbarFilters> {{#unless this.emptyState}} - + JSON {{/unless}} @@ -93,15 +98,10 @@ {{/if}} {{else}} - {{#if this.showJsonView}} - - {{else}} - {{#each-in @secret.secretData as |key value|}} - - - - {{else}} - - {{/each-in}} - {{/if}} + {{/if}} \ No newline at end of file diff --git a/ui/lib/kv/addon/components/page/secret/details.js b/ui/lib/kv/addon/components/page/secret/details.js index 844706f8dbcd..d5c6189bac2b 100644 --- a/ui/lib/kv/addon/components/page/secret/details.js +++ b/ui/lib/kv/addon/components/page/secret/details.js @@ -35,6 +35,16 @@ export default class KvSecretDetails extends Component { @tracked showJsonView = false; @tracked wrappedData = null; + secretDataIsAdvanced; + + constructor() { + super(...arguments); + this.originalSecret = JSON.stringify(this.args.secret.secretData || {}); + if (this.originalSecret.lastIndexOf('{') > 0) { + // Dumb way to check if there's a nested object in the secret + this.secretDataIsAdvanced = true; + } + } @action closeVersionMenu(dropdown) { diff --git a/ui/lib/kv/addon/components/page/secret/edit.hbs b/ui/lib/kv/addon/components/page/secret/edit.hbs index 8a5be5646996..28ba62d89f78 100644 --- a/ui/lib/kv/addon/components/page/secret/edit.hbs +++ b/ui/lib/kv/addon/components/page/secret/edit.hbs @@ -46,7 +46,7 @@ @showJson={{or this.showJsonView this.secretDataIsAdvanced}} @secret={{@secret}} @modelValidations={{this.modelValidations}} - @isEdit={{true}} + @type="edit" />
diff --git a/ui/lib/kv/addon/components/page/secrets/create.hbs b/ui/lib/kv/addon/components/page/secrets/create.hbs index 326ecab52f9a..4eefecf19854 100644 --- a/ui/lib/kv/addon/components/page/secrets/create.hbs +++ b/ui/lib/kv/addon/components/page/secrets/create.hbs @@ -21,6 +21,7 @@ @secret={{@secret}} @modelValidations={{this.modelValidations}} @pathValidations={{this.pathValidations}} + @type="create" /> `, { owner: this.engine }); - + await render(hbs``, { + owner: this.engine, + }); await fillIn(FORM.inputByAttr('path'), this.path); await fillIn(FORM.keyInput(), 'foo'); await fillIn(FORM.maskedValueInput(), 'bar'); @@ -40,7 +41,6 @@ module('Integration | Component | kv-v2 | KvDataFields', function (hooks) { assert.expect(3); await render(hbs``, { owner: this.engine }); - assert.strictEqual( codemirror().getValue(' '), `{ \"\": \"\" }`, // eslint-disable-line no-useless-escape @@ -63,7 +63,7 @@ module('Integration | Component | kv-v2 | KvDataFields', function (hooks) { secretData: this.secret.secretData, }); - await render(hbs``, { + await render(hbs``, { owner: this.engine, }); @@ -73,4 +73,35 @@ module('Integration | Component | kv-v2 | KvDataFields', function (hooks) { assert.dom(FORM.maskedValueInput()).hasValue('bar'); assert.dom(FORM.dataInputLabel({ isJson: false })).hasText('Version data'); }); + + test('it shows readonly info rows when viewing secret details of simple secret', async function (assert) { + assert.expect(3); + this.secret.secretData = { foo: 'bar' }; + this.secret.path = this.path; + + await render(hbs``, { + owner: this.engine, + }); + assert.dom(PAGE.infoRow).exists({ count: 1 }, '1 row of data shows'); + assert.dom(PAGE.infoRowValue('foo')).hasText('***********'); + await click(PAGE.infoRowToggleMasked('foo')); + assert.dom(PAGE.infoRowValue('foo')).hasText('bar', 'secret value shows after toggle'); + }); + + test('it shows readonly json editor when viewing secret details of complex secret', async function (assert) { + assert.expect(3); + this.secret.secretData = { + foo: { + bar: 'baz', + }, + }; + this.secret.path = this.path; + + await render(hbs``, { + owner: this.engine, + }); + assert.dom(PAGE.infoRowValue('foo')).doesNotExist('does not render rows of secret data'); + assert.dom('[data-test-component="code-mirror-modifier"]').hasClass('readonly-codemirror'); + assert.dom('[data-test-component="code-mirror-modifier"]').includesText(`{ "foo": { "bar": "baz" }}`); + }); }); diff --git a/ui/tests/integration/components/kv/page/kv-page-secret-details-test.js b/ui/tests/integration/components/kv/page/kv-page-secret-details-test.js index 1520818eb598..e570753989a8 100644 --- a/ui/tests/integration/components/kv/page/kv-page-secret-details-test.js +++ b/ui/tests/integration/components/kv/page/kv-page-secret-details-test.js @@ -23,8 +23,10 @@ module('Integration | Component | kv-v2 | Page::Secret::Details', function (hook this.server.post('/sys/capabilities-self', allowAllCapabilitiesStub()); this.backend = 'kv-engine'; this.path = 'my-secret'; + this.pathComplex = 'my-secret-object'; this.version = 2; this.dataId = kvDataPath(this.backend, this.path); + this.dataIdComplex = kvDataPath(this.backend, this.pathComplex); this.metadataId = kvMetadataPath(this.backend, this.path); this.secretData = { foo: 'bar' }; @@ -38,6 +40,22 @@ module('Integration | Component | kv-v2 | Page::Secret::Details', function (hook destroyed: false, version: this.version, }); + // nested secret + this.secretDataComplex = { + foo: { + bar: 'baz', + }, + }; + this.store.pushPayload('kv/data', { + modelName: 'kv/data', + id: this.dataIdComplex, + secret_data: this.secretDataComplex, + created_time: '2023-08-20T02:12:17.379762Z', + custom_metadata: null, + deletion_time: '', + destroyed: false, + version: this.version, + }); const metadata = this.server.create('kv-metadatum'); metadata.id = this.metadataId; @@ -48,6 +66,7 @@ module('Integration | Component | kv-v2 | Page::Secret::Details', function (hook this.metadata = this.store.peekRecord('kv/metadata', this.metadataId); this.secret = this.store.peekRecord('kv/data', this.dataId); + this.secretComplex = this.store.peekRecord('kv/data', this.dataIdComplex); // this is the route model, not an ember data model this.model = { @@ -61,6 +80,12 @@ module('Integration | Component | kv-v2 | Page::Secret::Details', function (hook { label: this.model.backend, route: 'list' }, { label: this.model.path }, ]; + this.modelComplex = { + backend: this.backend, + path: this.pathComplex, + secret: this.secretComplex, + metadata: this.metadata, + }; }); test('it renders secret details and toggles json view', async function (assert) { @@ -90,6 +115,23 @@ module('Integration | Component | kv-v2 | Page::Secret::Details', function (hook .includesText(`Version ${this.version} created`, 'renders version and time created'); }); + test('it renders json view when secret is complex', async function (assert) { + assert.expect(3); + await render( + hbs` + + `, + { owner: this.engine } + ); + assert.dom(PAGE.infoRowValue('foo')).doesNotExist('does not render rows of secret data'); + assert.dom(FORM.toggleJson).isDisabled(); + assert.dom('[data-test-component="code-mirror-modifier"]').includesText(`{ "foo": { "bar": "baz" }}`); + }); + test('it renders deleted empty state', async function (assert) { assert.expect(3); this.secret.deletionTime = '2023-07-23T02:12:17.379762Z';