mirror of
https://github.com/hashicorp/vault.git
synced 2026-05-28 04:10:44 -04:00
* remove request for ldap config; * simplify tabs to accept routes as args * reuse tabs in ember engines Co-authored-by: claire bontempo <68122737+hellobontempo@users.noreply.github.com>
This commit is contained in:
parent
d602d6164f
commit
51c6f056bb
16 changed files with 216 additions and 283 deletions
|
|
@ -1,23 +0,0 @@
|
|||
{{!
|
||||
Copyright IBM Corp. 2016, 2025
|
||||
SPDX-License-Identifier: BUSL-1.1
|
||||
}}
|
||||
|
||||
<nav class="tabs" aria-label={{@engineMetadata.displayName}}>
|
||||
<ul>
|
||||
<li data-test-tab="general-settings">
|
||||
<LinkTo @route="{{this.routePrefix}}configuration.general-settings" @model={{@path}}>
|
||||
General settings
|
||||
</LinkTo>
|
||||
</li>
|
||||
{{! If there are not engine-specific settings do not render this tab }}
|
||||
{{#if @engineMetadata.isConfigurable}}
|
||||
<li data-test-tab="plugin-settings">
|
||||
<LinkTo @route={{this.pluginSettingsRoute}} @model={{@path}}>
|
||||
{{@engineMetadata.displayName}}
|
||||
settings
|
||||
</LinkTo>
|
||||
</li>
|
||||
{{/if}}
|
||||
</ul>
|
||||
</nav>
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import Component from '@glimmer/component';
|
||||
|
||||
import type { EngineDisplayData } from 'vault/utils/all-engines-metadata';
|
||||
|
||||
/**
|
||||
* @module ConfigureTabs
|
||||
* These tabs render in the shared general-settings, plugin-settings and edit routes of the secret engines headers.
|
||||
*
|
||||
* @param {string} [configRoute] - only passed when rendering the vault.cluster.secrets.backend.configuration.edit route to highlight the tab for that view
|
||||
* @param {object} engineMetadata - engine specific metadata
|
||||
* @param {boolean} [isConfigured] - whether an engine has been configured. if configured, plugin settings exist
|
||||
* @param {object} path - the secret engine mount path, sometimes referred to as the engine "id" or "backend"
|
||||
*/
|
||||
|
||||
interface Args {
|
||||
configRoute?: string;
|
||||
engineMetadata: EngineDisplayData;
|
||||
isConfigured: boolean;
|
||||
path: string;
|
||||
}
|
||||
|
||||
export default class ConfigureTabs extends Component<Args> {
|
||||
routePrefix = 'vault.cluster.secrets.backend.';
|
||||
|
||||
// The plugin settings tab only renders if an engine is configurable.
|
||||
// `configEditRoute` and `configReadRoute` are defined for engines with custom route patterns, like Ember engines.
|
||||
// Otherwise navigates to default 'backend.configuration' routes.
|
||||
get pluginSettingsRoute() {
|
||||
const { engineMetadata, isConfigured } = this.args;
|
||||
|
||||
// If the engine is configurable, but not configured, navigate to its edit route
|
||||
if (engineMetadata.isConfigurable && !isConfigured) {
|
||||
const route = engineMetadata.configEditRoute || 'configuration.edit';
|
||||
return this.routePrefix + route;
|
||||
}
|
||||
|
||||
// For configured engines, determine route based on context:
|
||||
// - If @configRoute is passed (from edit.hbs) the user has navigated to edit and this ensures the tab is highlighted.
|
||||
// - Otherwise, route to the read view (`configReadRoute` or 'plugin-settings')
|
||||
const route = this.args.configRoute || engineMetadata?.configReadRoute || 'configuration.plugin-settings';
|
||||
return this.routePrefix + route;
|
||||
}
|
||||
}
|
||||
|
|
@ -7,15 +7,17 @@
|
|||
@title="{{@model.secretsEngine.id}} configuration"
|
||||
@description={{engineDisplayData.displayName}}
|
||||
@icon={{engineDisplayData.glyph}}
|
||||
@subtitle={{engineDisplayData.typeDisplay}}
|
||||
>
|
||||
<:breadcrumbs>
|
||||
<Page::Breadcrumbs @breadcrumbs={{@breadcrumbs}} />
|
||||
</:breadcrumbs>
|
||||
<:tabs>
|
||||
<SecretEngine::ConfigureTabs
|
||||
@engineMetadata={{engineDisplayData}}
|
||||
@isConfigured={{not (is-empty-value @model.config)}}
|
||||
<Mount::ConfigureTabs
|
||||
@configRoute={{if
|
||||
engineDisplayData.isConfigurable
|
||||
(or engineDisplayData.configRoute "configuration.plugin-settings")
|
||||
}}
|
||||
@displayName={{engineDisplayData.displayName}}
|
||||
@path={{@model.secretsEngine.id}}
|
||||
/>
|
||||
</:tabs>
|
||||
|
|
|
|||
|
|
@ -2,20 +2,20 @@
|
|||
Copyright IBM Corp. 2016, 2025
|
||||
SPDX-License-Identifier: BUSL-1.1
|
||||
}}
|
||||
|
||||
{{#let (engines-display-data @model.secretsEngine.type) as |engineDisplayData|}}
|
||||
<Page::Header
|
||||
@title="{{@model.secretsEngine.id}} configuration"
|
||||
@description={{engineDisplayData.displayName}}
|
||||
@icon={{engineDisplayData.glyph}}
|
||||
@subtitle={{engineDisplayData.typeDisplay}}
|
||||
>
|
||||
<:breadcrumbs>
|
||||
<Page::Breadcrumbs @breadcrumbs={{@breadcrumbs}} />
|
||||
</:breadcrumbs>
|
||||
<:tabs>
|
||||
<SecretEngine::ConfigureTabs
|
||||
@engineMetadata={{engineDisplayData}}
|
||||
@isConfigured={{not (is-empty-value @model.config)}}
|
||||
<Mount::ConfigureTabs
|
||||
@configRoute={{or engineDisplayData.configRoute "configuration.plugin-settings"}}
|
||||
@displayName={{engineDisplayData.displayName}}
|
||||
@path={{@model.secretsEngine.id}}
|
||||
/>
|
||||
</:tabs>
|
||||
|
|
@ -28,70 +28,62 @@
|
|||
/>
|
||||
</:actions>
|
||||
</Page::Header>
|
||||
{{/let}}
|
||||
|
||||
{{#if @model.config}}
|
||||
<Toolbar>
|
||||
<ToolbarActions>
|
||||
<ToolbarLink
|
||||
@route="vault.cluster.secrets.backend.configuration.edit"
|
||||
@model={{this.model.secretsEngine.id}}
|
||||
data-test-secret-backend-configure
|
||||
>
|
||||
Edit configuration
|
||||
</ToolbarLink>
|
||||
</ToolbarActions>
|
||||
</Toolbar>
|
||||
{{#if @model.config}}
|
||||
<Toolbar>
|
||||
<ToolbarActions>
|
||||
<ToolbarLink
|
||||
@route="vault.cluster.secrets.backend.configuration.edit"
|
||||
@model={{this.model.secretsEngine.id}}
|
||||
data-test-secret-backend-configure
|
||||
>
|
||||
Edit configuration
|
||||
</ToolbarLink>
|
||||
</ToolbarActions>
|
||||
</Toolbar>
|
||||
|
||||
{{#each this.displayFields as |field|}}
|
||||
{{! public key while not sensitive when editing/creating, should be hidden by default on viewing }}
|
||||
{{#if (eq field "public_key")}}
|
||||
<InfoTableRow @label="Public key" @value={{@model.config.public_key}}>
|
||||
<MaskedInput @value={{@model.config.public_key}} @name={{field}} @displayOnly={{true}} @allowCopy={{true}} />
|
||||
</InfoTableRow>
|
||||
{{else}}
|
||||
<InfoTableRow
|
||||
@alwaysRender={{not (is-empty-value (get @model.config field))}}
|
||||
@label={{this.label field}}
|
||||
@value={{get @model.config field}}
|
||||
@formatTtl={{this.isDuration field}}
|
||||
/>
|
||||
{{/if}}
|
||||
{{/each}}
|
||||
{{else}}
|
||||
{{#if (get (engines-display-data @model.secretsEngine.type) "isConfigurable")}}
|
||||
{{#each this.displayFields as |field|}}
|
||||
{{! public key while not sensitive when editing/creating, should be hidden by default on viewing }}
|
||||
{{#if (eq field "public_key")}}
|
||||
<InfoTableRow @label="Public key" @value={{@model.config.public_key}}>
|
||||
<MaskedInput @value={{@model.config.public_key}} @name={{field}} @displayOnly={{true}} @allowCopy={{true}} />
|
||||
</InfoTableRow>
|
||||
{{else}}
|
||||
<InfoTableRow
|
||||
@alwaysRender={{not (is-empty-value (get @model.config field))}}
|
||||
@label={{this.label field}}
|
||||
@value={{get @model.config field}}
|
||||
@formatTtl={{this.isDuration field}}
|
||||
/>
|
||||
{{/if}}
|
||||
{{/each}}
|
||||
{{else if engineDisplayData.isConfigurable}}
|
||||
{{! Prompt user to configure the secret engine }}
|
||||
<EmptyState
|
||||
@title="{{get (engines-display-data @model.secretsEngine.type) 'displayName'}} not configured"
|
||||
@message="Get started by configuring your {{get
|
||||
(engines-display-data @model.secretsEngine.type)
|
||||
'displayName'
|
||||
}} secrets engine."
|
||||
@title="{{engineDisplayData.displayName}} not configured"
|
||||
@message="Get started by configuring your {{engineDisplayData.displayName}} secrets engine."
|
||||
>
|
||||
<Hds::Link::Standalone
|
||||
@icon="chevron-right"
|
||||
@iconPosition="trailing"
|
||||
@text="Configure {{get (engines-display-data @model.secretsEngine.type) 'displayName'}}"
|
||||
@text="Configure {{engineDisplayData.displayName}}"
|
||||
@route="vault.cluster.secrets.backend.configuration.edit"
|
||||
@model={{@id}}
|
||||
@model={{@model.secretsEngine.id}}
|
||||
/>
|
||||
</EmptyState>
|
||||
{{else}}
|
||||
<EmptyState
|
||||
data-test-no-config
|
||||
@title="No configuration details available"
|
||||
@message="{{get
|
||||
(engines-display-data @model.secretsEngine.type)
|
||||
'displayName'
|
||||
}} does not have any plugin specific configuration. All configurable parameters for this engine are under 'General Settings'."
|
||||
@message="{{engineDisplayData.displayName}} does not have any plugin specific configuration. All configurable parameters for this engine are under 'General Settings'."
|
||||
>
|
||||
<Hds::Link::Standalone
|
||||
@icon="chevron-right"
|
||||
@iconPosition="trailing"
|
||||
@text="Back to general settings"
|
||||
@route="vault.cluster.secrets.backend.configuration.general-settings"
|
||||
@model={{@id}}
|
||||
@model={{@model.secretsEngine.id}}
|
||||
/>
|
||||
</EmptyState>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
{{/let}}
|
||||
|
|
@ -21,15 +21,23 @@ export default class SecretsBackendConfigurationRoute extends Route {
|
|||
const { type, id } = secretsEngine;
|
||||
return {
|
||||
secretsEngine,
|
||||
config: await this.fetchConfig(type, id), // fetch config for configurable engines (aws, azure, gcp, ssh)
|
||||
// fetch config for configurable secrets engines that use the "general" route pattern: aws, azure, gcp, ssh
|
||||
// ember-engines manage their own engine config routes and requests so do not fetch here.
|
||||
config: await this.fetchConfig(type, id),
|
||||
};
|
||||
}
|
||||
|
||||
// TODO after update to show separated general settings vs plugin settings redirect if not configured?
|
||||
// afterModel(resolvedModel) {
|
||||
// // Redirect to edit route if not configured
|
||||
// if (!resolvedModel.config) {
|
||||
// this.router.transitionTo('vault.cluster.secrets.backend.configuration.edit');
|
||||
// }
|
||||
// }
|
||||
|
||||
fetchConfig(type, id) {
|
||||
// id is the path where the backend is mounted since there's only one config per engine (often this path is referred to just as backend)
|
||||
switch (type) {
|
||||
case 'ldap':
|
||||
return this.fetchLdapConfigs(id);
|
||||
case 'aws':
|
||||
return this.fetchAwsConfigs(id);
|
||||
case 'azure':
|
||||
|
|
@ -41,18 +49,6 @@ export default class SecretsBackendConfigurationRoute extends Route {
|
|||
}
|
||||
}
|
||||
|
||||
async fetchLdapConfigs(path) {
|
||||
try {
|
||||
const { data } = await this.api.secrets.ldapReadConfiguration(path);
|
||||
return data;
|
||||
} catch (e) {
|
||||
const error = await this.parseApiError(e);
|
||||
if (error.httpStatus === 404) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fetchAwsConfigs(path) {
|
||||
// AWS has two configuration endpoints root and lease, as well as a separate endpoint for the issuer.
|
||||
const handleError = async (e) => {
|
||||
|
|
|
|||
|
|
@ -20,12 +20,4 @@ export default class SecretsBackendConfigurationIndexRoute extends Route {
|
|||
return this.router.replaceWith('vault.cluster.secrets.backend.configuration.general-settings');
|
||||
}
|
||||
}
|
||||
|
||||
setupController(controller, resolvedModel) {
|
||||
super.setupController(controller, resolvedModel);
|
||||
const engine = engineDisplayData(resolvedModel.secretsEngine.type);
|
||||
controller.typeDisplay = engine.displayName;
|
||||
controller.isConfigurable = engine.isConfigurable ?? false;
|
||||
controller.modelId = resolvedModel.secretsEngine.id;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@
|
|||
@title={{concat this.model.secretsEngine.id " configuration"}}
|
||||
@description={{engineDisplayData.displayName}}
|
||||
@icon={{engineDisplayData.glyph}}
|
||||
@subtitle={{engineDisplayData.typeDisplay}}
|
||||
>
|
||||
<:breadcrumbs>
|
||||
<Page::Breadcrumbs @breadcrumbs={{this.breadcrumbs}} />
|
||||
|
|
@ -40,10 +39,9 @@
|
|||
/>
|
||||
</:actions>
|
||||
<:tabs>
|
||||
<SecretEngine::ConfigureTabs
|
||||
<Mount::ConfigureTabs
|
||||
@configRoute="configuration.edit"
|
||||
@engineMetadata={{engineDisplayData}}
|
||||
@isConfigured={{not (is-empty-value this.model.config)}}
|
||||
@displayName={{engineDisplayData.displayName}}
|
||||
@path={{this.model.secretsEngine.id}}
|
||||
/>
|
||||
</:tabs>
|
||||
|
|
|
|||
|
|
@ -5,36 +5,41 @@
|
|||
|
||||
<SecretListHeader @model={{this.model.secretsEngine}} @isConfigure={{true}} />
|
||||
|
||||
{{#if this.isConfigurable}}
|
||||
<Toolbar>
|
||||
<ToolbarActions>
|
||||
<ToolbarLink
|
||||
@route="vault.cluster.secrets.backend.configuration.edit"
|
||||
@model={{this.model.secretsEngine.id}}
|
||||
data-test-secret-backend-configure
|
||||
>
|
||||
Configure
|
||||
</ToolbarLink>
|
||||
</ToolbarActions>
|
||||
</Toolbar>
|
||||
{{#let (engines-display-data @model.secretsEngine.type) as |engineMetadata|}}
|
||||
{{#if engineMetadata.isConfigurable}}
|
||||
<Toolbar>
|
||||
<ToolbarActions>
|
||||
<ToolbarLink
|
||||
@route="vault.cluster.secrets.backend.configuration.edit"
|
||||
@model={{this.model.secretsEngine.id}}
|
||||
data-test-secret-backend-configure
|
||||
>
|
||||
Configure
|
||||
</ToolbarLink>
|
||||
</ToolbarActions>
|
||||
</Toolbar>
|
||||
|
||||
<SecretEngine::ConfigurationDetails @config={{this.model.config}} @typeDisplay={{this.typeDisplay}} @id={{this.modelId}} />
|
||||
<SecretEngine::ConfigurationDetails
|
||||
@config={{this.model.config}}
|
||||
@typeDisplay={{engineMetadata.displayName}}
|
||||
@id={{this.model.secretsEngine.id}}
|
||||
/>
|
||||
|
||||
<SecretsEngineMountConfig
|
||||
@secretsEngine={{this.model.secretsEngine}}
|
||||
class="has-top-margin-xl has-bottom-margin-xl"
|
||||
data-test-mount-config
|
||||
/>
|
||||
|
||||
{{else}}
|
||||
<div class="box is-fullwidth is-sideless is-paddingless is-marginless">
|
||||
{{#each this.displayFields as |field|}}
|
||||
<InfoTableRow
|
||||
@alwaysRender={{and (not (is-empty-value (get this.model.secretsEngine field))) (not-eq field "version")}}
|
||||
@formatTtl={{includes field (array "config.default_lease_ttl" "config.max_lease_ttl")}}
|
||||
@label={{this.label field}}
|
||||
@value={{get this.model.secretsEngine field}}
|
||||
/>
|
||||
{{/each}}
|
||||
</div>
|
||||
{{/if}}
|
||||
<SecretsEngineMountConfig
|
||||
@secretsEngine={{this.model.secretsEngine}}
|
||||
class="has-top-margin-xl has-bottom-margin-xl"
|
||||
data-test-mount-config
|
||||
/>
|
||||
{{else}}
|
||||
<div class="box is-fullwidth is-sideless is-paddingless is-marginless">
|
||||
{{#each this.displayFields as |field|}}
|
||||
<InfoTableRow
|
||||
@alwaysRender={{and (not (is-empty-value (get this.model.secretsEngine field))) (not-eq field "version")}}
|
||||
@formatTtl={{includes field (array "config.default_lease_ttl" "config.max_lease_ttl")}}
|
||||
@label={{this.label field}}
|
||||
@value={{get this.model.secretsEngine field}}
|
||||
/>
|
||||
{{/each}}
|
||||
</div>
|
||||
{{/if}}
|
||||
{{/let}}
|
||||
|
|
@ -35,8 +35,7 @@ export interface EngineDisplayData {
|
|||
isOldEngine?: boolean; // flag for engine views, if set to true, the engine will show pre-existing page design, if not, then the new views will be used. This is temporary until all engines have been migrated to the new design.
|
||||
type: string;
|
||||
value?: string;
|
||||
configReadRoute?: string; // override for custom route if not "configuration.plugin-settings" (used for Ember engines)
|
||||
configEditRoute?: string; // override for custom route if not "configuration.edit" (used for Ember engines)
|
||||
configRoute?: string; // override for custom route if not "configuration.plugin-settings" (used for Ember engines)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -215,8 +214,7 @@ export const ALL_ENGINES: EngineDisplayData[] = [
|
|||
displayName: 'LDAP',
|
||||
isConfigurable: true,
|
||||
engineRoute: 'ldap.overview',
|
||||
configEditRoute: 'ldap.configure',
|
||||
configReadRoute: 'ldap.configuration',
|
||||
configRoute: 'ldap.configuration',
|
||||
glyph: 'folder-users',
|
||||
mountCategory: ['auth', 'secret'],
|
||||
type: 'ldap',
|
||||
|
|
|
|||
35
ui/lib/core/addon/components/mount/configure-tabs.hbs
Normal file
35
ui/lib/core/addon/components/mount/configure-tabs.hbs
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
{{!
|
||||
Copyright IBM Corp. 2016, 2025
|
||||
SPDX-License-Identifier: BUSL-1.1
|
||||
}}
|
||||
|
||||
<nav class="tabs" aria-label={{@displayName}}>
|
||||
<ul>
|
||||
<li data-test-tab="general-settings">
|
||||
{{! General settings are the mount details for a secrets engine - all engines have these }}
|
||||
{{#if @externalRoute}}
|
||||
<LinkToExternal @route={{@externalRoute}} @model={{@path}}>
|
||||
General settings
|
||||
</LinkToExternal>
|
||||
{{else}}
|
||||
<LinkTo @route="vault.cluster.secrets.backend.configuration.general-settings" @model={{@path}}>
|
||||
General settings
|
||||
</LinkTo>
|
||||
{{/if}}
|
||||
</li>
|
||||
{{! @configRoute is supplied if an engine is configurable - only some engines support additional configuration }}
|
||||
{{#if @configRoute}}
|
||||
<li data-test-tab="plugin-settings">
|
||||
{{! if @externalRoute is provided this component is rendering in an ember engine and just @configRoute is sufficient
|
||||
otherwise we need to provide the full route by concatenating the prefix. }}
|
||||
<LinkTo
|
||||
@route={{if @externalRoute @configRoute (concat "vault.cluster.secrets.backend." @configRoute)}}
|
||||
@model={{@path}}
|
||||
>
|
||||
{{or @displayName "Plugin"}}
|
||||
settings
|
||||
</LinkTo>
|
||||
</li>
|
||||
{{/if}}
|
||||
</ul>
|
||||
</nav>
|
||||
6
ui/lib/core/app/components/mount/configure-tabs.js
Normal file
6
ui/lib/core/app/components/mount/configure-tabs.js
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
/**
|
||||
* Copyright IBM Corp. 2016, 2025
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
export { default } from 'core/components/mount/configure-tabs';
|
||||
|
|
@ -33,28 +33,22 @@
|
|||
{{/if}}
|
||||
</:actions>
|
||||
<:tabs>
|
||||
<div class="tabs-container box is-marginless is-fullwidth is-paddingless">
|
||||
{{#if @configRoute}}
|
||||
<Mount::ConfigureTabs
|
||||
@configRoute={{@configRoute}}
|
||||
@displayName="LDAP"
|
||||
@path={{@model.id}}
|
||||
@externalRoute="secretsGeneralSettingsConfiguration"
|
||||
/>
|
||||
{{else}}
|
||||
<nav class="tabs" aria-label="ldap tabs">
|
||||
<ul>
|
||||
{{#if @configRoute}}
|
||||
<li data-test-tab="general-settings">
|
||||
<LinkToExternal @route="secretsGeneralSettingsConfiguration" @model={{@model.id}}>
|
||||
General settings
|
||||
</LinkToExternal>
|
||||
</li>
|
||||
<li data-test-tab="plugin-settings">
|
||||
<LinkTo @route={{@configRoute}} @model={{@model.id}}>
|
||||
LDAP settings
|
||||
</LinkTo>
|
||||
</li>
|
||||
{{else}}
|
||||
<li><LinkTo @route="overview" data-test-tab="overview">Overview</LinkTo></li>
|
||||
<li><LinkTo @route="roles" data-test-tab="roles">Roles</LinkTo></li>
|
||||
<li><LinkTo @route="libraries" data-test-tab="libraries">Libraries</LinkTo></li>
|
||||
{{/if}}
|
||||
<li><LinkTo @route="overview" data-test-tab="overview">Overview</LinkTo></li>
|
||||
<li><LinkTo @route="roles" data-test-tab="roles">Roles</LinkTo></li>
|
||||
<li><LinkTo @route="libraries" data-test-tab="libraries">Libraries</LinkTo></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
{{/if}}
|
||||
</:tabs>
|
||||
</Page::Header>
|
||||
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ module('Acceptance | ldap | overview', function (hooks) {
|
|||
`write ${backend}/config binddn=foo bindpass=bar url=http://localhost:8208`,
|
||||
]);
|
||||
};
|
||||
this.expectedConfigEditRoute = 'ldap.configure';
|
||||
return login();
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -11,15 +11,14 @@ import { GENERAL } from 'vault/tests/helpers/general-selectors';
|
|||
import { SECRET_ENGINE_SELECTORS as SES } from 'vault/tests/helpers/secret-engine/secret-engine-selectors';
|
||||
|
||||
// To use this helper for configurable engines
|
||||
// define `this.mountAndConfig` in the beforeEach hook
|
||||
// define `this.mountAndConfig` and this.expectedConfigEditRoute in the beforeEach hook
|
||||
// (see "Acceptance | ldap | overview" as an example)
|
||||
const BASE_ROUTE = 'vault.cluster.secrets.backend';
|
||||
|
||||
export default (test, type) => {
|
||||
const {
|
||||
isConfigurable = false,
|
||||
configReadRoute = 'configuration.plugin-settings',
|
||||
configEditRoute = 'configuration.edit',
|
||||
configRoute = 'configuration.plugin-settings',
|
||||
engineRoute = 'list-root',
|
||||
} = engineDisplayData(type);
|
||||
|
||||
|
|
@ -35,7 +34,7 @@ export default (test, type) => {
|
|||
await click(GENERAL.menuItem('View configuration'));
|
||||
assert.strictEqual(
|
||||
currentRouteName(),
|
||||
`${BASE_ROUTE}.${configEditRoute}`,
|
||||
`${BASE_ROUTE}.${this.expectedConfigEditRoute}`,
|
||||
'it navigates to the configure route from the list view'
|
||||
);
|
||||
|
||||
|
|
@ -57,7 +56,7 @@ export default (test, type) => {
|
|||
await click(GENERAL.tabLink('plugin-settings'));
|
||||
assert.strictEqual(
|
||||
currentRouteName(),
|
||||
`${BASE_ROUTE}.${configEditRoute}`,
|
||||
`${BASE_ROUTE}.${this.expectedConfigEditRoute}`,
|
||||
'clicking plugin settings navigates to edit route when not configured'
|
||||
);
|
||||
assert
|
||||
|
|
@ -79,7 +78,7 @@ export default (test, type) => {
|
|||
await click(GENERAL.menuItem('View configuration'));
|
||||
assert.strictEqual(
|
||||
currentRouteName(),
|
||||
`${BASE_ROUTE}.${configReadRoute}`,
|
||||
`${BASE_ROUTE}.${configRoute}`,
|
||||
'it navigates to the configure route from the list view'
|
||||
);
|
||||
|
||||
|
|
@ -102,7 +101,7 @@ export default (test, type) => {
|
|||
// Confirm tabs after clicking plugin-settings
|
||||
assert.strictEqual(
|
||||
currentRouteName(),
|
||||
`${BASE_ROUTE}.${configReadRoute}`,
|
||||
`${BASE_ROUTE}.${configRoute}`,
|
||||
'it navigates to the read route when configured'
|
||||
);
|
||||
assert
|
||||
|
|
@ -114,7 +113,7 @@ export default (test, type) => {
|
|||
await click(SES.configure);
|
||||
assert.strictEqual(
|
||||
currentRouteName(),
|
||||
`${BASE_ROUTE}.${configEditRoute}`,
|
||||
`${BASE_ROUTE}.${this.expectedConfigEditRoute}`,
|
||||
'it navigates to the edit route'
|
||||
);
|
||||
assert
|
||||
|
|
@ -135,7 +134,7 @@ export default (test, type) => {
|
|||
await click(GENERAL.tabLink('plugin-settings'));
|
||||
assert.strictEqual(
|
||||
currentRouteName(),
|
||||
`${BASE_ROUTE}.${configReadRoute}`,
|
||||
`${BASE_ROUTE}.${configRoute}`,
|
||||
'it navigates to the read route when configured'
|
||||
);
|
||||
await click(GENERAL.button('Exit configuration'));
|
||||
|
|
|
|||
58
ui/tests/integration/components/mount/configure-tabs-test.js
Normal file
58
ui/tests/integration/components/mount/configure-tabs-test.js
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
/**
|
||||
* Copyright IBM Corp. 2016, 2025
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import { module, test } from 'qunit';
|
||||
import { setupRenderingTest } from 'ember-qunit';
|
||||
import { click, render } from '@ember/test-helpers';
|
||||
import { GENERAL } from 'vault/tests/helpers/general-selectors';
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
|
||||
module('Integration | Component | mount/configure-tabs', function (hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
hooks.beforeEach(function () {
|
||||
this.configRoute = 'pki.configuration.create';
|
||||
// This component also accepts @path but that's route related and irrelevant to an integration test
|
||||
this.renderComponent = () => {
|
||||
return render(
|
||||
hbs`<Mount::ConfigureTabs
|
||||
@displayName={{this.displayName}}
|
||||
@configRoute={{this.configRoute}}
|
||||
@path="my-pki-engine"
|
||||
/>`
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
test('it renders when args are undefined', async function (assert) {
|
||||
this.configRoute = undefined;
|
||||
this.displayName = undefined;
|
||||
await this.renderComponent();
|
||||
assert.dom(GENERAL.tab('general-settings')).exists().hasText('General settings');
|
||||
});
|
||||
|
||||
test('it renders plugin settings tab when @configRoute provided', async function (assert) {
|
||||
this.displayName = 'PKI';
|
||||
await this.renderComponent();
|
||||
|
||||
assert.dom(GENERAL.tab('general-settings')).exists().hasText('General settings');
|
||||
await click(GENERAL.tab('plugin-settings'));
|
||||
assert.dom(GENERAL.tab('plugin-settings')).exists().hasText('PKI settings');
|
||||
});
|
||||
|
||||
test('it renders fallback when @displayName not provided', async function (assert) {
|
||||
this.displayName = '';
|
||||
await this.renderComponent();
|
||||
assert.dom(GENERAL.tab('plugin-settings')).exists().hasText('Plugin settings');
|
||||
});
|
||||
|
||||
test('it hides plugin settings when there is no @configRoute', async function (assert) {
|
||||
this.configRoute = '';
|
||||
await this.renderComponent();
|
||||
|
||||
assert.dom(GENERAL.tab('general-settings')).exists().hasText('General settings');
|
||||
assert.dom(GENERAL.tab('plugin-settings')).doesNotExist();
|
||||
});
|
||||
});
|
||||
|
|
@ -1,72 +0,0 @@
|
|||
/**
|
||||
* Copyright IBM Corp. 2016, 2025
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import { module, test } from 'qunit';
|
||||
import { setupRenderingTest } from 'ember-qunit';
|
||||
import { render } from '@ember/test-helpers';
|
||||
import { filterEnginesByMountCategory } from 'vault/utils/all-engines-metadata';
|
||||
import { GENERAL } from 'vault/tests/helpers/general-selectors';
|
||||
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
import engineDisplayData from 'vault/helpers/engines-display-data';
|
||||
|
||||
// The `configurable` array is hardcoded to validate that ALL_ENGINES metadata is correctly
|
||||
// defined to render the tabs correctly.
|
||||
const configurable = ['aws', 'azure', 'gcp', 'ldap', 'ssh'];
|
||||
|
||||
module('Integration | Component | secret-engines/configure-tabs', function (hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
hooks.beforeEach(function () {
|
||||
this.isConfigured = undefined;
|
||||
// This component accepts more args, but they are route related and instead asserted by acceptance tests
|
||||
this.renderComponent = (type) => {
|
||||
this.engineMetadata = type ? engineDisplayData(type) : undefined;
|
||||
return render(
|
||||
hbs`<SecretEngine::ConfigureTabs
|
||||
@engineMetadata={{this.engineMetadata}}
|
||||
@isConfigured={{this.isConfigured}}
|
||||
/>`
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
test('it renders when args are undefined', async function (assert) {
|
||||
await this.renderComponent();
|
||||
assert.dom(GENERAL.tab('general-settings')).exists().hasText('General settings');
|
||||
});
|
||||
|
||||
for (const { type } of filterEnginesByMountCategory({ mountCategory: 'secret' })) {
|
||||
if (configurable.includes(type)) {
|
||||
test(`${type} (configurable): it renders expected tabs when not configured`, async function (assert) {
|
||||
await this.renderComponent(type);
|
||||
assert.dom(GENERAL.tab('general-settings')).exists().hasText('General settings');
|
||||
assert
|
||||
.dom(GENERAL.tab('plugin-settings'))
|
||||
.exists()
|
||||
.hasText(`${this.engineMetadata.displayName} settings`);
|
||||
});
|
||||
|
||||
test(`${type} (configurable): it renders expected tabs when configured`, async function (assert) {
|
||||
this.isConfigured = true;
|
||||
await this.renderComponent(type);
|
||||
|
||||
assert.dom(GENERAL.tab('general-settings')).exists().hasText('General settings');
|
||||
assert
|
||||
.dom(GENERAL.tab('plugin-settings'))
|
||||
.exists()
|
||||
.hasText(`${this.engineMetadata.displayName} settings`);
|
||||
});
|
||||
} else {
|
||||
// NON-CONFIGURABLE ENGINES
|
||||
test(`${type} it hides plugin settings when not configurable`, async function (assert) {
|
||||
await this.renderComponent(type);
|
||||
|
||||
assert.dom(GENERAL.tab('general-settings')).exists().hasText('General settings');
|
||||
assert.dom(GENERAL.tab('plugin-settings')).doesNotExist();
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
Loading…
Reference in a new issue