-
Notifications
You must be signed in to change notification settings - Fork 4.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
UI: add pagination to new PKI #23193
Changes from all commits
89a09a3
d162fc7
34d8609
4d7afe8
7e35e46
fa00fb7
ff71561
a7db129
511e999
005ce76
ff6af41
cd896c5
089c806
522847e
3792dfe
77acb0b
a4efdbd
196314a
9664ea1
76f4ac1
03799ff
fe82d33
e7e6f83
6d79b30
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
```release-note:improvement | ||
ui: Add pagination to PKI roles, keys, issuers, and certificates list pages | ||
``` |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,68 +3,114 @@ | |
SPDX-License-Identifier: BUSL-1.1 | ||
~}} | ||
|
||
{{#each @issuers as |pkiIssuer idx|}} | ||
<LinkedBlock class="list-item-row" @params={{array "issuers.issuer.details" pkiIssuer.id}} @linkPrefix={{@mountPoint}}> | ||
<div class="level is-mobile"> | ||
<div class="level-left"> | ||
<div data-test-issuer-list={{pkiIssuer.id}}> | ||
<Icon @name="certificate" class="has-text-grey-light" /> | ||
<span class="has-text-weight-semibold is-underline"> | ||
{{pkiIssuer.issuerRef}} | ||
{{#if pkiIssuer.issuerName}} | ||
<span class="tag has-text-grey-dark">{{pkiIssuer.id}}</span> | ||
{{/if}} | ||
</span> | ||
<div class="is-flex-row has-left-margin-l has-top-margin-xs"> | ||
{{#if pkiIssuer.isDefault}} | ||
<span class="tag has-text-grey-dark" data-test-is-default={{idx}}>default issuer</span> | ||
{{/if}} | ||
{{#if (not-eq pkiIssuer.isRoot undefined)}} | ||
<span class="tag has-text-grey-dark" data-test-is-root-tag={{idx}}>{{if | ||
pkiIssuer.isRoot | ||
"root" | ||
"intermediate" | ||
}}</span> | ||
{{/if}} | ||
{{#if pkiIssuer.serialNumber}} | ||
<span class="tag is-transparent has-right-margin-none" data-test-serial-number={{idx}}> | ||
<InfoTooltip> | ||
Serial number | ||
</InfoTooltip> | ||
{{pkiIssuer.serialNumber}} | ||
<PkiPaginatedList @listRoute="issuers.index" @list={{@issuers}}> | ||
<:actions> | ||
<ToolbarLink @route="issuers.import" data-test-generate-issuer="import"> | ||
Import | ||
</ToolbarLink> | ||
<BasicDropdown @class="popup-menu" @horizontalPosition="auto-right" @verticalPosition="below" as |D|> | ||
<D.Trigger | ||
class={{concat "toolbar-link" (if D.isOpen " is-active")}} | ||
@htmlTag="button" | ||
data-test-issuer-generate-dropdown | ||
> | ||
Generate | ||
<Chevron @direction="down" @isButton={{true}} /> | ||
</D.Trigger> | ||
<D.Content @defaultClass="popup-menu-content"> | ||
<nav class="box menu" aria-label="generate options"> | ||
<ul class="menu-list"> | ||
<li class="action"> | ||
<LinkTo @route="issuers.generate-root" {{on "click" (fn this.onLinkClick D)}} data-test-generate-issuer="root"> | ||
Root | ||
</LinkTo> | ||
</li> | ||
<li class="action"> | ||
<LinkTo | ||
@route="issuers.generate-intermediate" | ||
{{on "click" (fn this.onLinkClick D)}} | ||
data-test-generate-issuer="intermediate" | ||
> | ||
Intermediate CSR | ||
</LinkTo> | ||
</li> | ||
</ul> | ||
</nav> | ||
</D.Content> | ||
</BasicDropdown> | ||
</:actions> | ||
<:list as |issuers|> | ||
{{#each issuers as |pkiIssuer idx|}} | ||
<LinkedBlock class="list-item-row" @params={{array "issuers.issuer.details" pkiIssuer.id}} @linkPrefix={{@mountPoint}}> | ||
<div class="level is-mobile"> | ||
<div class="level-left"> | ||
<div data-test-issuer-list={{pkiIssuer.id}}> | ||
<Icon @name="certificate" class="has-text-grey-light" /> | ||
<span class="has-text-weight-semibold is-underline"> | ||
{{pkiIssuer.issuerRef}} | ||
{{#if pkiIssuer.issuerName}} | ||
<span class="tag has-text-grey-dark">{{pkiIssuer.id}}</span> | ||
{{/if}} | ||
</span> | ||
{{/if}} | ||
{{#if pkiIssuer.parsedCertificate.common_name}} | ||
<span class="tag is-transparent has-left-margin-none" data-test-common-name={{idx}}> | ||
<InfoTooltip> | ||
Common name | ||
</InfoTooltip> | ||
{{pkiIssuer.parsedCertificate.common_name}} | ||
</span> | ||
{{/if}} | ||
<div class="is-flex-row has-left-margin-l has-top-margin-xs"> | ||
{{#if pkiIssuer.isDefault}} | ||
<span class="tag has-text-grey-dark" data-test-is-default={{idx}}>default issuer</span> | ||
{{/if}} | ||
{{#if (not-eq pkiIssuer.isRoot undefined)}} | ||
<span class="tag has-text-grey-dark" data-test-is-root-tag={{idx}}>{{if | ||
pkiIssuer.isRoot | ||
"root" | ||
"intermediate" | ||
}}</span> | ||
{{/if}} | ||
{{#if pkiIssuer.serialNumber}} | ||
<span class="tag is-transparent has-right-margin-none" data-test-serial-number={{idx}}> | ||
<InfoTooltip> | ||
Serial number | ||
</InfoTooltip> | ||
{{pkiIssuer.serialNumber}} | ||
</span> | ||
{{/if}} | ||
{{#if pkiIssuer.parsedCertificate.common_name}} | ||
<span class="tag is-transparent has-left-margin-none" data-test-common-name={{idx}}> | ||
<InfoTooltip> | ||
Common name | ||
</InfoTooltip> | ||
{{pkiIssuer.parsedCertificate.common_name}} | ||
</span> | ||
{{/if}} | ||
</div> | ||
</div> | ||
</div> | ||
<div class="level-right is-flex is-paddingless is-marginless"> | ||
<div class="level-item"> | ||
<PopupMenu> | ||
<nav class="menu" aria-label="issuer config options"> | ||
<ul class="menu-list"> | ||
<li data-test-popup-menu-details> | ||
<LinkTo @route="issuers.issuer.details" @model={{pkiIssuer.id}}> | ||
Details | ||
</LinkTo> | ||
</li> | ||
<li> | ||
<LinkTo @route="issuers.issuer.edit" @model={{pkiIssuer.id}}> | ||
Edit | ||
</LinkTo> | ||
</li> | ||
</ul> | ||
</nav> | ||
</PopupMenu> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
<div class="level-right is-flex is-paddingless is-marginless"> | ||
<div class="level-item"> | ||
<PopupMenu> | ||
<nav class="menu" aria-label="issuer config options"> | ||
<ul class="menu-list"> | ||
<li data-test-popup-menu-details> | ||
<LinkTo @route="issuers.issuer.details" @model={{pkiIssuer.id}}> | ||
Details | ||
</LinkTo> | ||
</li> | ||
<li> | ||
<LinkTo @route="issuers.issuer.edit" @model={{pkiIssuer.id}}> | ||
Edit | ||
</LinkTo> | ||
</li> | ||
</ul> | ||
</nav> | ||
</PopupMenu> | ||
</div> | ||
</div> | ||
</div> | ||
</LinkedBlock> | ||
{{/each}} | ||
</LinkedBlock> | ||
{{/each}} | ||
</:list> | ||
<:empty> | ||
<EmptyState @title="PKI not configured" @message={{this.notConfiguredMessage}}> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Moved the empty state into here from the route template, so I needed to add a backing class for |
||
<LinkTo @route="configuration.create"> | ||
Configure PKI | ||
</LinkTo> | ||
</EmptyState> | ||
</:empty> | ||
</PkiPaginatedList> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
/** | ||
* Copyright (c) HashiCorp, Inc. | ||
* SPDX-License-Identifier: BUSL-1.1 | ||
*/ | ||
|
||
import { action } from '@ember/object'; | ||
import { next } from '@ember/runloop'; | ||
import Component from '@glimmer/component'; | ||
import { PKI_DEFAULT_EMPTY_STATE_MSG } from 'pki/routes/overview'; | ||
import type PkiIssuerModel from 'vault/models/pki/issuer'; | ||
|
||
interface BasicDropdown { | ||
actions: { | ||
close: CallableFunction; | ||
}; | ||
} | ||
interface Args { | ||
issuers: PkiIssuerModel[]; | ||
mountPoint: string; | ||
} | ||
|
||
export default class PkiIssuerList extends Component<Args> { | ||
notConfiguredMessage = PKI_DEFAULT_EMPTY_STATE_MSG; | ||
|
||
// To prevent production build bug of passing D.actions to on "click": https://github.com/hashicorp/vault/pull/16983 | ||
@action onLinkClick(D: BasicDropdown) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Moved from controller |
||
next(() => D.actions.close()); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,8 +3,8 @@ | |
SPDX-License-Identifier: BUSL-1.1 | ||
~}} | ||
|
||
<Toolbar> | ||
<ToolbarActions> | ||
<PkiPaginatedList @listRoute="keys.index" @list={{@keyModels}} @hasConfig={{@hasConfig}}> | ||
<:actions> | ||
{{#if @canImportKey}} | ||
<ToolbarLink @route="keys.import" @type="download" data-test-pki-key-import> | ||
Import | ||
|
@@ -15,62 +15,73 @@ | |
Generate | ||
</ToolbarLink> | ||
{{/if}} | ||
</ToolbarActions> | ||
</Toolbar> | ||
<p class="has-padding">Below is information about the private keys used by the issuers to sign certificates. While | ||
certificates represent a public assertion of an identity, private keys represent the private part of that identity, a | ||
secret used to prove who they are and who they trust.</p> | ||
|
||
{{#if @keyModels.length}} | ||
{{#each @keyModels as |pkiKey|}} | ||
<LinkedBlock class="list-item-row" @params={{array "keys.key.details" pkiKey.keyId}} @linkPrefix={{@mountPoint}}> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. copypasta'd into <:list> |
||
<div class="level is-mobile"> | ||
<div class="level-left"> | ||
<div> | ||
<Icon @name="certificate" class="has-text-grey-light" /> | ||
<span class="has-text-weight-semibold is-underline" data-test-key={{if pkiKey.keyName "name" "id"}}> | ||
{{or pkiKey.keyName pkiKey.id}} | ||
</span> | ||
<div class="is-flex-row has-left-margin-l has-top-margin-xs"> | ||
{{#if pkiKey.keyName}} | ||
<span class="tag has-text-grey-dark" data-test-key="id">{{pkiKey.id}}</span> | ||
{{/if}} | ||
</:actions> | ||
<:description> | ||
<p class="has-padding">Below is information about the private keys used by the issuers to sign certificates. While | ||
certificates represent a public assertion of an identity, private keys represent the private part of that identity, a | ||
secret used to prove who they are and who they trust.</p> | ||
</:description> | ||
<:list as |keys|> | ||
{{#each keys as |pkiKey|}} | ||
<LinkedBlock class="list-item-row" @params={{array "keys.key.details" pkiKey.keyId}} @linkPrefix={{@mountPoint}}> | ||
<div class="level is-mobile"> | ||
<div class="level-left"> | ||
<div> | ||
<Icon @name="certificate" class="has-text-grey-light" /> | ||
<span class="has-text-weight-semibold is-underline" data-test-key={{if pkiKey.keyName "name" "id"}}> | ||
{{or pkiKey.keyName pkiKey.id}} | ||
</span> | ||
<div class="is-flex-row has-left-margin-l has-top-margin-xs"> | ||
{{#if pkiKey.keyName}} | ||
<span class="tag has-text-grey-dark" data-test-key="id">{{pkiKey.id}}</span> | ||
{{/if}} | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
<div class="level-right is-flex is-paddingless is-marginless"> | ||
<div class="level-item"> | ||
<PopupMenu> | ||
<nav class="menu"> | ||
<ul class="menu-list"> | ||
<li> | ||
<LinkTo | ||
@route="keys.key.details" | ||
@model={{pkiKey.keyId}} | ||
@disabled={{not @canRead}} | ||
data-test-key-menu-link="details" | ||
> | ||
Details | ||
</LinkTo> | ||
</li> | ||
<li> | ||
<LinkTo | ||
@route="keys.key.edit" | ||
@model={{pkiKey.keyId}} | ||
@disabled={{not @canEdit}} | ||
data-test-key-menu-link="edit" | ||
> | ||
Edit | ||
</LinkTo> | ||
</li> | ||
</ul> | ||
</nav> | ||
</PopupMenu> | ||
<div class="level-right is-flex is-paddingless is-marginless"> | ||
<div class="level-item"> | ||
<PopupMenu> | ||
<nav class="menu"> | ||
<ul class="menu-list"> | ||
<li> | ||
<LinkTo | ||
@route="keys.key.details" | ||
@model={{pkiKey.keyId}} | ||
@disabled={{not @canRead}} | ||
data-test-key-menu-link="details" | ||
> | ||
Details | ||
</LinkTo> | ||
</li> | ||
<li> | ||
<LinkTo | ||
@route="keys.key.edit" | ||
@model={{pkiKey.keyId}} | ||
@disabled={{not @canEdit}} | ||
data-test-key-menu-link="edit" | ||
> | ||
Edit | ||
</LinkTo> | ||
</li> | ||
</ul> | ||
</nav> | ||
</PopupMenu> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
</LinkedBlock> | ||
{{/each}} | ||
{{else}} | ||
<EmptyState @title="No keys yet" @message="There are no keys in this PKI mount. You can generate or create one." /> | ||
{{/if}} | ||
</LinkedBlock> | ||
{{/each}} | ||
</:list> | ||
|
||
<:empty> | ||
<EmptyState @title="No keys yet" @message="There are no keys in this PKI mount. You can generate or create one." /> | ||
</:empty> | ||
|
||
<:configure> | ||
<EmptyState @title="PKI not configured" @message={{this.notConfiguredMessage}}> | ||
<LinkTo @route="configuration.create"> | ||
Configure PKI | ||
</LinkTo> | ||
</EmptyState> | ||
</:configure> | ||
</PkiPaginatedList> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moved this inside the <:list> section, otherwise is exactly the same