Skip to content

Commit 49b1992

Browse files
authored
Merge branch 'main' into ibm-license-tests-ce-test
2 parents b9387c9 + 93eacdd commit 49b1992

File tree

6 files changed

+178
-99
lines changed

6 files changed

+178
-99
lines changed

ui/app/components/page/namespaces.hbs

Lines changed: 139 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -16,90 +16,150 @@
1616
@breadcrumbs={{array (hash label="Vault" route="vault.cluster.dashboard" icon="vault") (hash label="Namespaces")}}
1717
/>
1818
</:breadcrumbs>
19+
<:badges>
20+
<Hds::Badge @icon="org" @text={{this.namespacePath}} data-test-badge="namespace-path" />
21+
</:badges>
22+
<:actions>
23+
{{#unless @model.namespaces}}
24+
<Hds::Button
25+
class="has-right-margin-4"
26+
@color="secondary"
27+
@icon="bulb"
28+
@text="New to Namespaces?"
29+
{{on "click" this.enterGuidedStart}}
30+
data-test-button="guided-start"
31+
/>
32+
<Hds::Button
33+
class="has-right-margin-4"
34+
@icon="plus"
35+
@route="vault.cluster.access.namespaces.create"
36+
@text="Create namespace"
37+
data-test-button="create-namespace"
38+
/>
39+
{{/unless}}
40+
</:actions>
1941
</Page::Header>
2042

21-
<Toolbar>
22-
<ToolbarFilters>
23-
<FilterInputExplicit
24-
@query={{@model.pageFilter}}
25-
@placeholder="Search"
26-
@handleSearch={{this.handleSearch}}
27-
@handleInput={{this.handleInput}}
28-
@handleKeyDown={{this.handleKeyDown}}
29-
/>
30-
</ToolbarFilters>
31-
<ToolbarActions>
32-
<Hds::Button
33-
class="has-right-margin-4"
34-
@color="secondary"
35-
@icon="reload"
36-
@iconPosition="trailing"
37-
@text="Refresh list"
38-
{{on "click" this.refreshNamespaceList}}
39-
data-test-button="refresh-namespace-list"
40-
/>
41-
<ToolbarLink @route="vault.cluster.access.namespaces.create" @type="add" data-test-link-to="create-namespace">
42-
Create namespace
43-
</ToolbarLink>
44-
</ToolbarActions>
45-
</Toolbar>
46-
47-
<ListView
48-
@items={{@model.namespaces}}
49-
@itemNoun="namespace"
50-
@paginationRouteName="vault.cluster.access.namespaces"
51-
@onPageChange={{this.handlePageChange}}
52-
as |list|
53-
>
54-
{{#if @model.namespaces.length}}
55-
<ListItem as |Item|>
56-
<Item.content>
57-
{{list.item.id}}
58-
</Item.content>
59-
<Item.menu>
60-
<Hds::Dropdown @isInline={{true}} @listPosition="bottom-right" as |dd|>
61-
<dd.ToggleIcon
62-
@icon="more-horizontal"
63-
@text="More options"
64-
@hasChevron={{false}}
65-
data-test-popup-menu-trigger
66-
/>
67-
{{#let (concat this.namespace.path (if this.namespace.path "/") list.item.id) as |targetNamespace|}}
68-
{{#if (includes targetNamespace this.namespace.accessibleNamespaces)}}
69-
<dd.Interactive
70-
{{on "click" (fn this.switchNamespace targetNamespace)}}
71-
data-test-popup-menu="switch"
72-
>Switch to namespace</dd.Interactive>
73-
{{/if}}
74-
{{/let}}
75-
<dd.Interactive
76-
@color="critical"
77-
{{on "click" (fn (mut this.nsToDelete) list.item)}}
78-
data-test-popup-menu="delete"
79-
>Delete</dd.Interactive>
80-
</Hds::Dropdown>
81-
{{#if (eq this.nsToDelete list.item)}}
82-
<ConfirmModal
83-
@color="critical"
84-
@onClose={{fn (mut this.nsToDelete) null}}
85-
@onConfirm={{fn this.deleteNamespace list.item}}
86-
@confirmTitle="Delete this namespace?"
87-
@confirmMessage="Any engines or mounts in this namespace will also be removed."
88-
/>
89-
{{/if}}
90-
</Item.menu>
91-
</ListItem>
92-
{{else}}
93-
<list.empty>
94-
<Hds::Link::Standalone
95-
@icon="learn-link"
96-
@text="Secure multi-tenancy with namespaces tutorial"
97-
@href={{doc-link "/vault/tutorials/enterprise/namespaces"}}
43+
{{#if @model.namespaces}}
44+
<Toolbar>
45+
<ToolbarFilters>
46+
<FilterInputExplicit
47+
@query={{@model.pageFilter}}
48+
@placeholder="Search"
49+
@handleSearch={{this.handleSearch}}
50+
@handleInput={{this.handleInput}}
51+
@handleKeyDown={{this.handleKeyDown}}
52+
/>
53+
</ToolbarFilters>
54+
<ToolbarActions>
55+
<Hds::Button
56+
class="has-right-margin-4"
57+
@color="secondary"
58+
@icon="reload"
59+
@iconPosition="trailing"
60+
@text="Refresh list"
61+
{{on "click" this.refreshNamespaceList}}
62+
data-test-button="refresh-namespace-list"
9863
/>
99-
</list.empty>
64+
<ToolbarLink @route="vault.cluster.access.namespaces.create" @type="add" data-test-link-to="create-namespace">
65+
Create namespace
66+
</ToolbarLink>
67+
</ToolbarActions>
68+
</Toolbar>
69+
70+
<ListView
71+
@items={{@model.namespaces}}
72+
@itemNoun="namespace"
73+
@paginationRouteName="vault.cluster.access.namespaces"
74+
@onPageChange={{this.handlePageChange}}
75+
as |list|
76+
>
77+
{{#if @model.namespaces.length}}
78+
<ListItem as |Item|>
79+
<Item.content>
80+
{{list.item.id}}
81+
</Item.content>
82+
<Item.menu>
83+
<Hds::Dropdown @isInline={{true}} @listPosition="bottom-right" as |dd|>
84+
<dd.ToggleIcon
85+
@icon="more-horizontal"
86+
@text="More options"
87+
@hasChevron={{false}}
88+
data-test-popup-menu-trigger
89+
/>
90+
{{#let (concat this.namespace.path (if this.namespace.path "/") list.item.id) as |targetNamespace|}}
91+
{{#if (includes targetNamespace this.namespace.accessibleNamespaces)}}
92+
<dd.Interactive
93+
{{on "click" (fn this.switchNamespace targetNamespace)}}
94+
data-test-popup-menu="switch"
95+
>Switch to namespace</dd.Interactive>
96+
{{/if}}
97+
{{/let}}
98+
<dd.Interactive
99+
@color="critical"
100+
{{on "click" (fn (mut this.nsToDelete) list.item)}}
101+
data-test-popup-menu="delete"
102+
>Delete</dd.Interactive>
103+
</Hds::Dropdown>
104+
{{#if (eq this.nsToDelete list.item)}}
105+
<ConfirmModal
106+
@color="critical"
107+
@onClose={{fn (mut this.nsToDelete) null}}
108+
@onConfirm={{fn this.deleteNamespace list.item}}
109+
@confirmTitle="Delete this namespace?"
110+
@confirmMessage="Any engines or mounts in this namespace will also be removed."
111+
/>
112+
{{/if}}
113+
</Item.menu>
114+
</ListItem>
115+
{{else}}
116+
<list.empty>
117+
<Hds::Link::Standalone
118+
@icon="learn-link"
119+
@text="Secure multi-tenancy with namespaces tutorial"
120+
@href={{doc-link "/vault/tutorials/enterprise/namespaces"}}
121+
/>
122+
</list.empty>
123+
{{/if}}
124+
</ListView>
125+
{{else}}
126+
{{#if this.showSetupAlert}}
127+
<Hds::Alert @type="inline" class="top-margin-32" as |A|>
128+
<A.Title>Your current setup is 1 namespace.</A.Title>
129+
<A.Description>Based on your answer about your security policy in the Guided start, no new namespaces are required.</A.Description>
130+
</Hds::Alert>
100131
{{/if}}
101-
</ListView>
132+
133+
<Hds::Card::Container @hasBorder={{true}} class="has-padding-l top-margin-32">
134+
<Hds::ApplicationState @align="left" class="is-marginless" as |A|>
135+
<A.Header
136+
@title="No namespaces yet"
137+
@titleTag="h2"
138+
@icon="skip"
139+
class="align-items-end"
140+
data-test-empty-state-title
141+
/>
142+
<A.Body @text="Your namespaces will be listed here. Add a namespace to get started." />
143+
<A.Footer as |F|>
144+
<F.Button
145+
@color="secondary"
146+
@icon="reload"
147+
@text="Refresh"
148+
{{on "click" this.refreshNamespaceList}}
149+
data-test-button="refresh-namespace-list"
150+
/>
151+
<F.LinkStandalone
152+
@icon="learn-link"
153+
@text="Secure multi-tenancy with namespaces tutorial"
154+
@href={{doc-link "/vault/tutorials/enterprise/namespaces"}}
155+
/>
156+
</A.Footer>
157+
</Hds::ApplicationState>
158+
</Hds::Card::Container>
159+
{{/if}}
160+
102161
{{/if}}
162+
103163
{{else}}
104164
<UpgradePage @title="Namespaces" @minimumEdition="Vault Enterprise Pro" />
105165
{{/if}}

ui/app/components/page/namespaces.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import Component from '@glimmer/component';
1010
import keys from 'core/utils/keys';
1111

1212
import type ApiService from 'vault/services/api';
13+
import type FlagsService from 'vault/services/flags';
1314
import type FlashMessageService from 'vault/services/flash-messages';
1415
import type NamespaceService from 'vault/services/namespace';
1516
import type RouterService from '@ember/routing/router-service';
@@ -45,6 +46,7 @@ interface NamespaceModel {
4546
export default class PageNamespacesComponent extends Component<Args> {
4647
@service declare readonly api: ApiService;
4748
@service declare readonly router: RouterService;
49+
@service declare readonly flags: FlagsService;
4850
@service declare readonly flashMessages: FlashMessageService;
4951
@service declare namespace: NamespaceService;
5052

@@ -53,6 +55,7 @@ export default class PageNamespacesComponent extends Component<Args> {
5355
// browser query param to prevent unnecessary re-renders.
5456
@tracked query;
5557
@tracked nsToDelete = null;
58+
@tracked showSetupAlert = false;
5659
@tracked hasDismissedWizard = false;
5760

5861
wizardId = 'namespace';
@@ -68,6 +71,21 @@ export default class PageNamespacesComponent extends Component<Args> {
6871
}
6972
}
7073

74+
// show the full available namespace path e.g. "root/ns1/child2", "admin/ns1/child2"
75+
get namespacePath() {
76+
if (this.namespace.inRootNamespace) {
77+
return 'root';
78+
}
79+
80+
// For nested namespaces, show "root/" prefix if not HVD managed and no separate user root
81+
if (!this.namespace.userRootNamespace && !this.flags.isHvdManaged) {
82+
return `root/${this.namespace.path}`;
83+
}
84+
85+
// If there is a userRootNamespace or it is HVD managed, then the path alone will suffice
86+
return this.namespace.path;
87+
}
88+
7189
get showWizard() {
7290
// Show when there are no existing namespaces and it is not in a dismissed state
7391
return !this.hasDismissedWizard && !this.args.model.namespaces?.length;
@@ -123,6 +141,11 @@ export default class PageNamespacesComponent extends Component<Args> {
123141
}
124142
}
125143

144+
@action
145+
enterGuidedStart() {
146+
this.hasDismissedWizard = false;
147+
}
148+
126149
@action handlePageChange() {
127150
this.args.onRefresh();
128151
}

ui/app/components/wizard/namespaces/namespace-wizard.hbs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@
1717
<Wizard::Namespaces::Welcome />
1818
</:welcome>
1919
<:exit>
20-
{{#unless (eq this.wizardState.creationMethod this.methods.UI)}}
20+
{{#if this.shouldShowExitButton}}
2121
<Hds::Button @text={{this.exitText}} @color="secondary" {{on "click" this.onDismiss}} />
22-
{{/unless}}
22+
{{/if}}
2323
</:exit>
2424
<:submit>
2525
{{#if (eq this.wizardState.securityPolicyChoice this.policy.FLEXIBLE)}}

ui/app/components/wizard/namespaces/namespace-wizard.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,17 @@ export default class WizardNamespacesWizardComponent extends Component<Args> {
7272
}
7373
}
7474

75+
get isFinalStep() {
76+
return this.currentStep === this.steps.length - 1;
77+
}
78+
79+
get shouldShowExitButton() {
80+
// Show exit button unless we're on the final step with UI creation method
81+
return !(this.wizardState.creationMethod === CreationMethod.UI && this.isFinalStep);
82+
}
83+
7584
get exitText() {
76-
return this.currentStep === this.steps.length - 1 &&
77-
this.wizardState.securityPolicyChoice === SecurityPolicy.STRICT
85+
return this.isFinalStep && this.wizardState.securityPolicyChoice === SecurityPolicy.STRICT
7886
? 'Done & Exit'
7987
: 'Exit';
8088
}

ui/app/components/wizard/namespaces/step-3.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ export default class WizardNamespacesStep3 extends Component<Args> {
7070
icon: 'sidebar',
7171
label: CreationMethod.UI,
7272
description:
73-
'Apply changes immediately. Note: Changes made here may be overwritten if you also use Infrastructure as Code (Terraform).',
73+
'Apply changes immediately. Note: Changes made in the UI will be overwritten by any future updates made via Infrastructure as Code (Terraform).',
7474
},
7575
];
7676
tabOptions = ['API', 'CLI'];

ui/tests/acceptance/access/namespaces/index-test.js

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -74,21 +74,9 @@ module('Acceptance | Enterprise | /access/namespaces', function (hooks) {
7474
const testNS = 'test-create-ns-ui';
7575

7676
// Verify test-create-ns does not exist in the Manage Namespace page
77-
await fillIn(GENERAL.filterInputExplicit, testNS);
78-
await click(GENERAL.button('Search'));
79-
await waitFor(GENERAL.emptyStateTitle, {
80-
timeout: 2000,
81-
timeoutMessage: 'timed out waiting for empty state title to render',
82-
});
83-
assert
84-
.dom(GENERAL.emptyStateTitle)
85-
.hasText(
86-
'No namespaces yet',
87-
'Empty state is displayed when searching for the namespace we have created in the UI but have not refreshed the list yet'
88-
);
8977

9078
// Create a new namespace in the UI
91-
await click(GENERAL.linkTo('create-namespace'));
79+
await click(GENERAL.button('create-namespace'));
9280
await fillIn(GENERAL.inputByAttr('path'), testNS);
9381
await click(GENERAL.submitButton);
9482

@@ -113,11 +101,11 @@ module('Acceptance | Enterprise | /access/namespaces', function (hooks) {
113101
// Setup: Create namespace(s) via the CLI
114102
const testNS = 'asdf';
115103
await runCmd(createNS(testNS), false);
104+
await click(GENERAL.button('refresh-namespace-list'));
116105

117106
// Search for created namespace// Enter search text
118107
await fillIn(GENERAL.filterInputExplicit, testNS);
119108
await click(GENERAL.button('Search'));
120-
await click(GENERAL.button('refresh-namespace-list'));
121109

122110
// Verify the menu options
123111
await waitFor(GENERAL.menuTrigger, {
@@ -135,11 +123,11 @@ module('Acceptance | Enterprise | /access/namespaces', function (hooks) {
135123
// Setup: Create namespace(s) via the CLI
136124
const testNS = 'test-create-ns-switch';
137125
await runCmd(createNS(testNS), false);
126+
await click(GENERAL.button('refresh-namespace-list'));
138127

139128
// Search for created namespace
140129
await fillIn(GENERAL.filterInputExplicit, testNS);
141130
await click(GENERAL.button('Search'));
142-
await click(GENERAL.button('refresh-namespace-list'));
143131

144132
// Switch namespace
145133
await waitFor(GENERAL.menuTrigger);

0 commit comments

Comments
 (0)