mirror of
				https://github.com/optim-enterprises-bv/vault.git
				synced 2025-10-31 18:48:08 +00:00 
			
		
		
		
	UI: add copyable paths for CLI and API commands to kv v2 (#22551)
* add paths route * WIP copy secret path component * wip component * ad v1 * use each-in to iterate over info table row * update copy * add commands to kv paths page * add comments * WIP tests * finish tests * remove version, address comments and use path arg directly remove secret * update copy * fix typo for perms * remove destructuring, that was confusing * add changelog * add secure protocal
This commit is contained in:
		
							
								
								
									
										3
									
								
								changelog/22551.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								changelog/22551.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | |||||||
|  | ```release-note:feature | ||||||
|  | **Copyable KV v2 paths in UI**: KV v2 secret paths are copyable for use in CLI commands or API calls | ||||||
|  | ``` | ||||||
| @@ -3,7 +3,7 @@ | |||||||
|   SPDX-License-Identifier: BUSL-1.1 |   SPDX-License-Identifier: BUSL-1.1 | ||||||
| ~}} | ~}} | ||||||
|  |  | ||||||
| <div class="code-snippet-container"> | <div data-test-code-snippet class="code-snippet-container" ...attributes> | ||||||
|   <code class="text-grey-lightest"> |   <code class="text-grey-lightest"> | ||||||
|     {{@codeBlock}} |     {{@codeBlock}} | ||||||
|   </code> |   </code> | ||||||
|   | |||||||
| @@ -6,7 +6,7 @@ | |||||||
| {{#if (or (has-block) this.isVisible)}} | {{#if (or (has-block) this.isVisible)}} | ||||||
|   <div class="info-table-row" data-test-component="info-table-row" ...attributes> |   <div class="info-table-row" data-test-component="info-table-row" ...attributes> | ||||||
|     <div |     <div | ||||||
|       class="column is-one-quarter {{if this.hasLabelOverflow 'label-overflow'}}" |       class="column {{or @labelWidth 'is-one-quarter'}} {{if this.hasLabelOverflow 'label-overflow'}}" | ||||||
|       data-test-label-div |       data-test-label-div | ||||||
|       {{did-insert this.calculateLabelOverflow}} |       {{did-insert this.calculateLabelOverflow}} | ||||||
|     > |     > | ||||||
|   | |||||||
| @@ -2,6 +2,7 @@ | |||||||
|   <:tabLinks> |   <:tabLinks> | ||||||
|     <LinkTo @route="secret.details" data-test-secrets-tab="Secret">Secret</LinkTo> |     <LinkTo @route="secret.details" data-test-secrets-tab="Secret">Secret</LinkTo> | ||||||
|     <LinkTo @route="secret.metadata.index" data-test-secrets-tab="Metadata">Metadata</LinkTo> |     <LinkTo @route="secret.metadata.index" data-test-secrets-tab="Metadata">Metadata</LinkTo> | ||||||
|  |     <LinkTo @route="secret.paths" data-test-secrets-tab="Paths">Paths</LinkTo> | ||||||
|     {{#if @secret.canReadMetadata}} |     {{#if @secret.canReadMetadata}} | ||||||
|       <LinkTo @route="secret.metadata.versions" data-test-secrets-tab="Version History">Version History</LinkTo> |       <LinkTo @route="secret.metadata.versions" data-test-secrets-tab="Version History">Version History</LinkTo> | ||||||
|     {{/if}} |     {{/if}} | ||||||
|   | |||||||
| @@ -2,6 +2,7 @@ | |||||||
|   <:tabLinks> |   <:tabLinks> | ||||||
|     <LinkTo @route="secret.details" data-test-secrets-tab="Secret">Secret</LinkTo> |     <LinkTo @route="secret.details" data-test-secrets-tab="Secret">Secret</LinkTo> | ||||||
|     <LinkTo @route="secret.metadata.index" data-test-secrets-tab="Metadata">Metadata</LinkTo> |     <LinkTo @route="secret.metadata.index" data-test-secrets-tab="Metadata">Metadata</LinkTo> | ||||||
|  |     <LinkTo @route="secret.paths" data-test-secrets-tab="Paths">Paths</LinkTo> | ||||||
|     {{#if @secret.canReadMetadata}} |     {{#if @secret.canReadMetadata}} | ||||||
|       <LinkTo @route="secret.metadata.versions" data-test-secrets-tab="Version History">Version History</LinkTo> |       <LinkTo @route="secret.metadata.versions" data-test-secrets-tab="Version History">Version History</LinkTo> | ||||||
|     {{/if}} |     {{/if}} | ||||||
|   | |||||||
| @@ -2,6 +2,7 @@ | |||||||
|   <:tabLinks> |   <:tabLinks> | ||||||
|     <LinkTo @route="secret.details" data-test-secrets-tab="Secret">Secret</LinkTo> |     <LinkTo @route="secret.details" data-test-secrets-tab="Secret">Secret</LinkTo> | ||||||
|     <LinkTo @route="secret.metadata.index" data-test-secrets-tab="Metadata">Metadata</LinkTo> |     <LinkTo @route="secret.metadata.index" data-test-secrets-tab="Metadata">Metadata</LinkTo> | ||||||
|  |     <LinkTo @route="secret.paths" data-test-secrets-tab="Paths">Paths</LinkTo> | ||||||
|     <LinkTo @route="secret.metadata.versions" data-test-secrets-tab="Version History">Version History</LinkTo> |     <LinkTo @route="secret.metadata.versions" data-test-secrets-tab="Version History">Version History</LinkTo> | ||||||
|   </:tabLinks> |   </:tabLinks> | ||||||
| </KvPageHeader> | </KvPageHeader> | ||||||
|   | |||||||
							
								
								
									
										63
									
								
								ui/lib/kv/addon/components/page/secret/paths.hbs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								ui/lib/kv/addon/components/page/secret/paths.hbs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,63 @@ | |||||||
|  | <KvPageHeader @breadcrumbs={{@breadcrumbs}} @pageTitle={{@path}}> | ||||||
|  |   <:tabLinks> | ||||||
|  |     <LinkTo @route="secret.details" data-test-secrets-tab="Secret">Secret</LinkTo> | ||||||
|  |     <LinkTo @route="secret.metadata.index" data-test-secrets-tab="Metadata">Metadata</LinkTo> | ||||||
|  |     <LinkTo @route="secret.paths" data-test-secrets-tab="Paths">Paths</LinkTo> | ||||||
|  |     {{#if @canReadMetadata}} | ||||||
|  |       <LinkTo @route="secret.metadata.versions" data-test-secrets-tab="Version History">Version History</LinkTo> | ||||||
|  |     {{/if}} | ||||||
|  |   </:tabLinks> | ||||||
|  | </KvPageHeader> | ||||||
|  |  | ||||||
|  | <h2 class="title is-5 has-top-margin-xl"> | ||||||
|  |   Paths | ||||||
|  | </h2> | ||||||
|  |  | ||||||
|  | <div class="box is-fullwidth is-sideless is-paddingless is-marginless"> | ||||||
|  |   {{#each this.paths as |path|}} | ||||||
|  |     <InfoTableRow @label={{path.label}} @labelWidth="is-one-third" @helperText={{path.text}}> | ||||||
|  |       {{! replace with Hds::Copy::Snippet }} | ||||||
|  |       <CopyButton | ||||||
|  |         class="button is-compact is-transparent level-right" | ||||||
|  |         @clipboardText={{path.snippet}} | ||||||
|  |         @buttonType="button" | ||||||
|  |         @success={{fn (set-flash-message (concat path.label " copied!"))}} | ||||||
|  |       > | ||||||
|  |         <Icon @name="clipboard-copy" aria-label="Copy" /> | ||||||
|  |       </CopyButton> | ||||||
|  |       <code class="has-left-margin-s level-left"> | ||||||
|  |         {{path.snippet}} | ||||||
|  |       </code> | ||||||
|  |     </InfoTableRow> | ||||||
|  |   {{/each}} | ||||||
|  | </div> | ||||||
|  |  | ||||||
|  | <h2 class="title is-5 has-top-margin-xl"> | ||||||
|  |   Commands | ||||||
|  | </h2> | ||||||
|  |  | ||||||
|  | <div class="box is-fullwidth is-sideless"> | ||||||
|  |   <h3 class="is-label"> | ||||||
|  |     CLI | ||||||
|  |     <Hds::Badge @text="kv get" @color="neutral" /> | ||||||
|  |   </h3> | ||||||
|  |   <p class="helper-text has-text-grey has-bottom-padding-s"> | ||||||
|  |     This command retrieves the value from KV secrets engine at the given key name. For other CLI commands, | ||||||
|  |     <DocLink @path="/vault/docs/commands/kv"> | ||||||
|  |       learn more. | ||||||
|  |     </DocLink> | ||||||
|  |   </p> | ||||||
|  |   <CodeSnippet data-test-commands="cli" @codeBlock={{this.commands.cli}} /> | ||||||
|  |  | ||||||
|  |   <h3 class="has-top-margin-l is-label"> | ||||||
|  |     API read secret version | ||||||
|  |   </h3> | ||||||
|  |   <p class="helper-text has-text-grey has-bottom-padding-s"> | ||||||
|  |     This command obtains data and metadata for the latest version of this secret. In this example, Vault is located at | ||||||
|  |     https://127.0.0.1:8200. For other API commands, | ||||||
|  |     <DocLink @path="/vault/api-docs/secret/kv/kv-v2"> | ||||||
|  |       learn more. | ||||||
|  |     </DocLink> | ||||||
|  |   </p> | ||||||
|  |   <CodeSnippet data-test-commands="api" @clipboardCode={{this.commands.apiCopy}} @codeBlock={{this.commands.apiDisplay}} /> | ||||||
|  | </div> | ||||||
							
								
								
									
										72
									
								
								ui/lib/kv/addon/components/page/secret/paths.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								ui/lib/kv/addon/components/page/secret/paths.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,72 @@ | |||||||
|  | /** | ||||||
|  |  * Copyright (c) HashiCorp, Inc. | ||||||
|  |  * SPDX-License-Identifier: MPL-2.0 | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | import Component from '@glimmer/component'; | ||||||
|  | import { inject as service } from '@ember/service'; | ||||||
|  | import { kvMetadataPath, kvDataPath } from 'vault/utils/kv-path'; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * @module KvSecretPaths is used to display copyable secret paths for KV v2 for CLI and API use. | ||||||
|  |  * This view is permission agnostic because args come from the views mount path and url params. | ||||||
|  |  * | ||||||
|  |  * <Page::Secret::Paths | ||||||
|  |  *  @path={{this.model.path}} | ||||||
|  |  *  @backend={{this.model.backend}} | ||||||
|  |  *  @breadcrumbs={{this.breadcrumbs}} | ||||||
|  |  *  @canReadMetadata={{this.model.secret.canReadMetadata}} | ||||||
|  |  * /> | ||||||
|  |  * | ||||||
|  |  * @param {string} path - kv secret path for building the CLI and API paths | ||||||
|  |  * @param {string} backend - the secret engine mount path, comes from the secretMountPath service defined in the route | ||||||
|  |  * @param {array} breadcrumbs - Array to generate breadcrumbs, passed to the page header component | ||||||
|  |  * @param {boolean} [canReadMetadata=true] - if true, displays tab for Version History | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | export default class KvSecretPaths extends Component { | ||||||
|  |   @service namespace; | ||||||
|  |  | ||||||
|  |   get paths() { | ||||||
|  |     const { backend, path } = this.args; | ||||||
|  |     const namespace = this.namespace.path; | ||||||
|  |     const cli = `-mount="${backend}" "${path}"`; | ||||||
|  |     const data = kvDataPath(backend, path); | ||||||
|  |     const metadata = kvMetadataPath(backend, path); | ||||||
|  |  | ||||||
|  |     return [ | ||||||
|  |       { | ||||||
|  |         label: 'API path', | ||||||
|  |         snippet: namespace ? `/v1/${namespace}/${data}` : `/v1/${data}`, | ||||||
|  |         text: 'Use this path when referring to this secret in the API.', | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         label: 'CLI path', | ||||||
|  |         snippet: namespace ? `-namespace=${namespace} ${cli}` : cli, | ||||||
|  |         text: 'Use this path when referring to this secret in the CLI.', | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         label: 'API path for metadata', | ||||||
|  |         snippet: namespace ? `/v1/${namespace}/${metadata}` : `/v1/${metadata}`, | ||||||
|  |         text: `Use this path when referring to this secret's metadata in the API and permanent secret deletion.`, | ||||||
|  |       }, | ||||||
|  |     ]; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   get commands() { | ||||||
|  |     const cliPath = this.paths.findBy('label', 'CLI path').snippet; | ||||||
|  |     const apiPath = this.paths.findBy('label', 'API path').snippet; | ||||||
|  |     // as a future improvement, it might be nice to use window.location.protocol here: | ||||||
|  |     const url = `https://127.0.0.1:8200${apiPath}`; | ||||||
|  |  | ||||||
|  |     return { | ||||||
|  |       cli: `vault kv get ${cliPath}`, | ||||||
|  |       /* eslint-disable-next-line no-useless-escape */ | ||||||
|  |       apiCopy: `curl \ --header "X-Vault-Token: ..." \ --request GET \ ${url}`, | ||||||
|  |       apiDisplay: `curl \\ | ||||||
|  |         --header "X-Vault-Token: ..." \\ | ||||||
|  |         --request GET \\ | ||||||
|  |       ${url}`, | ||||||
|  |     }; | ||||||
|  |   } | ||||||
|  | } | ||||||
| @@ -12,6 +12,7 @@ export default buildRoutes(function () { | |||||||
|   this.route('list-directory', { path: '/:path_to_secret/directory' }); |   this.route('list-directory', { path: '/:path_to_secret/directory' }); | ||||||
|   this.route('create'); |   this.route('create'); | ||||||
|   this.route('secret', { path: '/:name' }, function () { |   this.route('secret', { path: '/:name' }, function () { | ||||||
|  |     this.route('paths'); | ||||||
|     this.route('details', function () { |     this.route('details', function () { | ||||||
|       this.route('edit'); // route to create new version of a secret |       this.route('edit'); // route to create new version of a secret | ||||||
|     }); |     }); | ||||||
|   | |||||||
							
								
								
									
										20
									
								
								ui/lib/kv/addon/routes/secret/paths.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								ui/lib/kv/addon/routes/secret/paths.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | |||||||
|  | /** | ||||||
|  |  * Copyright (c) HashiCorp, Inc. | ||||||
|  |  * SPDX-License-Identifier: MPL-2.0 | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | import Route from '@ember/routing/route'; | ||||||
|  | import { breadcrumbsForSecret } from 'kv/utils/kv-breadcrumbs'; | ||||||
|  |  | ||||||
|  | export default class KvSecretPathsRoute extends Route { | ||||||
|  |   setupController(controller, resolvedModel) { | ||||||
|  |     super.setupController(controller, resolvedModel); | ||||||
|  |  | ||||||
|  |     controller.breadcrumbs = [ | ||||||
|  |       { label: 'secrets', route: 'secrets', linkExternal: true }, | ||||||
|  |       { label: resolvedModel.backend, route: 'list' }, | ||||||
|  |       ...breadcrumbsForSecret(resolvedModel.path), | ||||||
|  |       { label: 'paths' }, | ||||||
|  |     ]; | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										6
									
								
								ui/lib/kv/addon/templates/secret/paths.hbs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								ui/lib/kv/addon/templates/secret/paths.hbs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | |||||||
|  | <Page::Secret::Paths | ||||||
|  |   @path={{this.model.path}} | ||||||
|  |   @backend={{this.model.backend}} | ||||||
|  |   @breadcrumbs={{this.breadcrumbs}} | ||||||
|  |   @canReadMetadata={{this.model.secret.canReadMetadata}} | ||||||
|  | /> | ||||||
| @@ -59,6 +59,11 @@ export const PAGE = { | |||||||
|   create: { |   create: { | ||||||
|     metadataSection: '[data-test-metadata-section]', |     metadataSection: '[data-test-metadata-section]', | ||||||
|   }, |   }, | ||||||
|  |   paths: { | ||||||
|  |     copyButton: (label) => `${PAGE.infoRowValue(label)} button`, | ||||||
|  |     codeSnippet: (section) => `[data-test-code-snippet][data-test-commands="${section}"] code`, | ||||||
|  |     snippetCopy: (section) => `[data-test-code-snippet][data-test-commands="${section}"] button`, | ||||||
|  |   }, | ||||||
| }; | }; | ||||||
|  |  | ||||||
| // Form/Interactive selectors that are common between pages and forms | // Form/Interactive selectors that are common between pages and forms | ||||||
|   | |||||||
| @@ -0,0 +1,148 @@ | |||||||
|  | /** | ||||||
|  |  * Copyright (c) HashiCorp, Inc. | ||||||
|  |  * SPDX-License-Identifier: MPL-2.0 | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | import { module, test } from 'qunit'; | ||||||
|  | import { setupRenderingTest } from 'ember-qunit'; | ||||||
|  | import { setupEngine } from 'ember-engines/test-support'; | ||||||
|  | import { render } from '@ember/test-helpers'; | ||||||
|  | import { hbs } from 'ember-cli-htmlbars'; | ||||||
|  | import { PAGE } from 'vault/tests/helpers/kv/kv-selectors'; | ||||||
|  | /* eslint-disable no-useless-escape */ | ||||||
|  |  | ||||||
|  | module('Integration | Component | kv-v2 | Page::Secret::Paths', function (hooks) { | ||||||
|  |   setupRenderingTest(hooks); | ||||||
|  |   setupEngine(hooks, 'kv'); | ||||||
|  |  | ||||||
|  |   hooks.beforeEach(async function () { | ||||||
|  |     this.backend = 'kv-engine'; | ||||||
|  |     this.path = 'my-secret'; | ||||||
|  |     this.breadcrumbs = [ | ||||||
|  |       { label: 'secrets', route: 'secrets', linkExternal: true }, | ||||||
|  |       { label: this.backend, route: 'list' }, | ||||||
|  |       { label: this.path }, | ||||||
|  |     ]; | ||||||
|  |  | ||||||
|  |     this.assertClipboard = (assert, element, expected) => { | ||||||
|  |       assert.dom(element).hasAttribute('data-clipboard-text', expected); | ||||||
|  |     }; | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   test('it renders copyable paths', async function (assert) { | ||||||
|  |     assert.expect(6); | ||||||
|  |  | ||||||
|  |     const paths = [ | ||||||
|  |       { label: 'API path', expected: `/v1/${this.backend}/data/${this.path}` }, | ||||||
|  |       { label: 'CLI path', expected: `-mount="${this.backend}" "${this.path}"` }, | ||||||
|  |       { label: 'API path for metadata', expected: `/v1/${this.backend}/metadata/${this.path}` }, | ||||||
|  |     ]; | ||||||
|  |  | ||||||
|  |     await render( | ||||||
|  |       hbs` | ||||||
|  | <Page::Secret::Paths | ||||||
|  |   @path={{this.path}} | ||||||
|  |   @backend={{this.backend}} | ||||||
|  |   @breadcrumbs={{this.breadcrumbs}} | ||||||
|  | /> | ||||||
|  |       `, | ||||||
|  |       { owner: this.engine } | ||||||
|  |     ); | ||||||
|  |  | ||||||
|  |     for (const path of paths) { | ||||||
|  |       assert.dom(PAGE.infoRowValue(path.label)).hasText(path.expected); | ||||||
|  |       this.assertClipboard(assert, PAGE.paths.copyButton(path.label), path.expected); | ||||||
|  |     } | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   test('it renders copyable encoded mount and secret paths', async function (assert) { | ||||||
|  |     assert.expect(6); | ||||||
|  |     this.path = `my spacey!"secret`; | ||||||
|  |     this.backend = `my fancy!"backend`; | ||||||
|  |     const backend = encodeURIComponent(this.backend); | ||||||
|  |     const path = encodeURIComponent(this.path); | ||||||
|  |     const paths = [ | ||||||
|  |       { | ||||||
|  |         label: 'API path', | ||||||
|  |         expected: `/v1/${backend}/data/${path}`, | ||||||
|  |       }, | ||||||
|  |       { label: 'CLI path', expected: `-mount="${this.backend}" "${this.path}"` }, | ||||||
|  |       { | ||||||
|  |         label: 'API path for metadata', | ||||||
|  |         expected: `/v1/${backend}/metadata/${path}`, | ||||||
|  |       }, | ||||||
|  |     ]; | ||||||
|  |  | ||||||
|  |     await render( | ||||||
|  |       hbs` | ||||||
|  | <Page::Secret::Paths | ||||||
|  |   @path={{this.path}} | ||||||
|  |   @backend={{this.backend}} | ||||||
|  |   @breadcrumbs={{this.breadcrumbs}} | ||||||
|  | /> | ||||||
|  |       `, | ||||||
|  |       { owner: this.engine } | ||||||
|  |     ); | ||||||
|  |  | ||||||
|  |     for (const path of paths) { | ||||||
|  |       assert.dom(PAGE.infoRowValue(path.label)).hasText(path.expected); | ||||||
|  |       this.assertClipboard(assert, PAGE.paths.copyButton(path.label), path.expected); | ||||||
|  |     } | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   test('it renders copyable commands', async function (assert) { | ||||||
|  |     assert.expect(4); | ||||||
|  |     const url = `https://127.0.0.1:8200/v1/${this.backend}/data/${this.path}`; | ||||||
|  |     const expected = { | ||||||
|  |       cli: `vault kv get -mount="${this.backend}" "${this.path}"`, | ||||||
|  |       apiDisplay: `curl \\ --header \"X-Vault-Token: ...\" \\ --request GET \\ ${url}`, | ||||||
|  |       apiCopy: `curl  --header \"X-Vault-Token: ...\"  --request GET \ ${url}`, | ||||||
|  |     }; | ||||||
|  |     await render( | ||||||
|  |       hbs` | ||||||
|  | <Page::Secret::Paths | ||||||
|  |   @path={{this.path}} | ||||||
|  |   @backend={{this.backend}} | ||||||
|  |   @breadcrumbs={{this.breadcrumbs}} | ||||||
|  | /> | ||||||
|  |       `, | ||||||
|  |       { owner: this.engine } | ||||||
|  |     ); | ||||||
|  |  | ||||||
|  |     assert.dom(PAGE.paths.codeSnippet('cli')).hasText(expected.cli); | ||||||
|  |     assert.dom(PAGE.paths.snippetCopy('cli')).hasAttribute('data-clipboard-text', expected.cli); | ||||||
|  |     assert.dom(PAGE.paths.codeSnippet('api')).hasText(expected.apiDisplay); | ||||||
|  |     assert.dom(PAGE.paths.snippetCopy('api')).hasAttribute('data-clipboard-text', expected.apiCopy); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   test('it renders copyable encoded mount and path commands', async function (assert) { | ||||||
|  |     assert.expect(4); | ||||||
|  |     this.path = `my spacey!"secret`; | ||||||
|  |     this.backend = `my fancy!"backend`; | ||||||
|  |  | ||||||
|  |     const backend = encodeURIComponent(this.backend); | ||||||
|  |     const path = encodeURIComponent(this.path); | ||||||
|  |     const url = `https://127.0.0.1:8200/v1/${backend}/data/${path}`; | ||||||
|  |  | ||||||
|  |     const expected = { | ||||||
|  |       cli: `vault kv get -mount="${this.backend}" "${this.path}"`, | ||||||
|  |       apiDisplay: `curl \\ --header \"X-Vault-Token: ...\" \\ --request GET \\ ${url}`, | ||||||
|  |       apiCopy: `curl  --header \"X-Vault-Token: ...\"  --request GET \ ${url}`, | ||||||
|  |     }; | ||||||
|  |     await render( | ||||||
|  |       hbs` | ||||||
|  | <Page::Secret::Paths | ||||||
|  |   @path={{this.path}} | ||||||
|  |   @backend={{this.backend}} | ||||||
|  |   @breadcrumbs={{this.breadcrumbs}} | ||||||
|  | /> | ||||||
|  |       `, | ||||||
|  |       { owner: this.engine } | ||||||
|  |     ); | ||||||
|  |  | ||||||
|  |     assert.dom(PAGE.paths.codeSnippet('cli')).hasText(expected.cli); | ||||||
|  |     assert.dom(PAGE.paths.snippetCopy('cli')).hasAttribute('data-clipboard-text', expected.cli); | ||||||
|  |     assert.dom(PAGE.paths.codeSnippet('api')).hasText(expected.apiDisplay); | ||||||
|  |     assert.dom(PAGE.paths.snippetCopy('api')).hasAttribute('data-clipboard-text', expected.apiCopy); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
		Reference in New Issue
	
	Block a user
	 claire bontempo
					claire bontempo