mirror of https://github.com/status-im/consul.git
ui: Move control of login modal to use JS rather than HTML (label/id) (#9883)
* Add before and after skip links portals * Move EmptyState and ErrorState to use a @login action/function * Move page title setting to the Route component * Add Routes and Outlets everywhere, and use those to access login modal * Add some aria-labels to the modals * Docs * Remove the label/input now we no longer need it, fixup pageobject * Add basic modal docs * Switch out old toggle names for ids * Wrap nspace Route template in a Route component * type > class
This commit is contained in:
parent
e494313e7b
commit
e4e85a8f83
|
@ -5,6 +5,9 @@
|
||||||
</h1>
|
</h1>
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
<BlockSlot @name="content">
|
<BlockSlot @name="content">
|
||||||
<ErrorState @error={{@error}} @allowLogin={{eq @error.status "403"}} />
|
<ErrorState
|
||||||
|
@error={{@error}}
|
||||||
|
@login={{if (eq @error.status "403") @login}}
|
||||||
|
/>
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
</AppView>
|
</AppView>
|
||||||
|
|
|
@ -108,7 +108,7 @@
|
||||||
@error={{hash
|
@error={{hash
|
||||||
status='403'
|
status='403'
|
||||||
}}
|
}}
|
||||||
@allowLogin={{true}}
|
@login={{login}}
|
||||||
/>
|
/>
|
||||||
{{else}}
|
{{else}}
|
||||||
<YieldSlot @name="content">{{yield}}</YieldSlot>
|
<YieldSlot @name="content">{{yield}}</YieldSlot>
|
||||||
|
|
|
@ -6,12 +6,15 @@
|
||||||
class="app"
|
class="app"
|
||||||
...attributes
|
...attributes
|
||||||
>
|
>
|
||||||
<ModalLayer />
|
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="skip-links"
|
class="skip-links"
|
||||||
{{on "click" this.focus}}
|
{{on "click" this.focus}}
|
||||||
>
|
>
|
||||||
|
<PortalTarget
|
||||||
|
@name="app-before-skip-links"
|
||||||
|
@mutiple={{true}}
|
||||||
|
></PortalTarget>
|
||||||
<a href={{concat '#' exported.main}}>{{t 'components.app.skip_to_content'}}</a>
|
<a href={{concat '#' exported.main}}>{{t 'components.app.skip_to_content'}}</a>
|
||||||
{{!--
|
{{!--
|
||||||
In order to add further skip links from within other templates use:
|
In order to add further skip links from within other templates use:
|
||||||
|
@ -21,11 +24,13 @@
|
||||||
from within your template
|
from within your template
|
||||||
--}}
|
--}}
|
||||||
<PortalTarget
|
<PortalTarget
|
||||||
@name="app-skip-links"
|
@name="app-after-skip-links"
|
||||||
@mutiple={{true}}
|
@mutiple={{true}}
|
||||||
></PortalTarget>
|
></PortalTarget>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<ModalLayer />
|
||||||
|
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
id={{concat guid "-main-nav-toggle"}}
|
id={{concat guid "-main-nav-toggle"}}
|
||||||
|
|
|
@ -193,6 +193,9 @@
|
||||||
<ModalDialog
|
<ModalDialog
|
||||||
class="consul-intention-permission-modal"
|
class="consul-intention-permission-modal"
|
||||||
@onclose={{action (mut permission) undefined}}
|
@onclose={{action (mut permission) undefined}}
|
||||||
|
@aria={{hash
|
||||||
|
label="Edit Permission"
|
||||||
|
}}
|
||||||
as |modal|>
|
as |modal|>
|
||||||
<Ref @target={{this}} @name="modal" @value={{modal}} />
|
<Ref @target={{this}} @name="modal" @value={{modal}} />
|
||||||
<BlockSlot @name="header">
|
<BlockSlot @name="header">
|
||||||
|
|
|
@ -38,6 +38,9 @@ as |api|>
|
||||||
<ModalDialog
|
<ModalDialog
|
||||||
class="consul-intention-action-warn-modal warning"
|
class="consul-intention-action-warn-modal warning"
|
||||||
data-test-action-warning
|
data-test-action-warning
|
||||||
|
@aria={{hash
|
||||||
|
label=(concat "Set intention to " newAction)
|
||||||
|
}}
|
||||||
as |modal|>
|
as |modal|>
|
||||||
<Ref @target={{this}} @name="modal" @value={{modal}} />
|
<Ref @target={{this}} @name="modal" @value={{modal}} />
|
||||||
<BlockSlot @name="header">
|
<BlockSlot @name="header">
|
||||||
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
---
|
||||||
|
class: ember
|
||||||
|
---
|
||||||
|
# EmptyState
|
||||||
|
|
||||||
|
Consul UIs default 'empty state' used for when we retrive an empty result set,
|
||||||
|
whether that set is successful or erroneous. This is mainly used via the
|
||||||
|
`ErrorState` component, so also consider using that directly instead of this
|
||||||
|
component if dealing with errors.
|
||||||
|
|
||||||
|
## Arguments
|
||||||
|
|
||||||
|
| Argument | Type | Default | Description |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `login` | `Function` | `undefined` | A login action to call when the login button is pressed (if not provided no login button will be shown |
|
||||||
|
|
||||||
|
Icons are controlled via a `status-xxx` class. `xxx` should be some sort of
|
||||||
|
3 digit error code, special icons are used for `404` and `403`, otherwise a
|
||||||
|
generic icon will be used. To add any further special icons please add to the
|
||||||
|
component's `skin` file.
|
||||||
|
|
||||||
|
If the `@login` attribute is provided, a button will be shown directly
|
||||||
|
underneath the body text clicking on which will fire the provided `@login`
|
||||||
|
function.
|
||||||
|
|
||||||
|
```hbs preview-template
|
||||||
|
<EmptyState
|
||||||
|
class="status-404"
|
||||||
|
@login={{noop}}
|
||||||
|
>
|
||||||
|
<BlockSlot @name="header">
|
||||||
|
<h2>
|
||||||
|
Header
|
||||||
|
</h2>
|
||||||
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="subheader">
|
||||||
|
<h3>
|
||||||
|
Subheader
|
||||||
|
</h3>
|
||||||
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="body">
|
||||||
|
<p>
|
||||||
|
Body text
|
||||||
|
</p>
|
||||||
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="actions">
|
||||||
|
<li class="docs-link">
|
||||||
|
<a
|
||||||
|
href="{{env 'CONSUL_DOCS_URL'}}/agent/kv"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
Documentation on K/V
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="learn-link">
|
||||||
|
<a
|
||||||
|
href="{{env 'CONSUL_DOCS_LEARN_URL'}}/consul/getting-started/kv"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
Read the guide
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</BlockSlot>
|
||||||
|
</EmptyState>
|
||||||
|
```
|
||||||
|
|
||||||
|
The component has four slots for specifying header, subheader, body and
|
||||||
|
'actions', although the component will show a minimal empty slot if only the
|
||||||
|
body slot is specified:
|
||||||
|
|
||||||
|
```hbs preview-template
|
||||||
|
<EmptyState>
|
||||||
|
<BlockSlot @name="body">
|
||||||
|
<p>
|
||||||
|
Minimal text
|
||||||
|
</p>
|
||||||
|
</BlockSlot>
|
||||||
|
</EmptyState>
|
||||||
|
```
|
|
@ -16,8 +16,11 @@
|
||||||
{{#yield-slot name="body"}}
|
{{#yield-slot name="body"}}
|
||||||
<div>
|
<div>
|
||||||
{{yield}}
|
{{yield}}
|
||||||
{{#if (and (env 'CONSUL_ACLS_ENABLED') allowLogin)}}
|
{{#if login}}
|
||||||
<label for="login-toggle" data-test-empty-state-login>
|
<Action
|
||||||
|
data-test-empty-state-login
|
||||||
|
{{on "click" login}}
|
||||||
|
>
|
||||||
<DataSource
|
<DataSource
|
||||||
@src="settings://consul:token"
|
@src="settings://consul:token"
|
||||||
@onchange={{action (mut token) value="data"}}
|
@onchange={{action (mut token) value="data"}}
|
||||||
|
@ -27,7 +30,7 @@
|
||||||
{{else}}
|
{{else}}
|
||||||
Log in
|
Log in
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</label>
|
</Action>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
{{/yield-slot}}
|
{{/yield-slot}}
|
||||||
|
|
|
@ -13,6 +13,6 @@
|
||||||
%empty-state > ul > li > label > button {
|
%empty-state > ul > li > label > button {
|
||||||
@extend %empty-state-anchor;
|
@extend %empty-state-anchor;
|
||||||
}
|
}
|
||||||
%empty-state label {
|
%empty-state div > button {
|
||||||
@extend %primary-button;
|
@extend %primary-button;
|
||||||
}
|
}
|
|
@ -15,8 +15,9 @@
|
||||||
width: 370px;
|
width: 370px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
}
|
}
|
||||||
%empty-state label {
|
%empty-state button {
|
||||||
margin: 0 auto !important;
|
margin: 0 auto;
|
||||||
|
display: inline;
|
||||||
}
|
}
|
||||||
%empty-state-header {
|
%empty-state-header {
|
||||||
margin-bottom: -3px;
|
margin-bottom: -3px;
|
|
@ -0,0 +1,34 @@
|
||||||
|
# ErrorState
|
||||||
|
|
||||||
|
Consul UIs default 'error state' used when an error is returned form the
|
||||||
|
backend. This component used `EmptyState` internally, so please refer to that
|
||||||
|
for more details.
|
||||||
|
|
||||||
|
Using this component for all of our errors means we can show a consistent
|
||||||
|
error page for generic errors.
|
||||||
|
|
||||||
|
This component show slighltly different visuals and copy depending on the
|
||||||
|
`status` of the error (the status is generally a HTTP error code)
|
||||||
|
|
||||||
|
## Arguments
|
||||||
|
|
||||||
|
| Argument | Type | Default | Description |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `login` | `Function` | `undefined` | A login action to call when the login button is pressed (if not provided no login button will be shown |
|
||||||
|
| `error` | `Object` | `undefined` | 'Consul UI error shaped' JSON `{status: String, message: String, detail: String}` |
|
||||||
|
|
||||||
|
```hbs preview-template
|
||||||
|
<ErrorState
|
||||||
|
@error={{hash status='403'}}
|
||||||
|
/>
|
||||||
|
```
|
||||||
|
|
||||||
|
As with `EmptyState` you can optionally chose to show a login button using the
|
||||||
|
`@login` argument.
|
||||||
|
|
||||||
|
```hbs preview-template
|
||||||
|
<ErrorState
|
||||||
|
@error={{hash status='403'}}
|
||||||
|
@login={{noop}}
|
||||||
|
/>
|
||||||
|
```
|
|
@ -1,38 +0,0 @@
|
||||||
import { Meta, Story, Canvas } from '@storybook/addon-docs/blocks';
|
|
||||||
import { hbs } from 'ember-cli-htmlbars';
|
|
||||||
|
|
||||||
<Meta title="Components/ErrorState" />
|
|
||||||
|
|
||||||
# ErrorState
|
|
||||||
|
|
||||||
<Canvas>
|
|
||||||
<Story
|
|
||||||
name="Basic"
|
|
||||||
argTypes={{
|
|
||||||
allowLogin: {
|
|
||||||
defaultValue: true,
|
|
||||||
control: {
|
|
||||||
type: 'boolean'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
status: {
|
|
||||||
defaultValue: '403',
|
|
||||||
control: {
|
|
||||||
type: 'select',
|
|
||||||
options: [
|
|
||||||
'404',
|
|
||||||
'403',
|
|
||||||
'500'
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>{(args) => ({
|
|
||||||
template: hbs`<ErrorState
|
|
||||||
@allowLogin={{allowLogin}}
|
|
||||||
@error={{hash status=status}}
|
|
||||||
/>`,
|
|
||||||
context: args
|
|
||||||
})}
|
|
||||||
</Story>
|
|
||||||
</Canvas>
|
|
|
@ -1,7 +1,7 @@
|
||||||
{{#if (not-eq @error.status "403")}}
|
{{#if (not-eq @error.status "403")}}
|
||||||
<EmptyState
|
<EmptyState
|
||||||
class={{concat "status-" @error.status}}
|
class={{concat "status-" @error.status}}
|
||||||
@allowLogin={{@allowLogin}}
|
@login={{@login}}
|
||||||
>
|
>
|
||||||
<BlockSlot @name="header">
|
<BlockSlot @name="header">
|
||||||
<h2>{{or @error.message "Consul returned an error"}}</h2>
|
<h2>{{or @error.message "Consul returned an error"}}</h2>
|
||||||
|
@ -34,7 +34,7 @@
|
||||||
{{else}}
|
{{else}}
|
||||||
<EmptyState
|
<EmptyState
|
||||||
class="status-403"
|
class="status-403"
|
||||||
@allowLogin={{@allowLogin}}
|
@login={{@login}}
|
||||||
>
|
>
|
||||||
<BlockSlot @name="header">
|
<BlockSlot @name="header">
|
||||||
<h2 data-test-status={{@error.status}}>You are not authorized</h2>
|
<h2 data-test-status={{@error.status}}>You are not authorized</h2>
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
import Component from '@ember/component';
|
|
||||||
|
|
||||||
export default Component.extend({
|
|
||||||
tagName: '',
|
|
||||||
});
|
|
|
@ -218,14 +218,28 @@
|
||||||
>
|
>
|
||||||
{{#let components.AuthForm components.AuthProfile as |AuthForm AuthProfile|}}
|
{{#let components.AuthForm components.AuthProfile as |AuthForm AuthProfile|}}
|
||||||
<BlockSlot @name="unauthorized">
|
<BlockSlot @name="unauthorized">
|
||||||
<label
|
<Portal @target="app-before-skip-links">
|
||||||
tabindex="0"
|
<button
|
||||||
{{on 'keypress' this.keypressClick}}
|
type="button"
|
||||||
|
{{on "click" (optional this.modal.open)}}
|
||||||
|
>
|
||||||
|
Login
|
||||||
|
</button>
|
||||||
|
</Portal>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
{{on "click" (optional this.modal.open)}}
|
{{on "click" (optional this.modal.open)}}
|
||||||
>
|
>
|
||||||
<span>Log in</span>
|
Log in
|
||||||
</label>
|
</button>
|
||||||
<ModalDialog @name="login-toggle" @onclose={{this.close}} @onopen={{this.open}} as |modal|>
|
<ModalDialog
|
||||||
|
@name="login-toggle"
|
||||||
|
@onclose={{this.close}}
|
||||||
|
@onopen={{this.open}}
|
||||||
|
@aria={{hash
|
||||||
|
label="Log in to Consul"
|
||||||
|
}}
|
||||||
|
as |modal|>
|
||||||
<Ref @target={{this}} @name="modal" @value={{modal}} />
|
<Ref @target={{this}} @name="modal" @value={{modal}} />
|
||||||
<BlockSlot @name="header">
|
<BlockSlot @name="header">
|
||||||
<h2>Log in to Consul</h2>
|
<h2>Log in to Consul</h2>
|
||||||
|
@ -245,7 +259,14 @@
|
||||||
</ModalDialog>
|
</ModalDialog>
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
<BlockSlot @name="authorized">
|
<BlockSlot @name="authorized">
|
||||||
<ModalDialog @name="login-toggle" @onclose={{this.close}} @onopen={{this.open}} as |modal|>
|
<ModalDialog
|
||||||
|
@name="login-toggle"
|
||||||
|
@onclose={{this.close}}
|
||||||
|
@onopen={{this.open}}
|
||||||
|
@aria={{hash
|
||||||
|
label="Log in with a different token"
|
||||||
|
}}
|
||||||
|
as |modal|>
|
||||||
<Ref @target={{this}} @name="modal" @value={{modal}} />
|
<Ref @target={{this}} @name="modal" @value={{modal}} />
|
||||||
<BlockSlot @name="header">
|
<BlockSlot @name="header">
|
||||||
<h2>Log in with a different token</h2>
|
<h2>Log in with a different token</h2>
|
||||||
|
@ -261,6 +282,14 @@
|
||||||
</button>
|
</button>
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
</ModalDialog>
|
</ModalDialog>
|
||||||
|
<Portal @target="app-before-skip-links">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
{{on "click" (optional authDialog.logout)}}
|
||||||
|
>
|
||||||
|
Logout
|
||||||
|
</button>
|
||||||
|
</Portal>
|
||||||
<PopoverMenu @position="right" as |components api|>
|
<PopoverMenu @position="right" as |components api|>
|
||||||
<BlockSlot @name="trigger">
|
<BlockSlot @name="trigger">
|
||||||
Logout
|
Logout
|
||||||
|
@ -295,7 +324,7 @@
|
||||||
|
|
||||||
<:main>
|
<:main>
|
||||||
{{yield (hash
|
{{yield (hash
|
||||||
modal=this.modal
|
login=(if (env 'CONSUL_ACLS_ENABLED') this.modal (hash open=undefined))
|
||||||
)}}
|
)}}
|
||||||
</:main>
|
</:main>
|
||||||
|
|
||||||
|
|
|
@ -42,7 +42,7 @@ export default (collection, clickable, attribute, is, authForm, emptyState) => s
|
||||||
status: attribute('data-test-status', '[data-test-status]'),
|
status: attribute('data-test-status', '[data-test-status]'),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
page.navigation.login = clickable('[data-test-main-nav-auth] label');
|
page.navigation.login = clickable('[data-test-main-nav-auth] button');
|
||||||
page.navigation.dc = clickable('[data-test-datacenter-menu] button');
|
page.navigation.dc = clickable('[data-test-datacenter-menu] button');
|
||||||
page.navigation.nspace = clickable('[data-test-nspace-menu] button');
|
page.navigation.nspace = clickable('[data-test-nspace-menu] button');
|
||||||
page.navigation.manageNspaces = clickable('[data-test-main-nav-nspaces] a');
|
page.navigation.manageNspaces = clickable('[data-test-main-nav-nspaces] a');
|
||||||
|
|
|
@ -4,13 +4,13 @@
|
||||||
/* things that should look like nav buttons */
|
/* things that should look like nav buttons */
|
||||||
%main-nav-horizontal > ul > li > a,
|
%main-nav-horizontal > ul > li > a,
|
||||||
%main-nav-horizontal > ul > li > span,
|
%main-nav-horizontal > ul > li > span,
|
||||||
%main-nav-horizontal > ul > li > label,
|
%main-nav-horizontal > ul > li > button,
|
||||||
%main-nav-horizontal > ul > li > .popover-menu > label > button {
|
%main-nav-horizontal > ul > li > .popover-menu > label > button {
|
||||||
@extend %main-nav-horizontal-action;
|
@extend %main-nav-horizontal-action;
|
||||||
}
|
}
|
||||||
%main-nav-horizontal .popover-menu [type='checkbox']:checked + label > *,
|
%main-nav-horizontal .popover-menu [type='checkbox']:checked + label > *,
|
||||||
%main-nav-horizontal > ul > li.is-active > a,
|
%main-nav-horizontal > ul > li.is-active > a,
|
||||||
%main-nav-horizontal > ul > li.is-active > label > * {
|
%main-nav-horizontal > ul > li.is-active > button {
|
||||||
@extend %main-nav-horizontal-action-active;
|
@extend %main-nav-horizontal-action-active;
|
||||||
}
|
}
|
||||||
/* Whilst we want spans to look the same as actions */
|
/* Whilst we want spans to look the same as actions */
|
||||||
|
|
|
@ -0,0 +1,69 @@
|
||||||
|
---
|
||||||
|
class: ember
|
||||||
|
---
|
||||||
|
# ModalDialog
|
||||||
|
|
||||||
|
## Arguments
|
||||||
|
|
||||||
|
| Argument | Type | Default | Description |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `onopen` | `Function` | `undefined` | A function to call when the modal has opened |
|
||||||
|
| `onclose` | `Function` | `undefined` | A function to call when the modal has closed |
|
||||||
|
| `aria` | `Object` | `undefined` | A `hash` of aria properties used in the component, currently only label is supported |
|
||||||
|
|
||||||
|
## Exports
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `open` | `Function` | Opens the modal dialog |
|
||||||
|
| `close` | `Function` | Closes the modal dialog |
|
||||||
|
|
||||||
|
Works in tandem with `<ModalLayer />` to render modals. First of all ensure
|
||||||
|
you have a modal layer on the page (it doesn't have to be in the same
|
||||||
|
template)
|
||||||
|
|
||||||
|
```hbs
|
||||||
|
<ModalLayer />
|
||||||
|
```
|
||||||
|
|
||||||
|
Then all modals will be rendered into the `<ModalLayer />` for example:
|
||||||
|
|
||||||
|
```hbs preview-template
|
||||||
|
<ModalDialog
|
||||||
|
@onclose={{noop}}
|
||||||
|
@onopen={{noop}}
|
||||||
|
@aria={{hash
|
||||||
|
label="Screenread name of the modal"
|
||||||
|
}}
|
||||||
|
as |modal|>
|
||||||
|
|
||||||
|
<!-- Save a reference to the modal component so we can call its methods -->
|
||||||
|
{{did-insert (set this 'modal' modal)}}
|
||||||
|
|
||||||
|
<BlockSlot @name="header">
|
||||||
|
<h2>
|
||||||
|
Modal Header
|
||||||
|
</h2>
|
||||||
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="body">
|
||||||
|
<p>
|
||||||
|
Modal body
|
||||||
|
</p>
|
||||||
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="actions">
|
||||||
|
<button type="button"
|
||||||
|
{{on "click" modal.close}}
|
||||||
|
>
|
||||||
|
Close modal
|
||||||
|
</button>
|
||||||
|
</BlockSlot>
|
||||||
|
</ModalDialog>
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
{{on 'click' (optional this.modal.open)}}
|
||||||
|
>
|
||||||
|
Open Modal
|
||||||
|
</button>
|
||||||
|
|
||||||
|
```
|
|
@ -1,56 +1,59 @@
|
||||||
<Portal @target="modal">
|
{{#let (hash
|
||||||
{{yield}}
|
labelledby=(unique-id)
|
||||||
<input
|
) as |aria|}}
|
||||||
id={{@name}}
|
<Portal @target="modal">
|
||||||
type="checkbox"
|
{{yield}}
|
||||||
{{on 'change' (action 'change')}}
|
|
||||||
/>
|
|
||||||
<div
|
|
||||||
class="modal-dialog"
|
|
||||||
aria-hidden="true"
|
|
||||||
...attributes
|
|
||||||
{{did-insert (action "connect")}}
|
|
||||||
{{will-destroy (action "disconnect")}}
|
|
||||||
>
|
|
||||||
<div tabindex="-1" data-a11y-dialog-hide></div>
|
|
||||||
<div
|
<div
|
||||||
class="modal-dialog-modal"
|
class="modal-dialog"
|
||||||
role="dialog"
|
aria-hidden="true"
|
||||||
|
...attributes
|
||||||
|
{{did-insert (action "connect")}}
|
||||||
|
{{will-destroy (action "disconnect")}}
|
||||||
>
|
>
|
||||||
|
<div tabindex="-1" data-a11y-dialog-hide></div>
|
||||||
<div
|
<div
|
||||||
role="document"
|
class="modal-dialog-modal"
|
||||||
|
role="dialog"
|
||||||
|
aria-label={{@aria.label}}
|
||||||
>
|
>
|
||||||
<header class="modal-dialog-header">
|
<div
|
||||||
<button
|
role="document"
|
||||||
type="button"
|
>
|
||||||
data-a11y-dialog-hide
|
<header class="modal-dialog-header">
|
||||||
aria-label="Close dialog"
|
<button
|
||||||
>
|
type="button"
|
||||||
</button>
|
data-a11y-dialog-hide
|
||||||
<YieldSlot @name="header">
|
aria-label="Close dialog"
|
||||||
{{yield (hash
|
>
|
||||||
open=(action "open")
|
</button>
|
||||||
close=(action "close")
|
<YieldSlot @name="header">
|
||||||
)}}
|
{{yield (hash
|
||||||
</YieldSlot>
|
open=(action "open")
|
||||||
</header>
|
close=(action "close")
|
||||||
<div class="modal-dialog-body">
|
aria=aria
|
||||||
<YieldSlot @name="body">
|
)}}
|
||||||
{{yield (hash
|
</YieldSlot>
|
||||||
open=(action "open")
|
</header>
|
||||||
close=(action "close")
|
<div class="modal-dialog-body">
|
||||||
)}}
|
<YieldSlot @name="body">
|
||||||
</YieldSlot>
|
{{yield (hash
|
||||||
|
open=(action "open")
|
||||||
|
close=(action "close")
|
||||||
|
aria=aria
|
||||||
|
)}}
|
||||||
|
</YieldSlot>
|
||||||
|
</div>
|
||||||
|
<footer class="modal-dialog-footer">
|
||||||
|
<YieldSlot @name="actions" @params={{block-params (action "close")}}>
|
||||||
|
{{yield (hash
|
||||||
|
open=(action "open")
|
||||||
|
close=(action "close")
|
||||||
|
aria=aria
|
||||||
|
)}}
|
||||||
|
</YieldSlot>
|
||||||
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
<footer class="modal-dialog-footer">
|
|
||||||
<YieldSlot @name="actions" @params={{block-params (action "close")}}>
|
|
||||||
{{yield (hash
|
|
||||||
open=(action "open")
|
|
||||||
close=(action "close")
|
|
||||||
)}}
|
|
||||||
</YieldSlot>
|
|
||||||
</footer>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Portal>
|
||||||
</Portal>
|
{{/let}}
|
|
@ -21,8 +21,5 @@ export default Component.extend(Slotted, {
|
||||||
close: function() {
|
close: function() {
|
||||||
this.dialog.hide();
|
this.dialog.hide();
|
||||||
},
|
},
|
||||||
change: function(e) {
|
|
||||||
this.actions.open.call(this);
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
# ModalLayer
|
||||||
|
|
||||||
|
A component to give you control over where `<ModalDialog />` components are
|
||||||
|
rendered. Please see `<ModalDialog />` for more details.
|
||||||
|
|
||||||
|
```hbs
|
||||||
|
<ModalLayer />
|
||||||
|
```
|
|
@ -0,0 +1,43 @@
|
||||||
|
# Outlet
|
||||||
|
|
||||||
|
The `Outlet` component should be used to wrap *every* ember `{{outlet}}`. It
|
||||||
|
provides/will provide functionality (along with the `<Route />` component)
|
||||||
|
for setting and announcing the title of the page, passing data down through
|
||||||
|
the route/template hierarchy, automatic orchestration of nested routing and
|
||||||
|
visual animating/transitioning between routes.
|
||||||
|
|
||||||
|
## Arguments
|
||||||
|
|
||||||
|
| Argument | Type | Default | Description |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `name` | `String` | `undefined` | The name of the route in ember routeName format e.g. `dc.services.index`. This is generally the `routeName` variable that is available to you in all Consul UI route/page level templates.|
|
||||||
|
| `model` | `Object` | `undefined` | Arbitrary hash of data to pass down to the child route (available in the `<Route as |route|>` export). |
|
||||||
|
|
||||||
|
|
||||||
|
```hbs
|
||||||
|
<Outlet
|
||||||
|
@name={{routeName}}
|
||||||
|
@model={{hash
|
||||||
|
dc=(hash
|
||||||
|
Name="dc-1"
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{{outlet}}
|
||||||
|
</Outlet>
|
||||||
|
```
|
||||||
|
|
||||||
|
Currently, using the `<Outlet />` component means that every single page/route
|
||||||
|
template is wrapped in a HTML `<section>` element. This `<section>` element
|
||||||
|
has various data attributes attached to indiciate the loading state of the
|
||||||
|
outlet. These can be used to specifically target every individual outlet via
|
||||||
|
CSS.
|
||||||
|
|
||||||
|
## Attributes
|
||||||
|
|
||||||
|
| Data Attribute | Description |
|
||||||
|
| --- | --- |
|
||||||
|
| `data-outlet` | The name of this outlet in ember routeName format e.g. `dc.services.index` |
|
||||||
|
| `data-route` | The name of the current child route of this outlet in ember routeName format e.g. `dc.services.show` |
|
||||||
|
| `data-state` | The current state of this outlet, `idle` or `loading` |
|
||||||
|
| `data-transition` | A combination of `idle` and `loading` states |
|
|
@ -1,5 +1,6 @@
|
||||||
{{yield}}
|
{{yield}}
|
||||||
<fieldset
|
<fieldset
|
||||||
|
class="policy-form"
|
||||||
disabled={{if (not (can "write policy" item=item)) "disabled"}}
|
disabled={{if (not (can "write policy" item=item)) "disabled"}}
|
||||||
...attributes
|
...attributes
|
||||||
>
|
>
|
||||||
|
|
|
@ -27,8 +27,11 @@
|
||||||
{{!the modal has to go here so that if you provide a slot to trigger it doesn't get rendered}}
|
{{!the modal has to go here so that if you provide a slot to trigger it doesn't get rendered}}
|
||||||
<ModalDialog
|
<ModalDialog
|
||||||
data-test-policy-form
|
data-test-policy-form
|
||||||
|
id="new-policy"
|
||||||
@onopen={{action "open"}}
|
@onopen={{action "open"}}
|
||||||
@name="new-policy-toggle"
|
@aria={{hash
|
||||||
|
label='New Policy'
|
||||||
|
}}
|
||||||
as |modal|>
|
as |modal|>
|
||||||
<Ref @target={{this}} @name="modal" @value={{modal}} />
|
<Ref @target={{this}} @name="modal" @value={{modal}} />
|
||||||
<BlockSlot @name="header">
|
<BlockSlot @name="header">
|
||||||
|
|
|
@ -5,7 +5,7 @@ export default (clickable, deletable, collection, alias, policyForm) => (
|
||||||
return {
|
return {
|
||||||
scope: scope,
|
scope: scope,
|
||||||
create: clickable(createSelector),
|
create: clickable(createSelector),
|
||||||
form: policyForm('#new-policy-toggle + div'),
|
form: policyForm('#new-policy'),
|
||||||
policies: alias('selectedOptions'),
|
policies: alias('selectedOptions'),
|
||||||
selectedOptions: collection(
|
selectedOptions: collection(
|
||||||
'[data-test-policies] [data-test-tabular-row]',
|
'[data-test-policies] [data-test-tabular-row]',
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
{{yield}}
|
{{yield}}
|
||||||
<fieldset
|
<fieldset
|
||||||
disabled={{if (not (can "write role" item=item)) "disabled"}}
|
|
||||||
class="role-form"
|
class="role-form"
|
||||||
|
disabled={{if (not (can "write role" item=item)) "disabled"}}
|
||||||
data-test-role-form
|
data-test-role-form
|
||||||
|
...attributes
|
||||||
>
|
>
|
||||||
<label class="type-text{{if item.error.Name ' has-error'}}">
|
<label class="type-text{{if item.error.Name ' has-error'}}">
|
||||||
<span>Name</span>
|
<span>Name</span>
|
||||||
|
|
|
@ -1,8 +1,11 @@
|
||||||
<ModalDialog
|
<ModalDialog
|
||||||
class="role-selector"
|
class="role-selector"
|
||||||
data-test-role-form
|
data-test-role-form
|
||||||
|
id="new-role"
|
||||||
@onclose={{action (mut state) "role"}}
|
@onclose={{action (mut state) "role"}}
|
||||||
@name="new-role-toggle"
|
@aria={{hash
|
||||||
|
label=(if (eq state 'role') 'New Role' 'New Policy')
|
||||||
|
}}
|
||||||
as |modal|>
|
as |modal|>
|
||||||
<Ref @target={{this}} @name="modal" @value={{modal}} />
|
<Ref @target={{this}} @name="modal" @value={{modal}} />
|
||||||
<BlockSlot @name="header">
|
<BlockSlot @name="header">
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
# Route
|
||||||
|
|
||||||
|
The `Route` component should be used for the top-level component for *every*
|
||||||
|
route/page template. It provides/will provide functionality (along with the
|
||||||
|
`<Outlet />` component) for setting and announcing the title of the page,
|
||||||
|
passing data down through the route/template hierarchy, automatic
|
||||||
|
orchestration of nested routing and visual animating/transitioning between
|
||||||
|
routes.
|
||||||
|
|
||||||
|
## Arguments
|
||||||
|
|
||||||
|
| Argument | Type | Default | Description |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `name` | `String` | `undefined` | The name of the route in ember routeName format e.g. `dc.services.index`. This is generally the `routeName` variable that is available to you in all Consul UI route/page level templates.|
|
||||||
|
| `title` | `String` | `undefined` | The title for this page (eventually passed through to the `{{page-title}}` helper. This argument should be omitted if a title change isn't required. |
|
||||||
|
| `titleSeparator` | `String` | `undefined` | This can be used in the top-level route to configure the separator for the `{{page-title}}` helper |
|
||||||
|
|
||||||
|
## Exports
|
||||||
|
|
||||||
|
| export | Type | Default | Description |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `model` | `Object` | `undefined` | Arbitrary hash of data passed down from the parent route/outlet |
|
||||||
|
|
||||||
|
```hbs
|
||||||
|
<Route
|
||||||
|
@name={{routeName}}
|
||||||
|
@title="Page Title"
|
||||||
|
@titleSeparator=" - "
|
||||||
|
as |route|>
|
||||||
|
{{route.model.dc.Name}}
|
||||||
|
</Route>
|
||||||
|
```
|
||||||
|
|
||||||
|
Every page/route template has a `routeName` variable exposed specifically to
|
||||||
|
allow you to use this to set the `@name` of the route.
|
|
@ -1,5 +1,10 @@
|
||||||
{{did-insert this.connect}}
|
{{did-insert this.connect}}
|
||||||
{{will-destroy this.disconnect}}
|
{{will-destroy this.disconnect}}
|
||||||
|
|
||||||
|
{{#if this.title}}
|
||||||
|
{{page-title this.title separator=@titleSeparator}}
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
{{yield (hash
|
{{yield (hash
|
||||||
model=model
|
model=model
|
||||||
)}}
|
)}}
|
|
@ -8,10 +8,15 @@ export default class RouteComponent extends Component {
|
||||||
|
|
||||||
@tracked model;
|
@tracked model;
|
||||||
|
|
||||||
|
get title() {
|
||||||
|
return this.args.title;
|
||||||
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
connect() {
|
connect() {
|
||||||
this.routlet.addRoute(this.args.name, this);
|
this.routlet.addRoute(this.args.name, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
disconnect() {
|
disconnect() {
|
||||||
this.routlet.removeRoute(this.args.name, this);
|
this.routlet.removeRoute(this.args.name, this);
|
||||||
|
|
|
@ -2,11 +2,20 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
z-index: 10;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
top: -100px;
|
top: -100px;
|
||||||
transform: translateX(-50%);
|
transform: translateX(-50%);
|
||||||
}
|
}
|
||||||
|
%skip-links div,
|
||||||
|
%skip-links button,
|
||||||
|
%skip-links a {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
%skip-links:focus-within {
|
%skip-links:focus-within {
|
||||||
top: 0px;
|
top: 0px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
color: $white;
|
color: $white;
|
||||||
background-color: $blue-500;
|
background-color: $blue-500;
|
||||||
}
|
}
|
||||||
|
%skip-links button,
|
||||||
%skip-links a {
|
%skip-links a {
|
||||||
color: inherit;
|
color: inherit;
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,6 +43,9 @@
|
||||||
|
|
||||||
<ModalDialog
|
<ModalDialog
|
||||||
class="sparkline-key"
|
class="sparkline-key"
|
||||||
|
@aria={{hash
|
||||||
|
label="Metrics Key"
|
||||||
|
}}
|
||||||
as |modal|>
|
as |modal|>
|
||||||
<Ref @target={{this}} @name="modal" @value={{modal}} />
|
<Ref @target={{this}} @name="modal" @value={{modal}} />
|
||||||
<BlockSlot @name="header">
|
<BlockSlot @name="header">
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
%visually-hidden {
|
%visually-hidden {
|
||||||
position: absolute !important;
|
position: absolute;
|
||||||
height: 1px;
|
|
||||||
width: 1px;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
clip: rect(1px, 1px, 1px, 1px);
|
clip: rect(0 0 0 0);
|
||||||
|
width: 1px;
|
||||||
|
height: 1px;
|
||||||
|
margin: -1px;
|
||||||
|
padding: 0;
|
||||||
|
border: 0;
|
||||||
}
|
}
|
||||||
%visually-hidden-text {
|
%visually-hidden-text {
|
||||||
text-indent: -9000px;
|
text-indent: -9000px;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
@import './empty-state/index';
|
@import 'consul-ui/components/empty-state/index';
|
||||||
.empty-state {
|
.empty-state {
|
||||||
@extend %empty-state;
|
@extend %empty-state;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
<Route
|
<Route
|
||||||
@name={{routeName}}
|
@name={{routeName}}
|
||||||
|
@title='Consul'
|
||||||
|
@titleSeparator=" - "
|
||||||
>
|
>
|
||||||
{{page-title 'Consul' separator=' - '}}
|
|
||||||
|
|
||||||
{{#if (env 'CONSUL_ACLS_ENABLED')}}
|
{{#if (env 'CONSUL_ACLS_ENABLED')}}
|
||||||
{{document-attrs class="has-acls"}}
|
{{document-attrs class="has-acls"}}
|
||||||
|
@ -28,9 +29,12 @@ as |source|>
|
||||||
@nspaces={{nspaces}}
|
@nspaces={{nspaces}}
|
||||||
@nspace={{or nspace nspaces.firstObject}}
|
@nspace={{or nspace nspaces.firstObject}}
|
||||||
@onchange={{action "reauthorize"}}
|
@onchange={{action "reauthorize"}}
|
||||||
>
|
as |consul|>
|
||||||
<Outlet
|
<Outlet
|
||||||
@name="application"
|
@name="application"
|
||||||
|
@model={{hash
|
||||||
|
app=consul
|
||||||
|
}}
|
||||||
as |o|>
|
as |o|>
|
||||||
{{outlet}}
|
{{outlet}}
|
||||||
</Outlet>
|
</Outlet>
|
||||||
|
|
|
@ -1 +1,10 @@
|
||||||
{{outlet}}
|
<Route
|
||||||
|
@name={{routeName}}
|
||||||
|
as |route|>
|
||||||
|
<Outlet
|
||||||
|
@name={{routeName}}
|
||||||
|
@model={{route.model}}
|
||||||
|
as |o|>
|
||||||
|
{{outlet}}
|
||||||
|
</Outlet>
|
||||||
|
</Route>
|
||||||
|
|
|
@ -1,102 +1,104 @@
|
||||||
{{#if isAuthorized }}
|
<Route
|
||||||
{{page-title 'Auth Methods'}}
|
@name={{routeName}}
|
||||||
{{else}}
|
@title="Auth Methods"
|
||||||
{{page-title 'Access Controls'}}
|
as |route|>
|
||||||
{{/if}}
|
{{#let
|
||||||
|
|
||||||
{{#let
|
(hash
|
||||||
|
value=(or sortBy "MethodName:asc")
|
||||||
(hash
|
change=(action (mut sortBy) value="target.selected")
|
||||||
value=(or sortBy "MethodName:asc")
|
|
||||||
change=(action (mut sortBy) value="target.selected")
|
|
||||||
)
|
|
||||||
|
|
||||||
(hash
|
|
||||||
kind=(hash
|
|
||||||
value=(if kind (split kind ',') undefined)
|
|
||||||
change=(action (mut kind) value="target.selectedItems")
|
|
||||||
)
|
)
|
||||||
source=(hash
|
|
||||||
value=(if source (split source ',') undefined)
|
(hash
|
||||||
change=(action (mut source) value="target.selectedItems")
|
kind=(hash
|
||||||
)
|
value=(if kind (split kind ',') undefined)
|
||||||
searchproperty=(hash
|
change=(action (mut kind) value="target.selectedItems")
|
||||||
value=(if (not-eq searchproperty undefined)
|
)
|
||||||
(split searchproperty ',')
|
source=(hash
|
||||||
searchProperties
|
value=(if source (split source ',') undefined)
|
||||||
|
change=(action (mut source) value="target.selectedItems")
|
||||||
|
)
|
||||||
|
searchproperty=(hash
|
||||||
|
value=(if (not-eq searchproperty undefined)
|
||||||
|
(split searchproperty ',')
|
||||||
|
searchProperties
|
||||||
|
)
|
||||||
|
change=(action (mut searchproperty) value="target.selectedItems")
|
||||||
|
default=searchProperties
|
||||||
)
|
)
|
||||||
change=(action (mut searchproperty) value="target.selectedItems")
|
|
||||||
default=searchProperties
|
|
||||||
)
|
)
|
||||||
)
|
|
||||||
|
|
||||||
items
|
items
|
||||||
|
|
||||||
as |sort filters items|}}
|
as |sort filters items|}}
|
||||||
|
|
||||||
|
|
||||||
<AppView
|
<AppView
|
||||||
@authorized={{isAuthorized}}
|
@authorized={{isAuthorized}}
|
||||||
@enabled={{isEnabled}}
|
@enabled={{isEnabled}}
|
||||||
>
|
@login={{route.model.app.login.open}}
|
||||||
<BlockSlot @name="header">
|
>
|
||||||
<h1>
|
<BlockSlot @name="header">
|
||||||
Access Controls
|
<h1>
|
||||||
</h1>
|
{{route.model.hi}}
|
||||||
</BlockSlot>
|
Auth Methods
|
||||||
<BlockSlot @name="toolbar">
|
</h1>
|
||||||
{{#if (gt items.length 0)}}
|
</BlockSlot>
|
||||||
<Consul::AuthMethod::SearchBar
|
<BlockSlot @name="toolbar">
|
||||||
@search={{search}}
|
{{#if (gt items.length 0)}}
|
||||||
@onsearch={{action (mut search) value="target.value"}}
|
<Consul::AuthMethod::SearchBar
|
||||||
@sort={{sort}}
|
@search={{search}}
|
||||||
@filter={{filters}}
|
@onsearch={{action (mut search) value="target.value"}}
|
||||||
/>
|
@sort={{sort}}
|
||||||
{{/if}}
|
@filter={{filters}}
|
||||||
</BlockSlot>
|
/>
|
||||||
<BlockSlot @name="content">
|
{{/if}}
|
||||||
<DataCollection
|
</BlockSlot>
|
||||||
@type="auth-method"
|
<BlockSlot @name="content">
|
||||||
@sort={{sort.value}}
|
<DataCollection
|
||||||
@filters={{filters}}
|
@type="auth-method"
|
||||||
@search={{search}}
|
@sort={{sort.value}}
|
||||||
@items={{items}}
|
@filters={{filters}}
|
||||||
as |collection|>
|
@search={{search}}
|
||||||
<collection.Collection>
|
@items={{items}}
|
||||||
<Consul::AuthMethod::List @items={{collection.items}} />
|
as |collection|>
|
||||||
</collection.Collection>
|
<collection.Collection>
|
||||||
<collection.Empty>
|
<Consul::AuthMethod::List @items={{collection.items}} />
|
||||||
<EmptyState @allowLogin={{true}}>
|
</collection.Collection>
|
||||||
<BlockSlot @name="header">
|
<collection.Empty>
|
||||||
<h2>
|
<EmptyState
|
||||||
{{#if (gt items.length 0)}}
|
@login={{route.model.app.login.open}}
|
||||||
No auth methods found
|
>
|
||||||
{{else}}
|
<BlockSlot @name="header">
|
||||||
Welcome to Auth Methods
|
<h2>
|
||||||
{{/if}}
|
{{#if (gt items.length 0)}}
|
||||||
</h2>
|
No auth methods found
|
||||||
</BlockSlot>
|
{{else}}
|
||||||
<BlockSlot @name="body">
|
Welcome to Auth Methods
|
||||||
<p>
|
{{/if}}
|
||||||
{{#if (gt items.length 0)}}
|
</h2>
|
||||||
No auth methods where found matching that search, or you may not have access to view the auth methods you are searching for.
|
</BlockSlot>
|
||||||
{{else}}
|
<BlockSlot @name="body">
|
||||||
There don't seem to be any auth methods, or you may not have access to view auth methods yet.
|
<p>
|
||||||
{{/if}}
|
{{#if (gt items.length 0)}}
|
||||||
</p>
|
No auth methods where found matching that search, or you may not have access to view the auth methods you are searching for.
|
||||||
</BlockSlot>
|
{{else}}
|
||||||
<BlockSlot @name="actions">
|
There don't seem to be any auth methods, or you may not have access to view auth methods yet.
|
||||||
<li class="docs-link">
|
{{/if}}
|
||||||
<a href="{{env 'CONSUL_DOCS_URL'}}/security/acl/auth-methods" rel="noopener noreferrer" target="_blank">Documentation on auth methods</a>
|
</p>
|
||||||
</li>
|
</BlockSlot>
|
||||||
<li class="learn-link">
|
<BlockSlot @name="actions">
|
||||||
<a href="{{env 'CONSUL_DOCS_API_URL'}}/acl/auth-methods.html" rel="noopener noreferrer" target="_blank">Read the API Docs</a>
|
<li class="docs-link">
|
||||||
</li>
|
<a href="{{env 'CONSUL_DOCS_URL'}}/security/acl/auth-methods" rel="noopener noreferrer" target="_blank">Documentation on auth methods</a>
|
||||||
</BlockSlot>
|
</li>
|
||||||
</EmptyState>
|
<li class="learn-link">
|
||||||
</collection.Empty>
|
<a href="{{env 'CONSUL_DOCS_API_URL'}}/acl/auth-methods.html" rel="noopener noreferrer" target="_blank">Read the API Docs</a>
|
||||||
</DataCollection>
|
</li>
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
</AppView>
|
</EmptyState>
|
||||||
{{/let}}
|
</collection.Empty>
|
||||||
|
</DataCollection>
|
||||||
|
</BlockSlot>
|
||||||
|
</AppView>
|
||||||
|
{{/let}}
|
||||||
|
</Route>
|
||||||
|
|
|
@ -1,48 +1,53 @@
|
||||||
<AppView>
|
<Route
|
||||||
<BlockSlot @name="notification" as |status type item error|>
|
@name={{routeName}}
|
||||||
<Consul::Acl::Notifications
|
@title={{if create 'New ACL' 'Edit ACL'}}
|
||||||
@status={{status}}
|
as |route|>
|
||||||
@type={{type}}
|
<AppView>
|
||||||
@error={{error}}
|
<BlockSlot @name="notification" as |status type item error|>
|
||||||
/>
|
<Consul::Acl::Notifications
|
||||||
</BlockSlot>
|
@status={{status}}
|
||||||
<BlockSlot @name="breadcrumbs">
|
@type={{type}}
|
||||||
<ol>
|
@error={{error}}
|
||||||
<li><a data-test-back href={{href-to 'dc.acls'}}>All Tokens</a></li>
|
/>
|
||||||
</ol>
|
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
<BlockSlot @name="header">
|
<BlockSlot @name="breadcrumbs">
|
||||||
<h1>
|
<ol>
|
||||||
{{#if item.Name }}
|
<li><a data-test-back href={{href-to 'dc.acls'}}>All Tokens</a></li>
|
||||||
{{item.Name}}
|
</ol>
|
||||||
{{else}}
|
</BlockSlot>
|
||||||
New token
|
<BlockSlot @name="header">
|
||||||
{{/if}}
|
<h1>
|
||||||
</h1>
|
{{#if item.Name }}
|
||||||
</BlockSlot>
|
{{item.Name}}
|
||||||
<BlockSlot @name="actions">
|
{{else}}
|
||||||
{{#if (not create) }}
|
New token
|
||||||
<CopyButton @value={{item.ID}} @name="token ID">
|
|
||||||
Copy token ID
|
|
||||||
</CopyButton>
|
|
||||||
{{#if (can "duplicate acl" item=item)}}
|
|
||||||
<button type="button" {{ action "clone" item }}>Clone token</button>
|
|
||||||
<ConfirmationDialog @message="Are you sure you want to use this ACL token?">
|
|
||||||
<BlockSlot @name="action" as |confirm|>
|
|
||||||
<button data-test-use type="button" {{ action confirm 'use' item }}>Use token</button>
|
|
||||||
</BlockSlot>
|
|
||||||
<BlockSlot @name="dialog" as |execute cancel message|>
|
|
||||||
<p>
|
|
||||||
{{message}}
|
|
||||||
</p>
|
|
||||||
<button type="button" class="type-delete" {{action execute}}>Confirm Use</button>
|
|
||||||
<button type="button" class="type-cancel" {{action cancel}}>Cancel</button>
|
|
||||||
</BlockSlot>
|
|
||||||
</ConfirmationDialog>
|
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{/if}}
|
</h1>
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
<BlockSlot @name="content">
|
<BlockSlot @name="actions">
|
||||||
{{ partial 'dc/acls/form'}}
|
{{#if (not create) }}
|
||||||
</BlockSlot>
|
<CopyButton @value={{item.ID}} @name="token ID">
|
||||||
</AppView>
|
Copy token ID
|
||||||
|
</CopyButton>
|
||||||
|
{{#if (can "duplicate acl" item=item)}}
|
||||||
|
<button type="button" {{ action "clone" item }}>Clone token</button>
|
||||||
|
<ConfirmationDialog @message="Are you sure you want to use this ACL token?">
|
||||||
|
<BlockSlot @name="action" as |confirm|>
|
||||||
|
<button data-test-use type="button" {{ action confirm 'use' item }}>Use token</button>
|
||||||
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="dialog" as |execute cancel message|>
|
||||||
|
<p>
|
||||||
|
{{message}}
|
||||||
|
</p>
|
||||||
|
<button type="button" class="type-delete" {{action execute}}>Confirm Use</button>
|
||||||
|
<button type="button" class="type-cancel" {{action cancel}}>Cancel</button>
|
||||||
|
</BlockSlot>
|
||||||
|
</ConfirmationDialog>
|
||||||
|
{{/if}}
|
||||||
|
{{/if}}
|
||||||
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="content">
|
||||||
|
{{ partial 'dc/acls/form'}}
|
||||||
|
</BlockSlot>
|
||||||
|
</AppView>
|
||||||
|
</Route>
|
|
@ -1,95 +1,101 @@
|
||||||
{{page-title 'ACLs'}}
|
<Route
|
||||||
{{#let
|
@name={{routeName}}
|
||||||
|
@title="ACLs"
|
||||||
|
as |route|>
|
||||||
|
{{#let
|
||||||
|
|
||||||
(hash
|
(hash
|
||||||
value=(or sortBy "Name:asc")
|
value=(or sortBy "Name:asc")
|
||||||
change=(action (mut sortBy) value="target.selected")
|
change=(action (mut sortBy) value="target.selected")
|
||||||
)
|
|
||||||
|
|
||||||
(hash
|
|
||||||
kind=(hash
|
|
||||||
value=(if kind (split kind ',') undefined)
|
|
||||||
change=(action (mut kind) value="target.selectedItems")
|
|
||||||
)
|
)
|
||||||
)
|
|
||||||
|
|
||||||
items
|
(hash
|
||||||
|
kind=(hash
|
||||||
|
value=(if kind (split kind ',') undefined)
|
||||||
|
change=(action (mut kind) value="target.selectedItems")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
as |sort filters items|}}
|
items
|
||||||
|
|
||||||
<AppView>
|
as |sort filters items|}}
|
||||||
<BlockSlot @name="notification" as |status type item error|>
|
|
||||||
<Consul::Acl::Notifications
|
|
||||||
@status={{status}}
|
|
||||||
@type={{type}}
|
|
||||||
@error={{error}}
|
|
||||||
/>
|
|
||||||
</BlockSlot>
|
|
||||||
<BlockSlot @name="header">
|
|
||||||
<h1>
|
|
||||||
ACL Tokens <em>{{format-number items.length}} total</em>
|
|
||||||
</h1>
|
|
||||||
<label for="toolbar-toggle"></label>
|
|
||||||
</BlockSlot>
|
|
||||||
<BlockSlot @name="actions">
|
|
||||||
{{#if (can "create acls")}}
|
|
||||||
<a data-test-create href="{{href-to 'dc.acls.create'}}" class="type-create">Create</a>
|
|
||||||
{{/if}}
|
|
||||||
</BlockSlot>
|
|
||||||
<BlockSlot @name="toolbar">
|
|
||||||
{{#if (gt items.length 0) }}
|
|
||||||
<Consul::Acl::SearchBar
|
|
||||||
@search={{search}}
|
|
||||||
@onsearch={{action (mut search) value="target.value"}}
|
|
||||||
|
|
||||||
@sort={{sort}}
|
<AppView>
|
||||||
|
<BlockSlot @name="notification" as |status type item error|>
|
||||||
@filter={{filters}}
|
<Consul::Acl::Notifications
|
||||||
|
@status={{status}}
|
||||||
|
@type={{type}}
|
||||||
|
@error={{error}}
|
||||||
/>
|
/>
|
||||||
{{/if}}
|
</BlockSlot>
|
||||||
</BlockSlot>
|
<BlockSlot @name="header">
|
||||||
<BlockSlot @name="content">
|
<h1>
|
||||||
<DataCollection
|
ACL Tokens <em>{{format-number items.length}} total</em>
|
||||||
@type="acl"
|
</h1>
|
||||||
@sort={{sort.value}}
|
<label for="toolbar-toggle"></label>
|
||||||
@filters={{filters}}
|
</BlockSlot>
|
||||||
@search={{search}}
|
<BlockSlot @name="actions">
|
||||||
@items={{items}}
|
{{#if (can "create acls")}}
|
||||||
as |collection|>
|
<a data-test-create href="{{href-to 'dc.acls.create'}}" class="type-create">Create</a>
|
||||||
<collection.Collection>
|
{{/if}}
|
||||||
<Consul::Acl::List
|
</BlockSlot>
|
||||||
@items={{collection.items}}
|
<BlockSlot @name="toolbar">
|
||||||
|
{{#if (gt items.length 0) }}
|
||||||
|
<Consul::Acl::SearchBar
|
||||||
|
@search={{search}}
|
||||||
|
@onsearch={{action (mut search) value="target.value"}}
|
||||||
|
|
||||||
@ondelete={{route-action 'delete'}}
|
@sort={{sort}}
|
||||||
@onuse={{route-action 'use'}}
|
|
||||||
@onclone={{route-action 'clone'}}
|
|
||||||
>
|
|
||||||
</Consul::Acl::List>
|
|
||||||
</collection.Collection>
|
|
||||||
<collection.Empty>
|
|
||||||
<EmptyState @allowLogin={{true}}>
|
|
||||||
<BlockSlot @name="header">
|
|
||||||
<h2>
|
|
||||||
{{#if (gt items.length 0)}}
|
|
||||||
No ACLs found
|
|
||||||
{{else}}
|
|
||||||
Welcome to ACLs
|
|
||||||
{{/if}}
|
|
||||||
</h2>
|
|
||||||
</BlockSlot>
|
|
||||||
<BlockSlot @name="body">
|
|
||||||
<p>
|
|
||||||
{{#if (gt items.length 0)}}
|
|
||||||
No ACLs where found matching that search, or you may not have access to view the ACLs you are searching for.
|
|
||||||
{{else}}
|
|
||||||
There don't seem to be any ACLs yet, or you may not have access to view ACLs yet.
|
|
||||||
{{/if}}
|
|
||||||
</p>
|
|
||||||
</BlockSlot>
|
|
||||||
</EmptyState>
|
|
||||||
</collection.Empty>
|
|
||||||
</DataCollection>
|
|
||||||
</BlockSlot>
|
|
||||||
</AppView>
|
|
||||||
|
|
||||||
{{/let}}
|
@filter={{filters}}
|
||||||
|
/>
|
||||||
|
{{/if}}
|
||||||
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="content">
|
||||||
|
<DataCollection
|
||||||
|
@type="acl"
|
||||||
|
@sort={{sort.value}}
|
||||||
|
@filters={{filters}}
|
||||||
|
@search={{search}}
|
||||||
|
@items={{items}}
|
||||||
|
as |collection|>
|
||||||
|
<collection.Collection>
|
||||||
|
<Consul::Acl::List
|
||||||
|
@items={{collection.items}}
|
||||||
|
|
||||||
|
@ondelete={{route-action 'delete'}}
|
||||||
|
@onuse={{route-action 'use'}}
|
||||||
|
@onclone={{route-action 'clone'}}
|
||||||
|
>
|
||||||
|
</Consul::Acl::List>
|
||||||
|
</collection.Collection>
|
||||||
|
<collection.Empty>
|
||||||
|
<EmptyState
|
||||||
|
@login={{route.model.app.login.open}}
|
||||||
|
>
|
||||||
|
<BlockSlot @name="header">
|
||||||
|
<h2>
|
||||||
|
{{#if (gt items.length 0)}}
|
||||||
|
No ACLs found
|
||||||
|
{{else}}
|
||||||
|
Welcome to ACLs
|
||||||
|
{{/if}}
|
||||||
|
</h2>
|
||||||
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="body">
|
||||||
|
<p>
|
||||||
|
{{#if (gt items.length 0)}}
|
||||||
|
No ACLs where found matching that search, or you may not have access to view the ACLs you are searching for.
|
||||||
|
{{else}}
|
||||||
|
There don't seem to be any ACLs yet, or you may not have access to view ACLs yet.
|
||||||
|
{{/if}}
|
||||||
|
</p>
|
||||||
|
</BlockSlot>
|
||||||
|
</EmptyState>
|
||||||
|
</collection.Empty>
|
||||||
|
</DataCollection>
|
||||||
|
</BlockSlot>
|
||||||
|
</AppView>
|
||||||
|
|
||||||
|
{{/let}}
|
||||||
|
</Route>
|
||||||
|
|
|
@ -26,6 +26,9 @@
|
||||||
<ModalDialog
|
<ModalDialog
|
||||||
data-test-delete-modal
|
data-test-delete-modal
|
||||||
@onclose={{action cancel}}
|
@onclose={{action cancel}}
|
||||||
|
@aria={{hash
|
||||||
|
label="Policy in Use"
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<BlockSlot @name="header">
|
<BlockSlot @name="header">
|
||||||
<h2>Policy in Use</h2>
|
<h2>Policy in Use</h2>
|
||||||
|
|
|
@ -1,61 +1,58 @@
|
||||||
{{#if isAuthorized }}
|
<Route
|
||||||
{{#if create }}
|
@name={{routeName}}
|
||||||
{{page-title 'New Policy'}}
|
@title={{if isAuthorized (if create 'New Policy' 'Edit Policy') 'Access Controls'}}
|
||||||
{{else}}
|
as |route|>
|
||||||
{{page-title 'Edit Policy'}}
|
<AppView
|
||||||
{{/if}}
|
@authorized={{isAuthorized}}
|
||||||
{{else}}
|
@enabled={{isEnabled}}
|
||||||
{{page-title 'Access Controls'}}
|
@login={{route.model.app.login.open}}
|
||||||
{{/if}}
|
>
|
||||||
<AppView
|
<BlockSlot @name="notification" as |status type item error|>
|
||||||
@authorized={{isAuthorized}}
|
<Consul::Policy::Notifications
|
||||||
@enabled={{isEnabled}}
|
@type={{type}}
|
||||||
>
|
@status={{status}}
|
||||||
<BlockSlot @name="notification" as |status type item error|>
|
@item={{item}}
|
||||||
<Consul::Policy::Notifications
|
@error={{error}}
|
||||||
@type={{type}}
|
/>
|
||||||
@status={{status}}
|
</BlockSlot>
|
||||||
@item={{item}}
|
<BlockSlot @name="breadcrumbs">
|
||||||
@error={{error}}
|
<ol>
|
||||||
/>
|
<li><a data-test-back href={{href-to 'dc.acls.policies'}}>All Policies</a></li>
|
||||||
</BlockSlot>
|
</ol>
|
||||||
<BlockSlot @name="breadcrumbs">
|
</BlockSlot>
|
||||||
<ol>
|
<BlockSlot @name="header">
|
||||||
<li><a data-test-back href={{href-to 'dc.acls.policies'}}>All Policies</a></li>
|
<h1>
|
||||||
</ol>
|
{{#if isAuthorized }}
|
||||||
</BlockSlot>
|
{{#if create }}
|
||||||
<BlockSlot @name="header">
|
New Policy
|
||||||
<h1>
|
|
||||||
{{#if isAuthorized }}
|
|
||||||
{{#if create }}
|
|
||||||
New Policy
|
|
||||||
{{else}}
|
|
||||||
{{#if (can "write policy" item=item)}}
|
|
||||||
Edit Policy
|
|
||||||
{{else}}
|
{{else}}
|
||||||
View Policy
|
{{#if (can "write policy" item=item)}}
|
||||||
|
Edit Policy
|
||||||
|
{{else}}
|
||||||
|
View Policy
|
||||||
|
{{/if}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
{{else}}
|
||||||
|
Access Controls
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{else}}
|
</h1>
|
||||||
Access Controls
|
</BlockSlot>
|
||||||
{{/if}}
|
<BlockSlot @name="content">
|
||||||
</h1>
|
{{#if (not create) }}
|
||||||
</BlockSlot>
|
<div class="definition-table">
|
||||||
<BlockSlot @name="content">
|
<dl>
|
||||||
{{#if (not create) }}
|
<dt>Policy ID</dt>
|
||||||
<div class="definition-table">
|
<dd>
|
||||||
<dl>
|
<CopyButton @value={{item.ID}} @name="Policy ID" @position="top-start" /> {{item.ID}}
|
||||||
<dt>Policy ID</dt>
|
</dd>
|
||||||
<dd>
|
</dl>
|
||||||
<CopyButton @value={{item.ID}} @name="Policy ID" @position="top-start" /> {{item.ID}}
|
</div>
|
||||||
</dd>
|
{{/if}}
|
||||||
</dl>
|
{{#if (eq (policy/typeof item) 'policy-management')}}
|
||||||
</div>
|
{{ partial 'dc/acls/policies/view'}}
|
||||||
{{/if}}
|
{{else}}
|
||||||
{{#if (eq (policy/typeof item) 'policy-management')}}
|
{{ partial 'dc/acls/policies/form'}}
|
||||||
{{ partial 'dc/acls/policies/view'}}
|
{{/if}}
|
||||||
{{else}}
|
</BlockSlot>
|
||||||
{{ partial 'dc/acls/policies/form'}}
|
</AppView>
|
||||||
{{/if}}
|
</Route>
|
||||||
</BlockSlot>
|
|
||||||
</AppView>
|
|
|
@ -1,115 +1,118 @@
|
||||||
{{#if isAuthorized }}
|
<Route
|
||||||
{{page-title 'Policies'}}
|
@name={{routeName}}
|
||||||
{{else}}
|
@title="Policies"
|
||||||
{{page-title 'Access Controls'}}
|
as |route|>
|
||||||
{{/if}}
|
{{#let
|
||||||
{{#let
|
|
||||||
|
|
||||||
(hash
|
(hash
|
||||||
value=(or sortBy "Name:asc")
|
value=(or sortBy "Name:asc")
|
||||||
change=(action (mut sortBy) value="target.selected")
|
change=(action (mut sortBy) value="target.selected")
|
||||||
)
|
)
|
||||||
|
|
||||||
(hash
|
(hash
|
||||||
kind=(hash
|
kind=(hash
|
||||||
value=(if kind (split kind ',') undefined)
|
value=(if kind (split kind ',') undefined)
|
||||||
change=(action (mut kind) value="target.selectedItems")
|
change=(action (mut kind) value="target.selectedItems")
|
||||||
)
|
)
|
||||||
datacenter=(hash
|
datacenter=(hash
|
||||||
value=(if datacenter (split datacenter ',') undefined)
|
value=(if datacenter (split datacenter ',') undefined)
|
||||||
change=(action (mut datacenter) value="target.selectedItems")
|
change=(action (mut datacenter) value="target.selectedItems")
|
||||||
)
|
)
|
||||||
searchproperty=(hash
|
searchproperty=(hash
|
||||||
value=(if (not-eq searchproperty undefined)
|
value=(if (not-eq searchproperty undefined)
|
||||||
(split searchproperty ',')
|
(split searchproperty ',')
|
||||||
searchProperties
|
searchProperties
|
||||||
|
)
|
||||||
|
change=(action (mut searchproperty) value="target.selectedItems")
|
||||||
|
default=searchProperties
|
||||||
)
|
)
|
||||||
change=(action (mut searchproperty) value="target.selectedItems")
|
|
||||||
default=searchProperties
|
|
||||||
)
|
)
|
||||||
)
|
|
||||||
|
|
||||||
items
|
items
|
||||||
|
|
||||||
as |sort filters items|}}
|
as |sort filters items|}}
|
||||||
<AppView
|
<AppView
|
||||||
@authorized={{isAuthorized}}
|
@authorized={{isAuthorized}}
|
||||||
@enabled={{isEnabled}}
|
@enabled={{isEnabled}}
|
||||||
>
|
@login={{route.model.app.login.open}}
|
||||||
<BlockSlot @name="notification" as |status type item error|>
|
>
|
||||||
<Consul::Policy::Notifications
|
<BlockSlot @name="notification" as |status type item error|>
|
||||||
@type={{type}}
|
<Consul::Policy::Notifications
|
||||||
@status={{status}}
|
@type={{type}}
|
||||||
@item={{item}}
|
@status={{status}}
|
||||||
@error={{error}}
|
@item={{item}}
|
||||||
/>
|
@error={{error}}
|
||||||
</BlockSlot>
|
/>
|
||||||
<BlockSlot @name="header">
|
</BlockSlot>
|
||||||
<h1>
|
<BlockSlot @name="header">
|
||||||
Policies
|
<h1>
|
||||||
</h1>
|
Policies
|
||||||
</BlockSlot>
|
</h1>
|
||||||
<BlockSlot @name="actions">
|
</BlockSlot>
|
||||||
<a data-test-create href="{{href-to 'dc.acls.policies.create'}}" class="type-create">Create</a>
|
<BlockSlot @name="actions">
|
||||||
</BlockSlot>
|
<a data-test-create href="{{href-to 'dc.acls.policies.create'}}" class="type-create">Create</a>
|
||||||
<BlockSlot @name="toolbar">
|
</BlockSlot>
|
||||||
{{#if (gt items.length 0) }}
|
<BlockSlot @name="toolbar">
|
||||||
<Consul::Policy::SearchBar
|
{{#if (gt items.length 0) }}
|
||||||
@search={{search}}
|
<Consul::Policy::SearchBar
|
||||||
@onsearch={{action (mut search) value="target.value"}}
|
@search={{search}}
|
||||||
|
@onsearch={{action (mut search) value="target.value"}}
|
||||||
|
|
||||||
@sort={{sort}}
|
@sort={{sort}}
|
||||||
|
|
||||||
@filter={{filters}}
|
@filter={{filters}}
|
||||||
/>
|
/>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
<BlockSlot @name="content">
|
<BlockSlot @name="content">
|
||||||
<DataCollection
|
<DataCollection
|
||||||
@type="policy"
|
@type="policy"
|
||||||
@sort={{sort.value}}
|
@sort={{sort.value}}
|
||||||
@filters={{filters}}
|
@filters={{filters}}
|
||||||
@search={{search}}
|
@search={{search}}
|
||||||
@items={{items}}
|
@items={{items}}
|
||||||
as |collection|>
|
as |collection|>
|
||||||
<collection.Collection>
|
<collection.Collection>
|
||||||
<Consul::Policy::List
|
<Consul::Policy::List
|
||||||
@items={{collection.items}}
|
@items={{collection.items}}
|
||||||
@ondelete={{route-action 'delete'}}
|
@ondelete={{route-action 'delete'}}
|
||||||
/>
|
/>
|
||||||
</collection.Collection>
|
</collection.Collection>
|
||||||
<collection.Empty>
|
<collection.Empty>
|
||||||
<EmptyState @allowLogin={{true}}>
|
<EmptyState
|
||||||
<BlockSlot @name="header">
|
@login={{route.model.app.login.open}}
|
||||||
<h2>
|
>
|
||||||
{{#if (gt items.length 0)}}
|
<BlockSlot @name="header">
|
||||||
No policies found
|
<h2>
|
||||||
{{else}}
|
{{#if (gt items.length 0)}}
|
||||||
Welcome to Policies
|
No policies found
|
||||||
{{/if}}
|
{{else}}
|
||||||
</h2>
|
Welcome to Policies
|
||||||
</BlockSlot>
|
{{/if}}
|
||||||
<BlockSlot @name="body">
|
</h2>
|
||||||
<p>
|
</BlockSlot>
|
||||||
{{#if (gt items.length 0)}}
|
<BlockSlot @name="body">
|
||||||
No policies where found matching that search, or you may not have access to view the policies you are searching for.
|
<p>
|
||||||
{{else}}
|
{{#if (gt items.length 0)}}
|
||||||
There don't seem to be any policies, or you may not have access to view policies yet.
|
No policies where found matching that search, or you may not have access to view the policies you are searching for.
|
||||||
{{/if}}
|
{{else}}
|
||||||
</p>
|
There don't seem to be any policies, or you may not have access to view policies yet.
|
||||||
</BlockSlot>
|
{{/if}}
|
||||||
<BlockSlot @name="actions">
|
</p>
|
||||||
<li class="docs-link">
|
</BlockSlot>
|
||||||
<a href="{{env 'CONSUL_DOCS_URL'}}/commands/acl/policy" rel="noopener noreferrer" target="_blank">Documentation on policies</a>
|
<BlockSlot @name="actions">
|
||||||
</li>
|
<li class="docs-link">
|
||||||
<li class="learn-link">
|
<a href="{{env 'CONSUL_DOCS_URL'}}/commands/acl/policy" rel="noopener noreferrer" target="_blank">Documentation on policies</a>
|
||||||
<a href="{{env 'CONSUL_LEARN_URL'}}/consul/security-networking/managing-acl-policies" rel="noopener noreferrer" target="_blank">Read the guide</a>
|
</li>
|
||||||
</li>
|
<li class="learn-link">
|
||||||
</BlockSlot>
|
<a href="{{env 'CONSUL_LEARN_URL'}}/consul/security-networking/managing-acl-policies" rel="noopener noreferrer" target="_blank">Read the guide</a>
|
||||||
</EmptyState>
|
</li>
|
||||||
</collection.Empty>
|
</BlockSlot>
|
||||||
</DataCollection>
|
</EmptyState>
|
||||||
</BlockSlot>
|
</collection.Empty>
|
||||||
</AppView>
|
</DataCollection>
|
||||||
|
</BlockSlot>
|
||||||
|
</AppView>
|
||||||
|
|
||||||
{{/let}}
|
{{/let}}
|
||||||
|
</Route>
|
|
@ -24,7 +24,12 @@
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
<BlockSlot @name="dialog" as |execute cancel message|>
|
<BlockSlot @name="dialog" as |execute cancel message|>
|
||||||
{{#if (gt items.length 0)}}
|
{{#if (gt items.length 0)}}
|
||||||
<ModalDialog @onclose={{action cancel}}>
|
<ModalDialog
|
||||||
|
@onclose={{action cancel}}
|
||||||
|
@aria={{hash
|
||||||
|
label="Role in Use"
|
||||||
|
}}
|
||||||
|
>
|
||||||
<BlockSlot @name="header">
|
<BlockSlot @name="header">
|
||||||
<h2>Role in Use</h2>
|
<h2>Role in Use</h2>
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
|
|
|
@ -1,53 +1,50 @@
|
||||||
{{#if isAuthorized }}
|
<Route
|
||||||
{{#if item.ID}}
|
@name={{routeName}}
|
||||||
{{page-title 'Edit Role'}}
|
@title={{if isAuthorized (if create 'New Role' 'Edit Role') 'Access Controls'}}
|
||||||
|
as |route|>
|
||||||
|
<AppView
|
||||||
|
@authorized={{isAuthorized}}
|
||||||
|
@enabled={{isEnabled}}
|
||||||
|
@login={{route.model.app.login.open}}
|
||||||
|
>
|
||||||
|
<BlockSlot @name="notification" as |status type item error|>
|
||||||
|
<Consul::Role::Notifications
|
||||||
|
@type={{type}}
|
||||||
|
@status={{status}}
|
||||||
|
@item={{item}}
|
||||||
|
@error={{error}}
|
||||||
|
/>
|
||||||
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="breadcrumbs">
|
||||||
|
<ol>
|
||||||
|
<li><a data-test-back href={{href-to 'dc.acls.roles'}}>All Roles</a></li>
|
||||||
|
</ol>
|
||||||
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="header">
|
||||||
|
<h1>
|
||||||
|
{{#if isAuthorized }}
|
||||||
|
{{#if create }}
|
||||||
|
New Role
|
||||||
|
{{else}}
|
||||||
|
Edit Role
|
||||||
|
{{/if}}
|
||||||
{{else}}
|
{{else}}
|
||||||
{{page-title 'New Role'}}
|
Access Controls
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{else}}
|
</h1>
|
||||||
{{page-title 'Access Controls'}}
|
</BlockSlot>
|
||||||
{{/if}}
|
<BlockSlot @name="content">
|
||||||
<AppView
|
{{#if (not create) }}
|
||||||
@authorized={{isAuthorized}}
|
<div class="definition-table">
|
||||||
@enabled={{isEnabled}}
|
<dl>
|
||||||
>
|
<dt>Role ID</dt>
|
||||||
<BlockSlot @name="notification" as |status type item error|>
|
<dd>
|
||||||
<Consul::Role::Notifications
|
<CopyButton @value={{item.ID}} @name="Role ID" @position="top-start" /> {{item.ID}}
|
||||||
@type={{type}}
|
</dd>
|
||||||
@status={{status}}
|
</dl>
|
||||||
@item={{item}}
|
</div>
|
||||||
@error={{error}}
|
|
||||||
/>
|
|
||||||
</BlockSlot>
|
|
||||||
<BlockSlot @name="breadcrumbs">
|
|
||||||
<ol>
|
|
||||||
<li><a data-test-back href={{href-to 'dc.acls.roles'}}>All Roles</a></li>
|
|
||||||
</ol>
|
|
||||||
</BlockSlot>
|
|
||||||
<BlockSlot @name="header">
|
|
||||||
<h1>
|
|
||||||
{{#if isAuthorized }}
|
|
||||||
{{#if create }}
|
|
||||||
New Role
|
|
||||||
{{else}}
|
|
||||||
Edit Role
|
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{else}}
|
{{ partial 'dc/acls/roles/form'}}
|
||||||
Access Controls
|
</BlockSlot>
|
||||||
{{/if}}
|
</AppView>
|
||||||
</h1>
|
</Route>
|
||||||
</BlockSlot>
|
|
||||||
<BlockSlot @name="content">
|
|
||||||
{{#if (not create) }}
|
|
||||||
<div class="definition-table">
|
|
||||||
<dl>
|
|
||||||
<dt>Role ID</dt>
|
|
||||||
<dd>
|
|
||||||
<CopyButton @value={{item.ID}} @name="Role ID" @position="top-start" /> {{item.ID}}
|
|
||||||
</dd>
|
|
||||||
</dl>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
{{ partial 'dc/acls/roles/form'}}
|
|
||||||
</BlockSlot>
|
|
||||||
</AppView>
|
|
|
@ -1,108 +1,110 @@
|
||||||
{{#if isAuthorized }}
|
<Route
|
||||||
{{page-title 'Roles'}}
|
@name={{routeName}}
|
||||||
{{else}}
|
@title="Roles"
|
||||||
{{page-title 'Access Controls'}}
|
as |route|>
|
||||||
{{/if}}
|
{{#let
|
||||||
|
|
||||||
{{#let
|
(hash
|
||||||
|
value=(or sortBy "Name:asc")
|
||||||
(hash
|
change=(action (mut sortBy) value="target.selected")
|
||||||
value=(or sortBy "Name:asc")
|
|
||||||
change=(action (mut sortBy) value="target.selected")
|
|
||||||
)
|
|
||||||
|
|
||||||
(hash
|
|
||||||
searchproperty=(hash
|
|
||||||
value=(if (not-eq searchproperty undefined)
|
|
||||||
(split searchproperty ',')
|
|
||||||
searchProperties
|
|
||||||
)
|
|
||||||
change=(action (mut searchproperty) value="target.selectedItems")
|
|
||||||
default=searchProperties
|
|
||||||
)
|
)
|
||||||
)
|
|
||||||
|
|
||||||
items
|
(hash
|
||||||
|
searchproperty=(hash
|
||||||
|
value=(if (not-eq searchproperty undefined)
|
||||||
|
(split searchproperty ',')
|
||||||
|
searchProperties
|
||||||
|
)
|
||||||
|
change=(action (mut searchproperty) value="target.selectedItems")
|
||||||
|
default=searchProperties
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
as |sort filters items|}}
|
items
|
||||||
|
|
||||||
<AppView
|
as |sort filters items|}}
|
||||||
@authorized={{isAuthorized}}
|
|
||||||
@enabled={{isEnabled}}
|
|
||||||
>
|
|
||||||
<BlockSlot @name="notification" as |status type item error|>
|
|
||||||
<Consul::Role::Notifications
|
|
||||||
@type={{type}}
|
|
||||||
@status={{status}}
|
|
||||||
@item={{item}}
|
|
||||||
@error={{error}}
|
|
||||||
/>
|
|
||||||
</BlockSlot>
|
|
||||||
<BlockSlot @name="header">
|
|
||||||
<h1>
|
|
||||||
Roles
|
|
||||||
</h1>
|
|
||||||
</BlockSlot>
|
|
||||||
<BlockSlot @name="actions">
|
|
||||||
<a data-test-create href="{{href-to 'dc.acls.roles.create'}}" class="type-create">Create</a>
|
|
||||||
</BlockSlot>
|
|
||||||
<BlockSlot @name="toolbar">
|
|
||||||
{{#if (gt items.length 0) }}
|
|
||||||
<Consul::Role::SearchBar
|
|
||||||
@search={{search}}
|
|
||||||
@onsearch={{action (mut search) value="target.value"}}
|
|
||||||
|
|
||||||
@sort={{sort}}
|
<AppView
|
||||||
|
@authorized={{isAuthorized}}
|
||||||
|
@enabled={{isEnabled}}
|
||||||
|
@login={{route.model.app.login.open}}
|
||||||
|
>
|
||||||
|
<BlockSlot @name="notification" as |status type item error|>
|
||||||
|
<Consul::Role::Notifications
|
||||||
|
@type={{type}}
|
||||||
|
@status={{status}}
|
||||||
|
@item={{item}}
|
||||||
|
@error={{error}}
|
||||||
|
/>
|
||||||
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="header">
|
||||||
|
<h1>
|
||||||
|
Roles
|
||||||
|
</h1>
|
||||||
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="actions">
|
||||||
|
<a data-test-create href="{{href-to 'dc.acls.roles.create'}}" class="type-create">Create</a>
|
||||||
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="toolbar">
|
||||||
|
{{#if (gt items.length 0) }}
|
||||||
|
<Consul::Role::SearchBar
|
||||||
|
@search={{search}}
|
||||||
|
@onsearch={{action (mut search) value="target.value"}}
|
||||||
|
|
||||||
@filter={{filters}}
|
@sort={{sort}}
|
||||||
/>
|
|
||||||
{{/if}}
|
@filter={{filters}}
|
||||||
</BlockSlot>
|
/>
|
||||||
<BlockSlot @name="content">
|
{{/if}}
|
||||||
<DataCollection
|
</BlockSlot>
|
||||||
@type="role"
|
<BlockSlot @name="content">
|
||||||
@sort={{sort.value}}
|
<DataCollection
|
||||||
@filters={{filters}}
|
@type="role"
|
||||||
@search={{search}}
|
@sort={{sort.value}}
|
||||||
@items={{items}}
|
@filters={{filters}}
|
||||||
as |collection|>
|
@search={{search}}
|
||||||
<collection.Collection>
|
@items={{items}}
|
||||||
<Consul::Role::List
|
as |collection|>
|
||||||
@items={{collection.items}}
|
<collection.Collection>
|
||||||
@ondelete={{route-action 'delete'}}
|
<Consul::Role::List
|
||||||
/>
|
@items={{collection.items}}
|
||||||
</collection.Collection>
|
@ondelete={{route-action 'delete'}}
|
||||||
<collection.Empty>
|
/>
|
||||||
<EmptyState @allowLogin={{true}}>
|
</collection.Collection>
|
||||||
<BlockSlot @name="header">
|
<collection.Empty>
|
||||||
<h2>
|
<EmptyState
|
||||||
{{#if (gt items.length 0)}}
|
@login={{route.model.app.login.open}}
|
||||||
No roles found
|
>
|
||||||
{{else}}
|
<BlockSlot @name="header">
|
||||||
Welcome to Roles
|
<h2>
|
||||||
{{/if}}
|
{{#if (gt items.length 0)}}
|
||||||
</h2>
|
No roles found
|
||||||
</BlockSlot>
|
{{else}}
|
||||||
<BlockSlot @name="body">
|
Welcome to Roles
|
||||||
<p>
|
{{/if}}
|
||||||
{{#if (gt items.length 0)}}
|
</h2>
|
||||||
No roles where found matching that search, or you may not have access to view the roles you are searching for.
|
</BlockSlot>
|
||||||
{{else}}
|
<BlockSlot @name="body">
|
||||||
There don't seem to be any roles, or you may not have access to view roles yet.
|
<p>
|
||||||
{{/if}}
|
{{#if (gt items.length 0)}}
|
||||||
</p>
|
No roles where found matching that search, or you may not have access to view the roles you are searching for.
|
||||||
</BlockSlot>
|
{{else}}
|
||||||
<BlockSlot @name="actions">
|
There don't seem to be any roles, or you may not have access to view roles yet.
|
||||||
<li class="docs-link">
|
{{/if}}
|
||||||
<a href="{{env 'CONSUL_DOCS_URL'}}/commands/acl/role" rel="noopener noreferrer" target="_blank">Documentation on roles</a>
|
</p>
|
||||||
</li>
|
</BlockSlot>
|
||||||
<li class="learn-link">
|
<BlockSlot @name="actions">
|
||||||
<a href="{{env 'CONSUL_DOCS_API_URL'}}/acl/roles.html" rel="noopener noreferrer" target="_blank">Read the API Docs</a>
|
<li class="docs-link">
|
||||||
</li>
|
<a href="{{env 'CONSUL_DOCS_URL'}}/commands/acl/role" rel="noopener noreferrer" target="_blank">Documentation on roles</a>
|
||||||
</BlockSlot>
|
</li>
|
||||||
</EmptyState>
|
<li class="learn-link">
|
||||||
</collection.Empty>
|
<a href="{{env 'CONSUL_DOCS_API_URL'}}/acl/roles.html" rel="noopener noreferrer" target="_blank">Read the API Docs</a>
|
||||||
</DataCollection>
|
</li>
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
</AppView>
|
</EmptyState>
|
||||||
{{/let}}
|
</collection.Empty>
|
||||||
|
</DataCollection>
|
||||||
|
</BlockSlot>
|
||||||
|
</AppView>
|
||||||
|
{{/let}}
|
||||||
|
</Route>
|
|
@ -1,98 +1,95 @@
|
||||||
{{#if isAuthorized }}
|
<Route
|
||||||
{{#if create}}
|
@name={{routeName}}
|
||||||
{{page-title 'New Token'}}
|
@title={{if isAuthorized (if create 'New Token' 'Edit Token') 'Access Controls'}}
|
||||||
{{else}}
|
as |route|>
|
||||||
{{page-title 'Edit Token'}}
|
<AppView
|
||||||
{{/if}}
|
@authorized={{isAuthorized}}
|
||||||
{{else}}
|
@enabled={{isEnabled}}
|
||||||
{{page-title 'Access Controls'}}
|
@login={{route.model.app.login.open}}
|
||||||
{{/if}}
|
|
||||||
<AppView
|
|
||||||
@authorized={{isAuthorized}}
|
|
||||||
@enabled={{isEnabled}}
|
|
||||||
>
|
>
|
||||||
<BlockSlot @name="notification" as |status type item error|>
|
<BlockSlot @name="notification" as |status type item error|>
|
||||||
<Consul::Token::Notifications
|
<Consul::Token::Notifications
|
||||||
@type={{type}}
|
@type={{type}}
|
||||||
@status={{status}}
|
@status={{status}}
|
||||||
@item={{item}}
|
@item={{item}}
|
||||||
@error={{error}}
|
@error={{error}}
|
||||||
/>
|
/>
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
<BlockSlot @name="breadcrumbs">
|
<BlockSlot @name="breadcrumbs">
|
||||||
<ol>
|
<ol>
|
||||||
<li><a data-test-back href={{href-to 'dc.acls.tokens'}}>All Tokens</a></li>
|
<li><a data-test-back href={{href-to 'dc.acls.tokens'}}>All Tokens</a></li>
|
||||||
</ol>
|
</ol>
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
<BlockSlot @name="header">
|
<BlockSlot @name="header">
|
||||||
<h1>
|
<h1>
|
||||||
{{#if isAuthorized }}
|
{{#if isAuthorized }}
|
||||||
{{#if create }}
|
{{#if create }}
|
||||||
New Token
|
New Token
|
||||||
|
{{else}}
|
||||||
|
Edit Token
|
||||||
|
{{/if}}
|
||||||
{{else}}
|
{{else}}
|
||||||
Edit Token
|
Access Controls
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{else}}
|
</h1>
|
||||||
Access Controls
|
</BlockSlot>
|
||||||
{{/if}}
|
<BlockSlot @name="actions">
|
||||||
</h1>
|
{{#if (not create)}}
|
||||||
</BlockSlot>
|
{{#if (not-eq item.AccessorID token.AccessorID)}}
|
||||||
<BlockSlot @name="actions">
|
<ConfirmationDialog @message="Are you sure you want to use this ACL token?">
|
||||||
{{#if (not create)}}
|
<BlockSlot @name="action" as |confirm|>
|
||||||
{{#if (not-eq item.AccessorID token.AccessorID)}}
|
<button data-test-use type="button" {{ action confirm 'use' item }}>Use</button>
|
||||||
<ConfirmationDialog @message="Are you sure you want to use this ACL token?">
|
</BlockSlot>
|
||||||
<BlockSlot @name="action" as |confirm|>
|
<BlockSlot @name="dialog" as |execute cancel message|>
|
||||||
<button data-test-use type="button" {{ action confirm 'use' item }}>Use</button>
|
<p>
|
||||||
</BlockSlot>
|
{{message}}
|
||||||
<BlockSlot @name="dialog" as |execute cancel message|>
|
</p>
|
||||||
<p>
|
<button type="button" class="type-delete" {{action execute}}>Confirm Use</button>
|
||||||
{{message}}
|
<button type="button" class="type-cancel" {{action cancel}}>Cancel</button>
|
||||||
</p>
|
</BlockSlot>
|
||||||
<button type="button" class="type-delete" {{action execute}}>Confirm Use</button>
|
</ConfirmationDialog>
|
||||||
<button type="button" class="type-cancel" {{action cancel}}>Cancel</button>
|
{{/if}}
|
||||||
</BlockSlot>
|
{{#if (can "duplicate token" item=item)}}
|
||||||
</ConfirmationDialog>
|
<button data-test-clone type="button" {{ action "clone" item }}>Duplicate</button>
|
||||||
|
{{/if}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#if (can "duplicate token" item=item)}}
|
</BlockSlot>
|
||||||
<button data-test-clone type="button" {{ action "clone" item }}>Duplicate</button>
|
<BlockSlot @name="content">
|
||||||
|
{{#if (token/is-legacy item)}}
|
||||||
|
<Notice
|
||||||
|
@type="info"
|
||||||
|
as |notice|>
|
||||||
|
<notice.Header>
|
||||||
|
<h2>Update</h2>
|
||||||
|
</notice.Header>
|
||||||
|
<notice.Body>
|
||||||
|
<p>
|
||||||
|
We have upgraded our ACL system by allowing you to create reusable policies which you can then apply to tokens. Don't worry, even though this token was written in the old style, it is still valid. However, we do recommend upgrading your old tokens to the new style. Learn how in our <a href="{{env 'CONSUL_DOCS_URL'}}/guides/acl-migrate-tokens.html" target="_blank" rel="noopener noreferrer">documentation</a>.
|
||||||
|
</p>
|
||||||
|
</notice.Body>
|
||||||
|
</Notice>
|
||||||
|
{{/if}}
|
||||||
|
{{#if (not create) }}
|
||||||
|
<div class="definition-table">
|
||||||
|
<dl>
|
||||||
|
<dt>AccessorID</dt>
|
||||||
|
<dd>
|
||||||
|
<CopyButton @value={{item.AccessorID}} @name="AccessorID" @position="top-start" /> {{item.AccessorID}}
|
||||||
|
</dd>
|
||||||
|
<dt>Token</dt>
|
||||||
|
<dd>
|
||||||
|
<CopyButton @value={{item.SecretID}} @name="Token" @position="top-start" /> <SecretButton>{{item.SecretID}}</SecretButton>
|
||||||
|
</dd>
|
||||||
|
{{#if (and (not (token/is-legacy item)) (not create))}}
|
||||||
|
<dt>Scope</dt>
|
||||||
|
<dd>
|
||||||
|
{{if item.Local 'local' 'global' }}
|
||||||
|
</dd>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{/if}}
|
</dl>
|
||||||
</BlockSlot>
|
</div>
|
||||||
<BlockSlot @name="content">
|
{{/if}}
|
||||||
{{#if (token/is-legacy item)}}
|
{{ partial 'dc/acls/tokens/form'}}
|
||||||
<Notice
|
</BlockSlot>
|
||||||
@type="info"
|
</AppView>
|
||||||
as |notice|>
|
</Route>
|
||||||
<notice.Header>
|
|
||||||
<h2>Update</h2>
|
|
||||||
</notice.Header>
|
|
||||||
<notice.Body>
|
|
||||||
<p>
|
|
||||||
We have upgraded our ACL system by allowing you to create reusable policies which you can then apply to tokens. Don't worry, even though this token was written in the old style, it is still valid. However, we do recommend upgrading your old tokens to the new style. Learn how in our <a href="{{env 'CONSUL_DOCS_URL'}}/guides/acl-migrate-tokens.html" target="_blank" rel="noopener noreferrer">documentation</a>.
|
|
||||||
</p>
|
|
||||||
</notice.Body>
|
|
||||||
</Notice>
|
|
||||||
{{/if}}
|
|
||||||
{{#if (not create) }}
|
|
||||||
<div class="definition-table">
|
|
||||||
<dl>
|
|
||||||
<dt>AccessorID</dt>
|
|
||||||
<dd>
|
|
||||||
<CopyButton @value={{item.AccessorID}} @name="AccessorID" @position="top-start" /> {{item.AccessorID}}
|
|
||||||
</dd>
|
|
||||||
<dt>Token</dt>
|
|
||||||
<dd>
|
|
||||||
<CopyButton @value={{item.SecretID}} @name="Token" @position="top-start" /> <SecretButton>{{item.SecretID}}</SecretButton>
|
|
||||||
</dd>
|
|
||||||
{{#if (and (not (token/is-legacy item)) (not create))}}
|
|
||||||
<dt>Scope</dt>
|
|
||||||
<dd>
|
|
||||||
{{if item.Local 'local' 'global' }}
|
|
||||||
</dd>
|
|
||||||
{{/if}}
|
|
||||||
</dl>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
{{ partial 'dc/acls/tokens/form'}}
|
|
||||||
</BlockSlot>
|
|
||||||
</AppView>
|
|
|
@ -1,122 +1,124 @@
|
||||||
{{#if isAuthorized }}
|
<Route
|
||||||
{{page-title 'Tokens'}}
|
@name={{routeName}}
|
||||||
{{else}}
|
@title="Tokens"
|
||||||
{{page-title 'Access Controls'}}
|
as |route|>
|
||||||
{{/if}}
|
{{#let
|
||||||
|
|
||||||
{{#let
|
(hash
|
||||||
|
value=(or sortBy "CreateTime:desc")
|
||||||
(hash
|
change=(action (mut sortBy) value="target.selected")
|
||||||
value=(or sortBy "CreateTime:desc")
|
|
||||||
change=(action (mut sortBy) value="target.selected")
|
|
||||||
)
|
|
||||||
|
|
||||||
(hash
|
|
||||||
kind=(hash
|
|
||||||
value=(if kind (split kind ',') undefined)
|
|
||||||
change=(action (mut kind) value="target.selectedItems")
|
|
||||||
)
|
)
|
||||||
searchproperty=(hash
|
|
||||||
value=(if (not-eq searchproperty undefined)
|
(hash
|
||||||
(split searchproperty ',')
|
kind=(hash
|
||||||
searchProperties
|
value=(if kind (split kind ',') undefined)
|
||||||
|
change=(action (mut kind) value="target.selectedItems")
|
||||||
|
)
|
||||||
|
searchproperty=(hash
|
||||||
|
value=(if (not-eq searchproperty undefined)
|
||||||
|
(split searchproperty ',')
|
||||||
|
searchProperties
|
||||||
|
)
|
||||||
|
change=(action (mut searchproperty) value="target.selectedItems")
|
||||||
|
default=searchProperties
|
||||||
)
|
)
|
||||||
change=(action (mut searchproperty) value="target.selectedItems")
|
|
||||||
default=searchProperties
|
|
||||||
)
|
)
|
||||||
)
|
|
||||||
|
|
||||||
items
|
items
|
||||||
|
|
||||||
as |sort filters items|}}
|
as |sort filters items|}}
|
||||||
|
|
||||||
<AppView
|
<AppView
|
||||||
@authorized={{isAuthorized}}
|
@authorized={{isAuthorized}}
|
||||||
@enabled={{isEnabled}}
|
@enabled={{isEnabled}}
|
||||||
>
|
@login={{route.model.app.login.open}}
|
||||||
<BlockSlot @name="notification" as |status type item error|>
|
>
|
||||||
<Consul::Token::Notifications
|
<BlockSlot @name="notification" as |status type item error|>
|
||||||
@type={{type}}
|
<Consul::Token::Notifications
|
||||||
@status={{status}}
|
@type={{type}}
|
||||||
@item={{item}}
|
@status={{status}}
|
||||||
@error={{error}}
|
@item={{item}}
|
||||||
/>
|
@error={{error}}
|
||||||
</BlockSlot>
|
/>
|
||||||
<BlockSlot @name="header">
|
</BlockSlot>
|
||||||
<h1>
|
<BlockSlot @name="header">
|
||||||
Tokens
|
<h1>
|
||||||
</h1>
|
Tokens
|
||||||
</BlockSlot>
|
</h1>
|
||||||
<BlockSlot @name="actions">
|
</BlockSlot>
|
||||||
{{#if (can "create tokens")}}
|
<BlockSlot @name="actions">
|
||||||
<a data-test-create href="{{href-to 'dc.acls.tokens.create'}}" class="type-create">Create</a>
|
{{#if (can "create tokens")}}
|
||||||
{{/if}}
|
<a data-test-create href="{{href-to 'dc.acls.tokens.create'}}" class="type-create">Create</a>
|
||||||
</BlockSlot>
|
|
||||||
<BlockSlot @name="toolbar">
|
|
||||||
{{#if (gt items.length 0)}}
|
|
||||||
<Consul::Token::SearchBar
|
|
||||||
@search={{search}}
|
|
||||||
@onsearch={{action (mut search) value="target.value"}}
|
|
||||||
|
|
||||||
@sort={{sort}}
|
|
||||||
|
|
||||||
@filter={{filters}}
|
|
||||||
/>
|
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
<BlockSlot @name="content">
|
<BlockSlot @name="toolbar">
|
||||||
{{#if (token/is-legacy items)}}
|
{{#if (gt items.length 0)}}
|
||||||
<Notice
|
<Consul::Token::SearchBar
|
||||||
@type="info"
|
@search={{search}}
|
||||||
as |notice|>
|
@onsearch={{action (mut search) value="target.value"}}
|
||||||
<notice.Header>
|
|
||||||
<h2>Update</h2>
|
@sort={{sort}}
|
||||||
</notice.Header>
|
|
||||||
<notice.Body>
|
@filter={{filters}}
|
||||||
<p data-test-notification-update>We have upgraded our ACL System to allow the creation of reusable policies that can be applied to tokens. Read more about the changes and how to upgrade legacy tokens in our <a href="{{env 'CONSUL_DOCS_URL'}}/guides/acl-migrate-tokens.html" target="_blank" rel="noopener noreferrer">documentation</a>.</p>
|
/>
|
||||||
</notice.Body>
|
{{/if}}
|
||||||
</Notice>
|
</BlockSlot>
|
||||||
{{/if}}
|
<BlockSlot @name="content">
|
||||||
<DataCollection
|
{{#if (token/is-legacy items)}}
|
||||||
@type="token"
|
<Notice
|
||||||
@sort={{sort.value}}
|
@type="info"
|
||||||
@filters={{filters}}
|
as |notice|>
|
||||||
@search={{search}}
|
<notice.Header>
|
||||||
@items={{items}}
|
<h2>Update</h2>
|
||||||
as |collection|>
|
</notice.Header>
|
||||||
<collection.Collection>
|
<notice.Body>
|
||||||
<Consul::Token::List
|
<p data-test-notification-update>We have upgraded our ACL System to allow the creation of reusable policies that can be applied to tokens. Read more about the changes and how to upgrade legacy tokens in our <a href="{{env 'CONSUL_DOCS_URL'}}/guides/acl-migrate-tokens.html" target="_blank" rel="noopener noreferrer">documentation</a>.</p>
|
||||||
@items={{collection.items}}
|
</notice.Body>
|
||||||
@token={{token}}
|
</Notice>
|
||||||
@onuse={{route-action 'use'}}
|
{{/if}}
|
||||||
@ondelete={{route-action 'delete'}}
|
<DataCollection
|
||||||
@onlogout={{route-action 'logout'}}
|
@type="token"
|
||||||
@onclone={{route-action 'clone'}}
|
@sort={{sort.value}}
|
||||||
/>
|
@filters={{filters}}
|
||||||
</collection.Collection>
|
@search={{search}}
|
||||||
<collection.Empty>
|
@items={{items}}
|
||||||
<EmptyState @allowLogin={{true}}>
|
as |collection|>
|
||||||
<BlockSlot @name="header">
|
<collection.Collection>
|
||||||
<h2>
|
<Consul::Token::List
|
||||||
{{#if (gt items.length 0)}}
|
@items={{collection.items}}
|
||||||
No tokens found
|
@token={{token}}
|
||||||
{{else}}
|
@onuse={{route-action 'use'}}
|
||||||
Welcome to ACL Tokens
|
@ondelete={{route-action 'delete'}}
|
||||||
{{/if}}
|
@onlogout={{route-action 'logout'}}
|
||||||
</h2>
|
@onclone={{route-action 'clone'}}
|
||||||
</BlockSlot>
|
/>
|
||||||
<BlockSlot @name="body">
|
</collection.Collection>
|
||||||
<p>
|
<collection.Empty>
|
||||||
{{#if (gt items.length 0)}}
|
<EmptyState
|
||||||
No tokens where found matching that search, or you may not have access to view the tokens you are searching for.
|
@login={{route.model.app.login.open}}
|
||||||
{{else}}
|
>
|
||||||
There don't seem to be any tokens, or you may not have access to view tokens yet.
|
<BlockSlot @name="header">
|
||||||
{{/if}}
|
<h2>
|
||||||
</p>
|
{{#if (gt items.length 0)}}
|
||||||
</BlockSlot>
|
No tokens found
|
||||||
</EmptyState>
|
{{else}}
|
||||||
</collection.Empty>
|
Welcome to ACL Tokens
|
||||||
</DataCollection>
|
{{/if}}
|
||||||
</BlockSlot>
|
</h2>
|
||||||
</AppView>
|
</BlockSlot>
|
||||||
{{/let}}
|
<BlockSlot @name="body">
|
||||||
|
<p>
|
||||||
|
{{#if (gt items.length 0)}}
|
||||||
|
No tokens where found matching that search, or you may not have access to view the tokens you are searching for.
|
||||||
|
{{else}}
|
||||||
|
There don't seem to be any tokens, or you may not have access to view tokens yet.
|
||||||
|
{{/if}}
|
||||||
|
</p>
|
||||||
|
</BlockSlot>
|
||||||
|
</EmptyState>
|
||||||
|
</collection.Empty>
|
||||||
|
</DataCollection>
|
||||||
|
</BlockSlot>
|
||||||
|
</AppView>
|
||||||
|
{{/let}}
|
||||||
|
</Route>
|
||||||
|
|
|
@ -1,33 +1,33 @@
|
||||||
{{#if item.ID}}
|
<Route
|
||||||
{{page-title 'Edit Intention'}}
|
@name={{routeName}}
|
||||||
{{else}}
|
@title={{if item.ID 'Edit Intention' 'New Intention'}}
|
||||||
{{page-title 'New Intention'}}
|
as |route|>
|
||||||
{{/if}}
|
<AppView>
|
||||||
<AppView>
|
<BlockSlot @name="breadcrumbs">
|
||||||
<BlockSlot @name="breadcrumbs">
|
<ol>
|
||||||
<ol>
|
<li><a data-test-back href={{href-to 'dc.intentions'}}>All Intentions</a></li>
|
||||||
<li><a data-test-back href={{href-to 'dc.intentions'}}>All Intentions</a></li>
|
</ol>
|
||||||
</ol>
|
</BlockSlot>
|
||||||
</BlockSlot>
|
<BlockSlot @name="header">
|
||||||
<BlockSlot @name="header">
|
<h1>
|
||||||
<h1>
|
{{#if item.IsEditable}}
|
||||||
{{#if item.IsEditable}}
|
{{#if item.ID}}
|
||||||
{{#if item.ID}}
|
Edit Intention
|
||||||
Edit Intention
|
{{else}}
|
||||||
|
New Intention
|
||||||
|
{{/if}}
|
||||||
{{else}}
|
{{else}}
|
||||||
New Intention
|
View Intention
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{else}}
|
</h1>
|
||||||
View Intention
|
</BlockSlot>
|
||||||
{{/if}}
|
<BlockSlot @name="content">
|
||||||
</h1>
|
<Consul::Intention::Form
|
||||||
</BlockSlot>
|
@item={{item}}
|
||||||
<BlockSlot @name="content">
|
@dc={{dc}}
|
||||||
<Consul::Intention::Form
|
@nspace={{nspace}}
|
||||||
@item={{item}}
|
@onsubmit={{transition-to 'dc.intentions.index' dc}}
|
||||||
@dc={{dc}}
|
/>
|
||||||
@nspace={{nspace}}
|
</BlockSlot>
|
||||||
@onsubmit={{transition-to 'dc.intentions.index' dc}}
|
</AppView>
|
||||||
/>
|
</Route>
|
||||||
</BlockSlot>
|
|
||||||
</AppView>
|
|
|
@ -1,122 +1,131 @@
|
||||||
{{page-title 'Intentions'}}
|
<Route
|
||||||
<DataLoader @src={{concat '/' nspace '/' dc '/intentions'}} as |api|>
|
@name={{routeName}}
|
||||||
|
@title="Intentions"
|
||||||
|
as |route|>
|
||||||
|
<DataLoader @src={{concat '/' nspace '/' dc '/intentions'}} as |api|>
|
||||||
|
|
||||||
<BlockSlot @name="error">
|
<BlockSlot @name="error">
|
||||||
<AppError @error={{api.error}} />
|
<AppError
|
||||||
</BlockSlot>
|
@error={{api.error}}
|
||||||
|
@login={{route.model.app.login.open}}
|
||||||
<BlockSlot @name="loaded">
|
|
||||||
{{#let
|
|
||||||
|
|
||||||
(hash
|
|
||||||
value=(or sortBy "Action:asc")
|
|
||||||
change=(action (mut sortBy) value="target.selected")
|
|
||||||
)
|
|
||||||
|
|
||||||
(hash
|
|
||||||
access=(hash
|
|
||||||
value=(if access (split access ',') undefined)
|
|
||||||
change=(action (mut access) value="target.selectedItems")
|
|
||||||
)
|
|
||||||
searchproperty=(hash
|
|
||||||
value=(if (not-eq searchproperty undefined)
|
|
||||||
(split searchproperty ',')
|
|
||||||
searchProperties
|
|
||||||
)
|
|
||||||
change=(action (mut searchproperty) value="target.selectedItems")
|
|
||||||
default=searchProperties
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
api.data
|
|
||||||
|
|
||||||
as |sort filters items|}}
|
|
||||||
|
|
||||||
<AppView>
|
|
||||||
<BlockSlot @name="header">
|
|
||||||
<h1>
|
|
||||||
Intentions <em>{{format-number items.length}} total</em>
|
|
||||||
</h1>
|
|
||||||
<label for="toolbar-toggle"></label>
|
|
||||||
</BlockSlot>
|
|
||||||
<BlockSlot @name="actions">
|
|
||||||
{{#if (can 'create intentions')}}
|
|
||||||
<a data-test-create href="{{href-to 'dc.intentions.create'}}" class="type-create">Create</a>
|
|
||||||
{{/if}}
|
|
||||||
</BlockSlot>
|
|
||||||
<BlockSlot @name="toolbar">
|
|
||||||
|
|
||||||
{{#if (gt items.length 0) }}
|
|
||||||
<Consul::Intention::SearchBar
|
|
||||||
@search={{search}}
|
|
||||||
@onsearch={{action (mut search) value="target.value"}}
|
|
||||||
|
|
||||||
@sort={{sort}}
|
|
||||||
|
|
||||||
@filter={{filters}}
|
|
||||||
/>
|
/>
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
<BlockSlot @name="content">
|
|
||||||
<DataWriter
|
|
||||||
@sink={{concat '/' dc '/' nspace '/intention/'}}
|
|
||||||
@type="intention"
|
|
||||||
@ondelete={{refresh-route}}
|
|
||||||
as |writer|>
|
|
||||||
<BlockSlot @name="content">
|
|
||||||
<DataCollection
|
|
||||||
@type="intention"
|
|
||||||
@sort={{sort.value}}
|
|
||||||
@filters={{filters}}
|
|
||||||
@search={{search}}
|
|
||||||
@items={{items}}
|
|
||||||
as |collection|>
|
|
||||||
<collection.Collection>
|
|
||||||
<Consul::Intention::List
|
|
||||||
@items={{collection.items}}
|
|
||||||
@delete={{writer.delete}}
|
|
||||||
as |list|>
|
|
||||||
<list.CustomResourceNotice />
|
|
||||||
<list.Table />
|
|
||||||
</Consul::Intention::List>
|
|
||||||
</collection.Collection>
|
|
||||||
<collection.Empty>
|
|
||||||
<EmptyState @allowLogin={{true}}>
|
|
||||||
<BlockSlot @name="header">
|
|
||||||
<h2>
|
|
||||||
{{#if (gt items.length 0)}}
|
|
||||||
No intentions found
|
|
||||||
{{else}}
|
|
||||||
Welcome to Intentions
|
|
||||||
{{/if}}
|
|
||||||
</h2>
|
|
||||||
</BlockSlot>
|
|
||||||
<BlockSlot @name="body">
|
|
||||||
<p>
|
|
||||||
{{#if (gt items.length 0)}}
|
|
||||||
No intentions where found matching that search, or you may not have access to view the intentions you are searching for.
|
|
||||||
{{else}}
|
|
||||||
There don't seem to be any intentions, or you may not have access to view intentions yet.
|
|
||||||
{{/if}}
|
|
||||||
</p>
|
|
||||||
</BlockSlot>
|
|
||||||
<BlockSlot @name="actions">
|
|
||||||
<li class="docs-link">
|
|
||||||
<a href="{{env 'CONSUL_DOCS_URL'}}/commands/intention" rel="noopener noreferrer" target="_blank">Documentation on intentions</a>
|
|
||||||
</li>
|
|
||||||
<li class="learn-link">
|
|
||||||
<a href="{{env 'CONSUL_DOCS_LEARN_URL'}}/consul/getting-started/connect" rel="noopener noreferrer" target="_blank">Read the guide</a>
|
|
||||||
</li>
|
|
||||||
</BlockSlot>
|
|
||||||
</EmptyState>
|
|
||||||
</collection.Empty>
|
|
||||||
</DataCollection>
|
|
||||||
</BlockSlot>
|
|
||||||
</DataWriter>
|
|
||||||
</BlockSlot>
|
|
||||||
</AppView>
|
|
||||||
|
|
||||||
{{/let}}
|
<BlockSlot @name="loaded">
|
||||||
</BlockSlot>
|
{{#let
|
||||||
</DataLoader>
|
|
||||||
|
(hash
|
||||||
|
value=(or sortBy "Action:asc")
|
||||||
|
change=(action (mut sortBy) value="target.selected")
|
||||||
|
)
|
||||||
|
|
||||||
|
(hash
|
||||||
|
access=(hash
|
||||||
|
value=(if access (split access ',') undefined)
|
||||||
|
change=(action (mut access) value="target.selectedItems")
|
||||||
|
)
|
||||||
|
searchproperty=(hash
|
||||||
|
value=(if (not-eq searchproperty undefined)
|
||||||
|
(split searchproperty ',')
|
||||||
|
searchProperties
|
||||||
|
)
|
||||||
|
change=(action (mut searchproperty) value="target.selectedItems")
|
||||||
|
default=searchProperties
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
api.data
|
||||||
|
|
||||||
|
as |sort filters items|}}
|
||||||
|
|
||||||
|
<AppView>
|
||||||
|
<BlockSlot @name="header">
|
||||||
|
<h1>
|
||||||
|
Intentions <em>{{format-number items.length}} total</em>
|
||||||
|
</h1>
|
||||||
|
<label for="toolbar-toggle"></label>
|
||||||
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="actions">
|
||||||
|
{{#if (can 'create intentions')}}
|
||||||
|
<a data-test-create href="{{href-to 'dc.intentions.create'}}" class="type-create">Create</a>
|
||||||
|
{{/if}}
|
||||||
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="toolbar">
|
||||||
|
|
||||||
|
{{#if (gt items.length 0) }}
|
||||||
|
<Consul::Intention::SearchBar
|
||||||
|
@search={{search}}
|
||||||
|
@onsearch={{action (mut search) value="target.value"}}
|
||||||
|
|
||||||
|
@sort={{sort}}
|
||||||
|
|
||||||
|
@filter={{filters}}
|
||||||
|
/>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="content">
|
||||||
|
<DataWriter
|
||||||
|
@sink={{concat '/' dc '/' nspace '/intention/'}}
|
||||||
|
@type="intention"
|
||||||
|
@ondelete={{refresh-route}}
|
||||||
|
as |writer|>
|
||||||
|
<BlockSlot @name="content">
|
||||||
|
<DataCollection
|
||||||
|
@type="intention"
|
||||||
|
@sort={{sort.value}}
|
||||||
|
@filters={{filters}}
|
||||||
|
@search={{search}}
|
||||||
|
@items={{items}}
|
||||||
|
as |collection|>
|
||||||
|
<collection.Collection>
|
||||||
|
<Consul::Intention::List
|
||||||
|
@items={{collection.items}}
|
||||||
|
@delete={{writer.delete}}
|
||||||
|
as |list|>
|
||||||
|
<list.CustomResourceNotice />
|
||||||
|
<list.Table />
|
||||||
|
</Consul::Intention::List>
|
||||||
|
</collection.Collection>
|
||||||
|
<collection.Empty>
|
||||||
|
<EmptyState
|
||||||
|
@login={{route.model.app.login.open}}
|
||||||
|
>
|
||||||
|
<BlockSlot @name="header">
|
||||||
|
<h2>
|
||||||
|
{{#if (gt items.length 0)}}
|
||||||
|
No intentions found
|
||||||
|
{{else}}
|
||||||
|
Welcome to Intentions
|
||||||
|
{{/if}}
|
||||||
|
</h2>
|
||||||
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="body">
|
||||||
|
<p>
|
||||||
|
{{#if (gt items.length 0)}}
|
||||||
|
No intentions where found matching that search, or you may not have access to view the intentions you are searching for.
|
||||||
|
{{else}}
|
||||||
|
There don't seem to be any intentions, or you may not have access to view intentions yet.
|
||||||
|
{{/if}}
|
||||||
|
</p>
|
||||||
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="actions">
|
||||||
|
<li class="docs-link">
|
||||||
|
<a href="{{env 'CONSUL_DOCS_URL'}}/commands/intention" rel="noopener noreferrer" target="_blank">Documentation on intentions</a>
|
||||||
|
</li>
|
||||||
|
<li class="learn-link">
|
||||||
|
<a href="{{env 'CONSUL_DOCS_LEARN_URL'}}/consul/getting-started/connect" rel="noopener noreferrer" target="_blank">Read the guide</a>
|
||||||
|
</li>
|
||||||
|
</BlockSlot>
|
||||||
|
</EmptyState>
|
||||||
|
</collection.Empty>
|
||||||
|
</DataCollection>
|
||||||
|
</BlockSlot>
|
||||||
|
</DataWriter>
|
||||||
|
</BlockSlot>
|
||||||
|
</AppView>
|
||||||
|
|
||||||
|
{{/let}}
|
||||||
|
</BlockSlot>
|
||||||
|
</DataLoader>
|
||||||
|
</Route>
|
|
@ -1,57 +1,57 @@
|
||||||
{{#if item.Key }}
|
<Route
|
||||||
{{page-title 'Edit Key/Value'}}
|
@name={{routeName}}
|
||||||
{{else}}
|
@title={{if item.Key 'Edit Key/Value' 'New Key/Value'}}
|
||||||
{{page-title 'New Key/Value'}}
|
as |route|>
|
||||||
{{/if}}
|
<AppView>
|
||||||
<AppView>
|
<BlockSlot @name="breadcrumbs">
|
||||||
<BlockSlot @name="breadcrumbs">
|
<ol>
|
||||||
<ol>
|
<li><a data-test-back href={{href-to 'dc.kv.index'}}>Key / Values</a></li>
|
||||||
<li><a data-test-back href={{href-to 'dc.kv.index'}}>Key / Values</a></li>
|
{{#if (not-eq parent.Key '/')}}
|
||||||
{{#if (not-eq parent.Key '/')}}
|
{{#each (slice 0 -1 (split parent.Key '/')) as |breadcrumb index|}}
|
||||||
{{#each (slice 0 -1 (split parent.Key '/')) as |breadcrumb index|}}
|
<li><a href={{href-to 'dc.kv.folder' (join '/' (append (slice 0 (add index 1) (split parent.Key '/')) ''))}}>{{breadcrumb}}</a></li>
|
||||||
<li><a href={{href-to 'dc.kv.folder' (join '/' (append (slice 0 (add index 1) (split parent.Key '/')) ''))}}>{{breadcrumb}}</a></li>
|
{{/each}}
|
||||||
{{/each}}
|
|
||||||
{{/if}}
|
|
||||||
</ol>
|
|
||||||
</BlockSlot>
|
|
||||||
<BlockSlot @name="header">
|
|
||||||
<h1>
|
|
||||||
{{#if item.Key}}
|
|
||||||
{{left-trim item.Key parent.Key}}
|
|
||||||
{{else}}
|
|
||||||
New Key / Value
|
|
||||||
{{/if}}
|
|
||||||
</h1>
|
|
||||||
</BlockSlot>
|
|
||||||
<BlockSlot @name="content">
|
|
||||||
{{! if a KV has a session `Session` will always be populated despite any specific session permissions }}
|
|
||||||
{{#if item.Session}}
|
|
||||||
<Notice
|
|
||||||
@type="warning"
|
|
||||||
data-test-session-warning
|
|
||||||
as |notice|>
|
|
||||||
<notice.Body>
|
|
||||||
<p>
|
|
||||||
<strong>Warning.</strong> This KV has a lock session. You can edit KV's with lock sessions, but we recommend doing so with care, or not doing so at all. It may negatively impact the active node it's associated with. See below for more details on the Lock Session and see <a href="{{env 'CONSUL_DOCS_URL'}}/internals/sessions.html" target="_blank" rel="noopener noreferrer">our documentation</a> for more information.
|
|
||||||
</p>
|
|
||||||
</notice.Body>
|
|
||||||
</Notice>
|
|
||||||
{{/if}}
|
{{/if}}
|
||||||
<Consul::Kv::Form
|
</ol>
|
||||||
@item={{item}}
|
</BlockSlot>
|
||||||
@dc={{dc}}
|
<BlockSlot @name="header">
|
||||||
@nspace={{nspace}}
|
<h1>
|
||||||
@onsubmit={{if (eq parent.Key '/') (transition-to 'dc.kv.index') (transition-to 'dc.kv.folder' parent.Key)}}
|
{{#if item.Key}}
|
||||||
@parent={{parent}}
|
{{left-trim item.Key parent.Key}}
|
||||||
/>
|
{{else}}
|
||||||
{{! session is slightly different to item.Session as we only have session if you have session:read perms}}
|
New Key / Value
|
||||||
{{#if session}}
|
|
||||||
<Consul::LockSession::Form
|
|
||||||
@item={{session}}
|
|
||||||
@dc={{dc}}
|
|
||||||
@nspace={{nspace}}
|
|
||||||
@onsubmit={{action (mut session) undefined}}
|
|
||||||
/>
|
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</BlockSlot>
|
</h1>
|
||||||
</AppView>
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="content">
|
||||||
|
{{! if a KV has a session `Session` will always be populated despite any specific session permissions }}
|
||||||
|
{{#if item.Session}}
|
||||||
|
<Notice
|
||||||
|
@type="warning"
|
||||||
|
data-test-session-warning
|
||||||
|
as |notice|>
|
||||||
|
<notice.Body>
|
||||||
|
<p>
|
||||||
|
<strong>Warning.</strong> This KV has a lock session. You can edit KV's with lock sessions, but we recommend doing so with care, or not doing so at all. It may negatively impact the active node it's associated with. See below for more details on the Lock Session and see <a href="{{env 'CONSUL_DOCS_URL'}}/internals/sessions.html" target="_blank" rel="noopener noreferrer">our documentation</a> for more information.
|
||||||
|
</p>
|
||||||
|
</notice.Body>
|
||||||
|
</Notice>
|
||||||
|
{{/if}}
|
||||||
|
<Consul::Kv::Form
|
||||||
|
@item={{item}}
|
||||||
|
@dc={{dc}}
|
||||||
|
@nspace={{nspace}}
|
||||||
|
@onsubmit={{if (eq parent.Key '/') (transition-to 'dc.kv.index') (transition-to 'dc.kv.folder' parent.Key)}}
|
||||||
|
@parent={{parent}}
|
||||||
|
/>
|
||||||
|
{{! session is slightly different to item.Session as we only have session if you have session:read perms}}
|
||||||
|
{{#if session}}
|
||||||
|
<Consul::LockSession::Form
|
||||||
|
@item={{session}}
|
||||||
|
@dc={{dc}}
|
||||||
|
@nspace={{nspace}}
|
||||||
|
@onsubmit={{action (mut session) undefined}}
|
||||||
|
/>
|
||||||
|
{{/if}}
|
||||||
|
</BlockSlot>
|
||||||
|
</AppView>
|
||||||
|
</Route>
|
|
@ -1,4 +1,7 @@
|
||||||
{{page-title 'Key/Value'}}
|
<Route
|
||||||
|
@name={{routeName}}
|
||||||
|
@title="Key/Value"
|
||||||
|
as |route|>
|
||||||
{{#let
|
{{#let
|
||||||
|
|
||||||
(hash
|
(hash
|
||||||
|
@ -81,7 +84,9 @@ as |sort filters items|}}
|
||||||
/>
|
/>
|
||||||
</collection.Collection>
|
</collection.Collection>
|
||||||
<collection.Empty>
|
<collection.Empty>
|
||||||
<EmptyState @allowLogin={{true}}>
|
<EmptyState
|
||||||
|
@login={{route.model.app.login.open}}
|
||||||
|
>
|
||||||
<BlockSlot @name="header">
|
<BlockSlot @name="header">
|
||||||
<h2>
|
<h2>
|
||||||
{{#if (gt items.length 0)}}
|
{{#if (gt items.length 0)}}
|
||||||
|
@ -115,4 +120,5 @@ as |sort filters items|}}
|
||||||
</DataWriter>
|
</DataWriter>
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
</AppView>
|
</AppView>
|
||||||
{{/let}}
|
{{/let}}
|
||||||
|
</Route>
|
|
@ -1,74 +1,78 @@
|
||||||
{{page-title 'Nodes'}}
|
<Route
|
||||||
<EventSource @src={{items}} />
|
@name={{routeName}}
|
||||||
<EventSource @src={{leader}} />
|
@title="Nodes"
|
||||||
{{#let
|
as |route|>
|
||||||
|
<EventSource @src={{items}} />
|
||||||
|
<EventSource @src={{leader}} />
|
||||||
|
{{#let
|
||||||
|
|
||||||
(hash
|
(hash
|
||||||
value=(or sortBy "Status:asc")
|
value=(or sortBy "Status:asc")
|
||||||
change=(action (mut sortBy) value="target.selected")
|
change=(action (mut sortBy) value="target.selected")
|
||||||
)
|
|
||||||
|
|
||||||
(hash
|
|
||||||
status=(hash
|
|
||||||
value=(if status (split status ',') undefined)
|
|
||||||
change=(action (mut status) value="target.selectedItems")
|
|
||||||
)
|
)
|
||||||
searchproperty=(hash
|
|
||||||
value=(if (not-eq searchproperty undefined)
|
(hash
|
||||||
(split searchproperty ',')
|
status=(hash
|
||||||
searchProperties
|
value=(if status (split status ',') undefined)
|
||||||
|
change=(action (mut status) value="target.selectedItems")
|
||||||
|
)
|
||||||
|
searchproperty=(hash
|
||||||
|
value=(if (not-eq searchproperty undefined)
|
||||||
|
(split searchproperty ',')
|
||||||
|
searchProperties
|
||||||
|
)
|
||||||
|
change=(action (mut searchproperty) value="target.selectedItems")
|
||||||
|
default=searchProperties
|
||||||
)
|
)
|
||||||
change=(action (mut searchproperty) value="target.selectedItems")
|
|
||||||
default=searchProperties
|
|
||||||
)
|
)
|
||||||
)
|
|
||||||
|
|
||||||
items
|
items
|
||||||
|
|
||||||
as |sort filters items|}}
|
as |sort filters items|}}
|
||||||
<AppView>
|
<AppView>
|
||||||
<BlockSlot @name="header">
|
<BlockSlot @name="header">
|
||||||
<h1>
|
<h1>
|
||||||
Nodes <em>{{format-number items.length}} total</em>
|
Nodes <em>{{format-number items.length}} total</em>
|
||||||
</h1>
|
</h1>
|
||||||
<label for="toolbar-toggle"></label>
|
<label for="toolbar-toggle"></label>
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
<BlockSlot @name="toolbar">
|
<BlockSlot @name="toolbar">
|
||||||
{{#if (gt items.length 0) }}
|
{{#if (gt items.length 0) }}
|
||||||
<Consul::Node::SearchBar
|
<Consul::Node::SearchBar
|
||||||
@search={{search}}
|
@search={{search}}
|
||||||
@onsearch={{action (mut search) value="target.value"}}
|
@onsearch={{action (mut search) value="target.value"}}
|
||||||
|
|
||||||
@sort={{sort}}
|
@sort={{sort}}
|
||||||
|
|
||||||
@filter={{filters}}
|
@filter={{filters}}
|
||||||
/>
|
|
||||||
{{/if}}
|
|
||||||
</BlockSlot>
|
|
||||||
<BlockSlot @name="content">
|
|
||||||
<DataCollection
|
|
||||||
@type="node"
|
|
||||||
@sort={{sort.value}}
|
|
||||||
@filters={{filters}}
|
|
||||||
@search={{search}}
|
|
||||||
@items={{items}}
|
|
||||||
as |collection|>
|
|
||||||
<collection.Collection>
|
|
||||||
<Consul::Node::List
|
|
||||||
@items={{collection.items}}
|
|
||||||
@leader={{leader}}
|
|
||||||
/>
|
/>
|
||||||
</collection.Collection>
|
{{/if}}
|
||||||
<collection.Empty>
|
</BlockSlot>
|
||||||
<EmptyState>
|
<BlockSlot @name="content">
|
||||||
<BlockSlot @name="body">
|
<DataCollection
|
||||||
<p>
|
@type="node"
|
||||||
There don't seem to be any registered nodes, or you may not have access to view nodes yet.
|
@sort={{sort.value}}
|
||||||
</p>
|
@filters={{filters}}
|
||||||
</BlockSlot>
|
@search={{search}}
|
||||||
</EmptyState>
|
@items={{items}}
|
||||||
</collection.Empty>
|
as |collection|>
|
||||||
</DataCollection>
|
<collection.Collection>
|
||||||
</BlockSlot>
|
<Consul::Node::List
|
||||||
</AppView>
|
@items={{collection.items}}
|
||||||
{{/let}}
|
@leader={{leader}}
|
||||||
|
/>
|
||||||
|
</collection.Collection>
|
||||||
|
<collection.Empty>
|
||||||
|
<EmptyState>
|
||||||
|
<BlockSlot @name="body">
|
||||||
|
<p>
|
||||||
|
There don't seem to be any registered nodes, or you may not have access to view nodes yet.
|
||||||
|
</p>
|
||||||
|
</BlockSlot>
|
||||||
|
</EmptyState>
|
||||||
|
</collection.Empty>
|
||||||
|
</DataCollection>
|
||||||
|
</BlockSlot>
|
||||||
|
</AppView>
|
||||||
|
{{/let}}
|
||||||
|
</Route>
|
|
@ -1,82 +1,90 @@
|
||||||
{{page-title item.Node}}
|
<Route
|
||||||
<DataLoader as |loader|>
|
@name={{routeName}}
|
||||||
|
@title={{item.Node}}
|
||||||
|
as |route|>
|
||||||
|
<DataLoader as |loader|>
|
||||||
|
|
||||||
<BlockSlot @name="data">
|
<BlockSlot @name="data">
|
||||||
<EventSource @src={{item}} @onerror={{action loader.dispatchError}} />
|
<EventSource @src={{item}} @onerror={{action loader.dispatchError}} />
|
||||||
<EventSource @src={{tomography}} />
|
<EventSource @src={{tomography}} />
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
|
|
||||||
<BlockSlot @name="error">
|
<BlockSlot @name="error">
|
||||||
<AppError @error={{loader.error}} />
|
<AppError
|
||||||
</BlockSlot>
|
@error={{loader.error}}
|
||||||
|
@login={{route.model.app.login.open}}
|
||||||
|
/>
|
||||||
|
</BlockSlot>
|
||||||
|
|
||||||
<BlockSlot @name="disconnected" as |Notification|>
|
<BlockSlot @name="disconnected" as |Notification|>
|
||||||
{{#if (eq loader.error.status "404")}}
|
{{#if (eq loader.error.status "404")}}
|
||||||
<Notification @sticky={{true}}>
|
<Notification @sticky={{true}}>
|
||||||
<p data-notification role="alert" class="warning notification-update">
|
<p data-notification role="alert" class="warning notification-update">
|
||||||
<strong>Warning!</strong>
|
<strong>Warning!</strong>
|
||||||
This node no longer exists in the catalog.
|
This node no longer exists in the catalog.
|
||||||
</p>
|
</p>
|
||||||
</Notification>
|
</Notification>
|
||||||
{{else if (eq loader.error.status "403")}}
|
{{else if (eq loader.error.status "403")}}
|
||||||
<Notification @sticky={{true}}>
|
<Notification @sticky={{true}}>
|
||||||
<p data-notification role="alert" class="error notification-update">
|
<p data-notification role="alert" class="error notification-update">
|
||||||
<strong>Error!</strong>
|
<strong>Error!</strong>
|
||||||
You no longer have access to this node
|
You no longer have access to this node
|
||||||
</p>
|
</p>
|
||||||
</Notification>
|
</Notification>
|
||||||
{{else}}
|
{{else}}
|
||||||
<Notification @sticky={{true}}>
|
<Notification @sticky={{true}}>
|
||||||
<p data-notification role="alert" class="warning notification-update">
|
<p data-notification role="alert" class="warning notification-update">
|
||||||
<strong>Warning!</strong>
|
<strong>Warning!</strong>
|
||||||
An error was returned whilst loading this data, refresh to try again.
|
An error was returned whilst loading this data, refresh to try again.
|
||||||
</p>
|
</p>
|
||||||
</Notification>
|
</Notification>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
|
|
||||||
<BlockSlot @name="loaded">
|
<BlockSlot @name="loaded">
|
||||||
<AppView>
|
<AppView>
|
||||||
<BlockSlot @name="notification" as |status type|>
|
<BlockSlot @name="notification" as |status type|>
|
||||||
<Consul::LockSession::Notifications
|
<Consul::LockSession::Notifications
|
||||||
@type={{type}}
|
@type={{type}}
|
||||||
@status={{status}}
|
@status={{status}}
|
||||||
/>
|
/>
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
<BlockSlot @name="breadcrumbs">
|
<BlockSlot @name="breadcrumbs">
|
||||||
<ol>
|
<ol>
|
||||||
<li><a data-test-back href={{href-to 'dc.nodes'}}>All Nodes</a></li>
|
<li><a data-test-back href={{href-to 'dc.nodes'}}>All Nodes</a></li>
|
||||||
</ol>
|
</ol>
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
<BlockSlot @name="header">
|
<BlockSlot @name="header">
|
||||||
<h1>
|
<h1>
|
||||||
{{ item.Node }}
|
{{ item.Node }}
|
||||||
</h1>
|
</h1>
|
||||||
<label for="toolbar-toggle"></label>
|
<label for="toolbar-toggle"></label>
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
<BlockSlot @name="nav">
|
<BlockSlot @name="nav">
|
||||||
<TabNav @items={{
|
<TabNav @items={{
|
||||||
compact
|
compact
|
||||||
(array
|
(array
|
||||||
(hash label="Health Checks" href=(href-to "dc.nodes.show.healthchecks") selected=(is-href "dc.nodes.show.healthchecks"))
|
(hash label="Health Checks" href=(href-to "dc.nodes.show.healthchecks") selected=(is-href "dc.nodes.show.healthchecks"))
|
||||||
(hash label="Service Instances" href=(href-to "dc.nodes.show.services") selected=(is-href "dc.nodes.show.services"))
|
(hash label="Service Instances" href=(href-to "dc.nodes.show.services") selected=(is-href "dc.nodes.show.services"))
|
||||||
(if tomography.distances (hash label="Round Trip Time" href=(href-to "dc.nodes.show.rtt") selected=(is-href "dc.nodes.show.rtt")) '')
|
(if tomography.distances (hash label="Round Trip Time" href=(href-to "dc.nodes.show.rtt") selected=(is-href "dc.nodes.show.rtt")) '')
|
||||||
(hash label="Lock Sessions" href=(href-to "dc.nodes.show.sessions") selected=(is-href "dc.nodes.show.sessions"))
|
(hash label="Lock Sessions" href=(href-to "dc.nodes.show.sessions") selected=(is-href "dc.nodes.show.sessions"))
|
||||||
(hash label="Metadata" href=(href-to "dc.nodes.show.metadata") selected=(is-href "dc.nodes.show.metadata"))
|
(hash label="Metadata" href=(href-to "dc.nodes.show.metadata") selected=(is-href "dc.nodes.show.metadata"))
|
||||||
)
|
)
|
||||||
}}/>
|
}}/>
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
<BlockSlot @name="actions">
|
<BlockSlot @name="actions">
|
||||||
<CopyButton @value={{item.Address}} @name="Address">{{item.Address}}</CopyButton>
|
<CopyButton @value={{item.Address}} @name="Address">{{item.Address}}</CopyButton>
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
<BlockSlot @name="content">
|
<BlockSlot @name="content">
|
||||||
<Outlet
|
<Outlet
|
||||||
@name={{routeName}}
|
@name={{routeName}}
|
||||||
as |o|>
|
@model={{route.model}}
|
||||||
{{outlet}}
|
as |o|>
|
||||||
</Outlet>
|
{{outlet}}
|
||||||
</BlockSlot>
|
</Outlet>
|
||||||
</AppView>
|
</BlockSlot>
|
||||||
|
</AppView>
|
||||||
|
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
</DataLoader>
|
</DataLoader>
|
||||||
|
</Route>
|
||||||
|
|
|
@ -1,70 +1,74 @@
|
||||||
{{#let
|
<Route
|
||||||
|
@name={{routeName}}
|
||||||
|
as |route|>
|
||||||
|
{{#let
|
||||||
|
|
||||||
(hash
|
(hash
|
||||||
value=(or sortBy "Status:asc")
|
value=(or sortBy "Status:asc")
|
||||||
change=(action (mut sortBy) value="target.selected")
|
change=(action (mut sortBy) value="target.selected")
|
||||||
)
|
)
|
||||||
|
|
||||||
(hash
|
(hash
|
||||||
status=(hash
|
status=(hash
|
||||||
value=(if status (split status ',') undefined)
|
value=(if status (split status ',') undefined)
|
||||||
change=(action (mut status) value="target.selectedItems")
|
change=(action (mut status) value="target.selectedItems")
|
||||||
)
|
)
|
||||||
kind=(hash
|
kind=(hash
|
||||||
value=(if kind (split kind ',') undefined)
|
value=(if kind (split kind ',') undefined)
|
||||||
change=(action (mut kind) value="target.selectedItems")
|
change=(action (mut kind) value="target.selectedItems")
|
||||||
)
|
)
|
||||||
check=(hash
|
check=(hash
|
||||||
value=(if check (split check ',') undefined)
|
value=(if check (split check ',') undefined)
|
||||||
change=(action (mut check) value="target.selectedItems")
|
change=(action (mut check) value="target.selectedItems")
|
||||||
)
|
)
|
||||||
searchproperty=(hash
|
searchproperty=(hash
|
||||||
value=(if (not-eq searchproperty undefined)
|
value=(if (not-eq searchproperty undefined)
|
||||||
(split searchproperty ',')
|
(split searchproperty ',')
|
||||||
searchProperties
|
searchProperties
|
||||||
|
)
|
||||||
|
change=(action (mut searchproperty) value="target.selectedItems")
|
||||||
|
default=searchProperties
|
||||||
)
|
)
|
||||||
change=(action (mut searchproperty) value="target.selectedItems")
|
|
||||||
default=searchProperties
|
|
||||||
)
|
)
|
||||||
)
|
|
||||||
|
|
||||||
item.Checks
|
item.Checks
|
||||||
|
|
||||||
as |sort filters items|}}
|
as |sort filters items|}}
|
||||||
<div class="tab-section">
|
<div class="tab-section">
|
||||||
{{#if (gt items.length 0) }}
|
{{#if (gt items.length 0) }}
|
||||||
<input type="checkbox" id="toolbar-toggle" />
|
<input type="checkbox" id="toolbar-toggle" />
|
||||||
<Consul::HealthCheck::SearchBar
|
<Consul::HealthCheck::SearchBar
|
||||||
|
|
||||||
@search={{search}}
|
@search={{search}}
|
||||||
@onsearch={{action (mut search) value="target.value"}}
|
@onsearch={{action (mut search) value="target.value"}}
|
||||||
|
|
||||||
@sort={{sort}}
|
@sort={{sort}}
|
||||||
|
|
||||||
@filter={{filters}}
|
@filter={{filters}}
|
||||||
/>
|
|
||||||
{{/if}}
|
|
||||||
<DataCollection
|
|
||||||
@type="health-check"
|
|
||||||
@sort={{sort.value}}
|
|
||||||
@filters={{filters}}
|
|
||||||
@search={{search}}
|
|
||||||
@items={{items}}
|
|
||||||
as |collection|>
|
|
||||||
<collection.Collection>
|
|
||||||
<Consul::HealthCheck::List
|
|
||||||
@items={{collection.items}}
|
|
||||||
/>
|
/>
|
||||||
</collection.Collection>
|
{{/if}}
|
||||||
<collection.Empty>
|
<DataCollection
|
||||||
<EmptyState>
|
@type="health-check"
|
||||||
<BlockSlot @name="body">
|
@sort={{sort.value}}
|
||||||
<p>
|
@filters={{filters}}
|
||||||
This node has no health checks{{#if (gt items.length 0)}} matching that search{{/if}}.
|
@search={{search}}
|
||||||
</p>
|
@items={{items}}
|
||||||
</BlockSlot>
|
as |collection|>
|
||||||
</EmptyState>
|
<collection.Collection>
|
||||||
</collection.Empty>
|
<Consul::HealthCheck::List
|
||||||
</DataCollection>
|
@items={{collection.items}}
|
||||||
</div>
|
/>
|
||||||
{{/let}}
|
</collection.Collection>
|
||||||
|
<collection.Empty>
|
||||||
|
<EmptyState>
|
||||||
|
<BlockSlot @name="body">
|
||||||
|
<p>
|
||||||
|
This node has no health checks{{#if (gt items.length 0)}} matching that search{{/if}}.
|
||||||
|
</p>
|
||||||
|
</BlockSlot>
|
||||||
|
</EmptyState>
|
||||||
|
</collection.Empty>
|
||||||
|
</DataCollection>
|
||||||
|
</div>
|
||||||
|
{{/let}}
|
||||||
|
</Route>
|
|
@ -1,13 +1,17 @@
|
||||||
<div class="tab-section">
|
<Route
|
||||||
{{#if item.Meta}}
|
@name={{routeName}}
|
||||||
<Consul::Metadata::List @items={{entries item.Meta}} />
|
as |route|>
|
||||||
{{else}}
|
<div class="tab-section">
|
||||||
<EmptyState>
|
{{#if item.Meta}}
|
||||||
<BlockSlot @name="body">
|
<Consul::Metadata::List @items={{entries item.Meta}} />
|
||||||
<p>
|
{{else}}
|
||||||
This node has no metadata.
|
<EmptyState>
|
||||||
</p>
|
<BlockSlot @name="body">
|
||||||
</BlockSlot>
|
<p>
|
||||||
</EmptyState>
|
This node has no metadata.
|
||||||
{{/if}}
|
</p>
|
||||||
</div>
|
</BlockSlot>
|
||||||
|
</EmptyState>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
</Route>
|
||||||
|
|
|
@ -1,26 +1,29 @@
|
||||||
<div class="tab-section">
|
<Route
|
||||||
<div class="definition-table">
|
@name={{routeName}}
|
||||||
<dl>
|
as |route|>
|
||||||
<dt>
|
<div class="tab-section">
|
||||||
Minimum
|
<div class="definition-table">
|
||||||
</dt>
|
<dl>
|
||||||
<dd>
|
<dt>
|
||||||
{{format-number tomography.min maximumFractionDigits=2}}ms
|
Minimum
|
||||||
</dd>
|
</dt>
|
||||||
<dt>
|
<dd>
|
||||||
Median
|
{{format-number tomography.min maximumFractionDigits=2}}ms
|
||||||
</dt>
|
</dd>
|
||||||
<dd>
|
<dt>
|
||||||
{{format-number tomography.median maximumFractionDigits=2}}ms
|
Median
|
||||||
</dd>
|
</dt>
|
||||||
<dt>
|
<dd>
|
||||||
Maximum
|
{{format-number tomography.median maximumFractionDigits=2}}ms
|
||||||
</dt>
|
</dd>
|
||||||
<dd>
|
<dt>
|
||||||
{{format-number tomography.max maximumFractionDigits=2}}ms
|
Maximum
|
||||||
</dd>
|
</dt>
|
||||||
</dl>
|
<dd>
|
||||||
|
{{format-number tomography.max maximumFractionDigits=2}}ms
|
||||||
|
</dd>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
<Consul::Tomography::Graph @distances={{tomography.distances}} />
|
||||||
</div>
|
</div>
|
||||||
<Consul::Tomography::Graph @distances={{tomography.distances}} />
|
</Route>
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
|
@ -1,71 +1,75 @@
|
||||||
{{#let
|
<Route
|
||||||
|
@name={{routeName}}
|
||||||
|
as |route|>
|
||||||
|
{{#let
|
||||||
|
|
||||||
(hash
|
(hash
|
||||||
value=(or sortBy "Status:asc")
|
value=(or sortBy "Status:asc")
|
||||||
change=(action (mut sortBy) value="target.selected")
|
change=(action (mut sortBy) value="target.selected")
|
||||||
)
|
)
|
||||||
|
|
||||||
(hash
|
(hash
|
||||||
status=(hash
|
status=(hash
|
||||||
value=(if status (split status ',') undefined)
|
value=(if status (split status ',') undefined)
|
||||||
change=(action (mut status) value="target.selectedItems")
|
change=(action (mut status) value="target.selectedItems")
|
||||||
)
|
)
|
||||||
source=(hash
|
source=(hash
|
||||||
value=(if source (split source ',') undefined)
|
value=(if source (split source ',') undefined)
|
||||||
change=(action (mut source) value="target.selectedItems")
|
change=(action (mut source) value="target.selectedItems")
|
||||||
)
|
)
|
||||||
searchproperty=(hash
|
searchproperty=(hash
|
||||||
value=(if (not-eq searchproperty undefined)
|
value=(if (not-eq searchproperty undefined)
|
||||||
(split searchproperty ',')
|
(split searchproperty ',')
|
||||||
searchProperties
|
searchProperties
|
||||||
|
)
|
||||||
|
change=(action (mut searchproperty) value="target.selectedItems")
|
||||||
|
default=searchProperties
|
||||||
)
|
)
|
||||||
change=(action (mut searchproperty) value="target.selectedItems")
|
|
||||||
default=searchProperties
|
|
||||||
)
|
)
|
||||||
)
|
|
||||||
|
|
||||||
item.MeshServiceInstances
|
item.MeshServiceInstances
|
||||||
|
|
||||||
as |sort filters items|}}
|
as |sort filters items|}}
|
||||||
<div class="tab-section">
|
<div class="tab-section">
|
||||||
{{#if (gt items.length 0) }}
|
{{#if (gt items.length 0) }}
|
||||||
<input type="checkbox" id="toolbar-toggle" />
|
<input type="checkbox" id="toolbar-toggle" />
|
||||||
<Consul::ServiceInstance::SearchBar
|
<Consul::ServiceInstance::SearchBar
|
||||||
@sources={{get (collection items) 'ExternalSources'}}
|
@sources={{get (collection items) 'ExternalSources'}}
|
||||||
@search={{search}}
|
@search={{search}}
|
||||||
@onsearch={{action (mut search) value="target.value"}}
|
@onsearch={{action (mut search) value="target.value"}}
|
||||||
@searchproperties={{searchProperties}}
|
@searchproperties={{searchProperties}}
|
||||||
|
|
||||||
@sort={{sort}}
|
@sort={{sort}}
|
||||||
|
|
||||||
@filter={{filters}}
|
@filter={{filters}}
|
||||||
/>
|
|
||||||
{{/if}}
|
|
||||||
{{! filter out any sidecar proxies }}
|
|
||||||
<DataCollection
|
|
||||||
@type="service-instance"
|
|
||||||
@sort={{sort.value}}
|
|
||||||
@filters={{filters}}
|
|
||||||
@search={{search}}
|
|
||||||
@items={{items}}
|
|
||||||
as |collection|>
|
|
||||||
<collection.Collection>
|
|
||||||
<Consul::ServiceInstance::List
|
|
||||||
@node={{item}}
|
|
||||||
@routeName="dc.services.show"
|
|
||||||
@items={{collection.items}}
|
|
||||||
@checks={{checks}}
|
|
||||||
/>
|
/>
|
||||||
</collection.Collection>
|
{{/if}}
|
||||||
<collection.Empty>
|
{{! filter out any sidecar proxies }}
|
||||||
<EmptyState>
|
<DataCollection
|
||||||
<BlockSlot @name="body">
|
@type="service-instance"
|
||||||
<p>
|
@sort={{sort.value}}
|
||||||
This node has no service instances{{#if (gt items.length 0)}} matching that search{{/if}}.
|
@filters={{filters}}
|
||||||
</p>
|
@search={{search}}
|
||||||
</BlockSlot>
|
@items={{items}}
|
||||||
</EmptyState>
|
as |collection|>
|
||||||
</collection.Empty>
|
<collection.Collection>
|
||||||
</DataCollection>
|
<Consul::ServiceInstance::List
|
||||||
</div>
|
@node={{item}}
|
||||||
{{/let}}
|
@routeName="dc.services.show"
|
||||||
|
@items={{collection.items}}
|
||||||
|
@checks={{checks}}
|
||||||
|
/>
|
||||||
|
</collection.Collection>
|
||||||
|
<collection.Empty>
|
||||||
|
<EmptyState>
|
||||||
|
<BlockSlot @name="body">
|
||||||
|
<p>
|
||||||
|
This node has no service instances{{#if (gt items.length 0)}} matching that search{{/if}}.
|
||||||
|
</p>
|
||||||
|
</BlockSlot>
|
||||||
|
</EmptyState>
|
||||||
|
</collection.Empty>
|
||||||
|
</DataCollection>
|
||||||
|
</div>
|
||||||
|
{{/let}}
|
||||||
|
</Route>
|
|
@ -1,30 +1,36 @@
|
||||||
<EventSource @src={{sessions}} />
|
<Route
|
||||||
<div class="tab-section">
|
@name={{routeName}}
|
||||||
{{#if (gt sessions.length 0)}}
|
as |route|>
|
||||||
<Consul::LockSession::List
|
<EventSource @src={{sessions}} />
|
||||||
@items={{sessions}}
|
<div class="tab-section">
|
||||||
@onInvalidate={{action send 'invalidateSession'}}
|
{{#if (gt sessions.length 0)}}
|
||||||
/>
|
<Consul::LockSession::List
|
||||||
{{else}}
|
@items={{sessions}}
|
||||||
<EmptyState @allowLogin={{true}}>
|
@onInvalidate={{action send 'invalidateSession'}}
|
||||||
<BlockSlot @name="header">
|
/>
|
||||||
<h2>
|
{{else}}
|
||||||
Welcome to Lock Sessions
|
<EmptyState
|
||||||
</h2>
|
@login={{route.model.app.login.open}}
|
||||||
</BlockSlot>
|
>
|
||||||
<BlockSlot @name="body">
|
<BlockSlot @name="header">
|
||||||
<p>
|
<h2>
|
||||||
Consul provides a session mechanism which can be used to build distributed locks. Sessions act as a binding layer between nodes, health checks, and key/value data. There are currently no lock sessions present, or you may not have permission to view lock sessions.
|
Welcome to Lock Sessions
|
||||||
</p>
|
</h2>
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
<BlockSlot @name="actions">
|
<BlockSlot @name="body">
|
||||||
<li class="docs-link">
|
<p>
|
||||||
<a href="{{env 'CONSUL_DOCS_URL'}}/internals/sessions.html" rel="noopener noreferrer" target="_blank">Documentation on sessions</a>
|
Consul provides a session mechanism which can be used to build distributed locks. Sessions act as a binding layer between nodes, health checks, and key/value data. There are currently no lock sessions present, or you may not have permission to view lock sessions.
|
||||||
</li>
|
</p>
|
||||||
<li class="learn-link">
|
</BlockSlot>
|
||||||
<a href="{{env 'CONSUL_DOCS_LEARN_URL'}}/tutorials/consul/distributed-semaphore" rel="noopener noreferrer" target="_blank">Read the guide</a>
|
<BlockSlot @name="actions">
|
||||||
</li>
|
<li class="docs-link">
|
||||||
</BlockSlot>
|
<a href="{{env 'CONSUL_DOCS_URL'}}/internals/sessions.html" rel="noopener noreferrer" target="_blank">Documentation on sessions</a>
|
||||||
</EmptyState>
|
</li>
|
||||||
{{/if}}
|
<li class="learn-link">
|
||||||
</div>
|
<a href="{{env 'CONSUL_DOCS_LEARN_URL'}}/tutorials/consul/distributed-semaphore" rel="noopener noreferrer" target="_blank">Read the guide</a>
|
||||||
|
</li>
|
||||||
|
</BlockSlot>
|
||||||
|
</EmptyState>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
</Route>
|
||||||
|
|
|
@ -1,33 +1,33 @@
|
||||||
{{#if create }}
|
<Route
|
||||||
{{page-title 'New Namespace'}}
|
@name={{routeName}}
|
||||||
{{else}}
|
@title={{if create 'New Namespace' 'Edit Namespace'}}
|
||||||
{{page-title 'Edit Namespace'}}
|
as |route|>
|
||||||
{{/if}}
|
<AppView>
|
||||||
<AppView>
|
<BlockSlot @name="notification" as |status type item error|>
|
||||||
<BlockSlot @name="notification" as |status type item error|>
|
<Consul::Nspace::Notifications
|
||||||
<Consul::Nspace::Notifications
|
@type={{type}}
|
||||||
@type={{type}}
|
@status={{status}}
|
||||||
@status={{status}}
|
@error={{error}}
|
||||||
@error={{error}}
|
/>
|
||||||
/>
|
</BlockSlot>
|
||||||
</BlockSlot>
|
<BlockSlot @name="breadcrumbs">
|
||||||
<BlockSlot @name="breadcrumbs">
|
<ol>
|
||||||
<ol>
|
<li><a data-test-back href={{href-to 'dc.nspaces'}}>All Namespaces</a></li>
|
||||||
<li><a data-test-back href={{href-to 'dc.nspaces'}}>All Namespaces</a></li>
|
</ol>
|
||||||
</ol>
|
</BlockSlot>
|
||||||
</BlockSlot>
|
<BlockSlot @name="header">
|
||||||
<BlockSlot @name="header">
|
<h1>
|
||||||
<h1>
|
{{#if create }}
|
||||||
{{#if create }}
|
New Namespace
|
||||||
New Namespace
|
{{else}}
|
||||||
{{else}}
|
Edit {{item.Name}}
|
||||||
Edit {{item.Name}}
|
{{/if}}
|
||||||
{{/if}}
|
</h1>
|
||||||
</h1>
|
</BlockSlot>
|
||||||
</BlockSlot>
|
<BlockSlot @name="actions">
|
||||||
<BlockSlot @name="actions">
|
</BlockSlot>
|
||||||
</BlockSlot>
|
<BlockSlot @name="content">
|
||||||
<BlockSlot @name="content">
|
{{ partial 'dc/nspaces/form'}}
|
||||||
{{ partial 'dc/nspaces/form'}}
|
</BlockSlot>
|
||||||
</BlockSlot>
|
</AppView>
|
||||||
</AppView>
|
</Route>
|
||||||
|
|
|
@ -1,100 +1,106 @@
|
||||||
{{page-title 'Namespaces'}}
|
<Route
|
||||||
<EventSource @src={{items}} />
|
@name={{routeName}}
|
||||||
{{#let
|
@title='Namespaces'
|
||||||
|
as |route|>
|
||||||
|
<EventSource @src={{items}} />
|
||||||
|
{{#let
|
||||||
|
|
||||||
(hash
|
(hash
|
||||||
value=(or sortBy "Name:asc")
|
value=(or sortBy "Name:asc")
|
||||||
change=(action (mut sortBy) value="target.selected")
|
change=(action (mut sortBy) value="target.selected")
|
||||||
)
|
|
||||||
|
|
||||||
(hash
|
|
||||||
searchproperty=(hash
|
|
||||||
value=(if (not-eq searchproperty undefined)
|
|
||||||
(split searchproperty ',')
|
|
||||||
searchProperties
|
|
||||||
)
|
|
||||||
change=(action (mut searchproperty) value="target.selectedItems")
|
|
||||||
default=searchProperties
|
|
||||||
)
|
)
|
||||||
)
|
|
||||||
|
|
||||||
items
|
(hash
|
||||||
|
searchproperty=(hash
|
||||||
|
value=(if (not-eq searchproperty undefined)
|
||||||
|
(split searchproperty ',')
|
||||||
|
searchProperties
|
||||||
|
)
|
||||||
|
change=(action (mut searchproperty) value="target.selectedItems")
|
||||||
|
default=searchProperties
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
as |sort filters items|}}
|
items
|
||||||
|
|
||||||
<AppView>
|
as |sort filters items|}}
|
||||||
<BlockSlot @name="notification" as |status type item error|>
|
|
||||||
<Consul::Nspace::Notifications
|
|
||||||
@type={{type}}
|
|
||||||
@status={{status}}
|
|
||||||
@error={{error}}
|
|
||||||
/>
|
|
||||||
</BlockSlot>
|
|
||||||
<BlockSlot @name="header">
|
|
||||||
<h1>
|
|
||||||
Namespaces
|
|
||||||
</h1>
|
|
||||||
</BlockSlot>
|
|
||||||
<BlockSlot @name="actions">
|
|
||||||
<a data-test-create href="{{href-to 'dc.nspaces.create'}}" class="type-create">Create</a>
|
|
||||||
</BlockSlot>
|
|
||||||
<BlockSlot @name="toolbar">
|
|
||||||
{{#if (gt items.length 0)}}
|
|
||||||
<Consul::Nspace::SearchBar
|
|
||||||
@search={{search}}
|
|
||||||
@onsearch={{action (mut search) value="target.value"}}
|
|
||||||
|
|
||||||
@sort={{sort}}
|
<AppView>
|
||||||
|
<BlockSlot @name="notification" as |status type item error|>
|
||||||
|
<Consul::Nspace::Notifications
|
||||||
|
@type={{type}}
|
||||||
|
@status={{status}}
|
||||||
|
@error={{error}}
|
||||||
|
/>
|
||||||
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="header">
|
||||||
|
<h1>
|
||||||
|
Namespaces
|
||||||
|
</h1>
|
||||||
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="actions">
|
||||||
|
<a data-test-create href="{{href-to 'dc.nspaces.create'}}" class="type-create">Create</a>
|
||||||
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="toolbar">
|
||||||
|
{{#if (gt items.length 0)}}
|
||||||
|
<Consul::Nspace::SearchBar
|
||||||
|
@search={{search}}
|
||||||
|
@onsearch={{action (mut search) value="target.value"}}
|
||||||
|
|
||||||
@filter={{filters}}
|
@sort={{sort}}
|
||||||
/>
|
|
||||||
{{/if}}
|
@filter={{filters}}
|
||||||
</BlockSlot>
|
/>
|
||||||
<BlockSlot @name="content">
|
{{/if}}
|
||||||
<DataCollection
|
</BlockSlot>
|
||||||
@type="nspace"
|
<BlockSlot @name="content">
|
||||||
@sort={{sort.value}}
|
<DataCollection
|
||||||
@filters={{filters}}
|
@type="nspace"
|
||||||
@search={{search}}
|
@sort={{sort.value}}
|
||||||
@items={{items}}
|
@filters={{filters}}
|
||||||
as |collection|>
|
@search={{search}}
|
||||||
<collection.Collection>
|
@items={{items}}
|
||||||
<Consul::Nspace::List
|
as |collection|>
|
||||||
@items={{collection.items}}
|
<collection.Collection>
|
||||||
@ondelete={{route-action 'delete'}}
|
<Consul::Nspace::List
|
||||||
/>
|
@items={{collection.items}}
|
||||||
</collection.Collection>
|
@ondelete={{route-action 'delete'}}
|
||||||
<collection.Empty>
|
/>
|
||||||
<EmptyState @allowLogin={{true}}>
|
</collection.Collection>
|
||||||
<BlockSlot @name="header">
|
<collection.Empty>
|
||||||
<h2>
|
<EmptyState
|
||||||
{{#if (gt items.length 0)}}
|
@login={{route.model.app.login.open}}
|
||||||
No namespaces found
|
>
|
||||||
{{else}}
|
<BlockSlot @name="header">
|
||||||
Welcome to Namespaces
|
<h2>
|
||||||
{{/if}}
|
{{#if (gt items.length 0)}}
|
||||||
</h2>
|
No namespaces found
|
||||||
</BlockSlot>
|
{{else}}
|
||||||
<BlockSlot @name="body">
|
Welcome to Namespaces
|
||||||
<p>
|
{{/if}}
|
||||||
{{#if (gt items.length 0)}}
|
</h2>
|
||||||
No namespaces where found matching that search, or you may not have access to view the namespaces you are searching for.
|
</BlockSlot>
|
||||||
{{else}}
|
<BlockSlot @name="body">
|
||||||
There don't seem to be any namespaces, or you may not have access to view namespaces yet.
|
<p>
|
||||||
{{/if}}
|
{{#if (gt items.length 0)}}
|
||||||
</p>
|
No namespaces where found matching that search, or you may not have access to view the namespaces you are searching for.
|
||||||
</BlockSlot>
|
{{else}}
|
||||||
<BlockSlot @name="actions">
|
There don't seem to be any namespaces, or you may not have access to view namespaces yet.
|
||||||
<li class="docs-link">
|
{{/if}}
|
||||||
<a href="{{env 'CONSUL_DOCS_URL'}}/commands/namespace" rel="noopener noreferrer" target="_blank">Documentation on namespaces</a>
|
</p>
|
||||||
</li>
|
</BlockSlot>
|
||||||
<li class="learn-link">
|
<BlockSlot @name="actions">
|
||||||
<a href="{{env 'CONSUL_DOCS_LEARN_URL'}}/consul/namespaces/secure-namespaces" rel="noopener noreferrer" target="_blank">Read the guide</a>
|
<li class="docs-link">
|
||||||
</li>
|
<a href="{{env 'CONSUL_DOCS_URL'}}/commands/namespace" rel="noopener noreferrer" target="_blank">Documentation on namespaces</a>
|
||||||
</BlockSlot>
|
</li>
|
||||||
</EmptyState>
|
<li class="learn-link">
|
||||||
</collection.Empty>
|
<a href="{{env 'CONSUL_DOCS_LEARN_URL'}}/consul/namespaces/secure-namespaces" rel="noopener noreferrer" target="_blank">Read the guide</a>
|
||||||
</DataCollection>
|
</li>
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
</AppView>
|
</EmptyState>
|
||||||
{{/let}}
|
</collection.Empty>
|
||||||
|
</DataCollection>
|
||||||
|
</BlockSlot>
|
||||||
|
</AppView>
|
||||||
|
{{/let}}
|
||||||
|
</Route>
|
|
@ -1,7 +1,7 @@
|
||||||
<Route
|
<Route
|
||||||
@name={{routeName}}
|
@name={{routeName}}
|
||||||
>
|
@title="Services"
|
||||||
{{page-title 'Services'}}
|
as |route|>
|
||||||
|
|
||||||
<EventSource @src={{items}} />
|
<EventSource @src={{items}} />
|
||||||
|
|
||||||
|
@ -76,7 +76,9 @@ as |sort filters items|}}
|
||||||
</Consul::Service::List>
|
</Consul::Service::List>
|
||||||
</collection.Collection>
|
</collection.Collection>
|
||||||
<collection.Empty>
|
<collection.Empty>
|
||||||
<EmptyState @allowLogin={{true}}>
|
<EmptyState
|
||||||
|
@login={{route.model.app.login.open}}
|
||||||
|
>
|
||||||
<BlockSlot @name="header">
|
<BlockSlot @name="header">
|
||||||
<h2>
|
<h2>
|
||||||
{{#if (gt services.length 0)}}
|
{{#if (gt services.length 0)}}
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
{{page-title item.Service.ID}}
|
<Route
|
||||||
|
@name={{routeName}}
|
||||||
|
@title={{item.Service.ID}}
|
||||||
|
as |route|>
|
||||||
<DataLoader as |loader|>
|
<DataLoader as |loader|>
|
||||||
|
|
||||||
<BlockSlot @name="data">
|
<BlockSlot @name="data">
|
||||||
|
@ -10,7 +13,10 @@
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
|
|
||||||
<BlockSlot @name="error">
|
<BlockSlot @name="error">
|
||||||
<AppError @error={{loader.error}} />
|
<AppError
|
||||||
|
@error={{loader.error}}
|
||||||
|
@login={{route.model.app.login.open}}
|
||||||
|
/>
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
|
|
||||||
<BlockSlot @name="disconnected" as |Notification|>
|
<BlockSlot @name="disconnected" as |Notification|>
|
||||||
|
@ -88,10 +94,12 @@
|
||||||
}}/>
|
}}/>
|
||||||
<Outlet
|
<Outlet
|
||||||
@name={{routeName}}
|
@name={{routeName}}
|
||||||
|
@model={{route.model}}
|
||||||
as |o|>
|
as |o|>
|
||||||
{{outlet}}
|
{{outlet}}
|
||||||
</Outlet>
|
</Outlet>
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
</AppView>
|
</AppView>
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
</DataLoader>
|
</DataLoader>
|
||||||
|
</Route>
|
|
@ -1,28 +1,32 @@
|
||||||
<div class="tab-section">
|
<Route
|
||||||
{{#if item.Service.TaggedAddresses }}
|
@name={{routeName}}
|
||||||
<TabularCollection
|
as |route|>
|
||||||
data-test-addresses
|
<div class="tab-section">
|
||||||
class="consul-tagged-addresses"
|
{{#if item.Service.TaggedAddresses }}
|
||||||
@items={{entries item.Service.TaggedAddresses}} as |taggedAddress index|
|
<TabularCollection
|
||||||
>
|
data-test-addresses
|
||||||
<BlockSlot @name="header">
|
class="consul-tagged-addresses"
|
||||||
<th>Tag</th>
|
@items={{entries item.Service.TaggedAddresses}} as |taggedAddress index|
|
||||||
<th>Address</th>
|
>
|
||||||
</BlockSlot>
|
<BlockSlot @name="header">
|
||||||
<BlockSlot @name="row">
|
<th>Tag</th>
|
||||||
{{#with (object-at 1 taggedAddress) as |address|}}
|
<th>Address</th>
|
||||||
<td>
|
</BlockSlot>
|
||||||
{{object-at 0 taggedAddress}}{{#if (and (eq address.Address item.Address) (eq address.Port item.Port))}} <em data-test-address-default>(default)</em>{{/if}}
|
<BlockSlot @name="row">
|
||||||
</td>
|
{{#with (object-at 1 taggedAddress) as |address|}}
|
||||||
<td data-test-address>
|
<td>
|
||||||
{{address.Address}}:{{address.Port}}
|
{{object-at 0 taggedAddress}}{{#if (and (eq address.Address item.Address) (eq address.Port item.Port))}} <em data-test-address-default>(default)</em>{{/if}}
|
||||||
</td>
|
</td>
|
||||||
{{/with}}
|
<td data-test-address>
|
||||||
</BlockSlot>
|
{{address.Address}}:{{address.Port}}
|
||||||
</TabularCollection>
|
</td>
|
||||||
{{else}}
|
{{/with}}
|
||||||
<p>
|
</BlockSlot>
|
||||||
There are no additional addresses.
|
</TabularCollection>
|
||||||
</p>
|
{{else}}
|
||||||
{{/if}}
|
<p>
|
||||||
</div>
|
There are no additional addresses.
|
||||||
|
</p>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
</Route>
|
||||||
|
|
|
@ -1,16 +1,20 @@
|
||||||
<div class="tab-section">
|
<Route
|
||||||
{{#if (gt proxy.Service.Proxy.Expose.Paths.length 0)}}
|
@name={{routeName}}
|
||||||
<p>
|
as |route|>
|
||||||
The following list shows individual HTTP paths exposed through Envoy for external services like Prometheus. Read more about this in our documentation.
|
<div class="tab-section">
|
||||||
</p>
|
{{#if (gt proxy.Service.Proxy.Expose.Paths.length 0)}}
|
||||||
<Consul::ExposedPath::List @items={{proxy.Service.Proxy.Expose.Paths}} @address={{proxy.Address}} />
|
<p>
|
||||||
{{else}}
|
The following list shows individual HTTP paths exposed through Envoy for external services like Prometheus. Read more about this in our documentation.
|
||||||
<EmptyState>
|
</p>
|
||||||
<BlockSlot @name="body">
|
<Consul::ExposedPath::List @items={{proxy.Service.Proxy.Expose.Paths}} @address={{proxy.Address}} />
|
||||||
<p>
|
{{else}}
|
||||||
There are no individual HTTP paths exposed through Envoy for external services like Prometheus. Read more about this in our <Action @href={{concat (env 'CONSUL_DOCS_URL') '/connect/registration/service-registration#expose-paths-configuration-reference'}} @external={{true}}>documentation</Action>.
|
<EmptyState>
|
||||||
</p>
|
<BlockSlot @name="body">
|
||||||
</BlockSlot>
|
<p>
|
||||||
</EmptyState>
|
There are no individual HTTP paths exposed through Envoy for external services like Prometheus. Read more about this in our <Action @href={{concat (env 'CONSUL_DOCS_URL') '/connect/registration/service-registration#expose-paths-configuration-reference'}} @external={{true}}>documentation</Action>.
|
||||||
{{/if}}
|
</p>
|
||||||
</div>
|
</BlockSlot>
|
||||||
|
</EmptyState>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
</Route>
|
|
@ -1,68 +1,72 @@
|
||||||
{{#let
|
<Route
|
||||||
|
@name={{routeName}}
|
||||||
|
as |route|>
|
||||||
|
{{#let
|
||||||
|
|
||||||
(hash
|
(hash
|
||||||
value=(or sortBy "Status:asc")
|
value=(or sortBy "Status:asc")
|
||||||
change=(action (mut sortBy) value="target.selected")
|
change=(action (mut sortBy) value="target.selected")
|
||||||
)
|
)
|
||||||
|
|
||||||
(hash
|
(hash
|
||||||
status=(hash
|
status=(hash
|
||||||
value=(if status (split status ',') undefined)
|
value=(if status (split status ',') undefined)
|
||||||
change=(action (mut status) value="target.selectedItems")
|
change=(action (mut status) value="target.selectedItems")
|
||||||
)
|
)
|
||||||
check=(hash
|
check=(hash
|
||||||
value=(if check (split check ',') undefined)
|
value=(if check (split check ',') undefined)
|
||||||
change=(action (mut check) value="target.selectedItems")
|
change=(action (mut check) value="target.selectedItems")
|
||||||
)
|
)
|
||||||
searchproperty=(hash
|
searchproperty=(hash
|
||||||
value=(if (not-eq searchproperty undefined)
|
value=(if (not-eq searchproperty undefined)
|
||||||
(split searchproperty ',')
|
(split searchproperty ',')
|
||||||
searchProperties
|
searchProperties
|
||||||
|
)
|
||||||
|
change=(action (mut searchproperty) value="target.selectedItems")
|
||||||
|
default=searchProperties
|
||||||
)
|
)
|
||||||
change=(action (mut searchproperty) value="target.selectedItems")
|
|
||||||
default=searchProperties
|
|
||||||
)
|
)
|
||||||
)
|
|
||||||
|
|
||||||
item.MeshChecks
|
item.MeshChecks
|
||||||
|
|
||||||
as |sort filters items|}}
|
as |sort filters items|}}
|
||||||
<div class="tab-section">
|
<div class="tab-section">
|
||||||
|
|
||||||
{{#if (gt items.length 0) }}
|
{{#if (gt items.length 0) }}
|
||||||
<input type="checkbox" id="toolbar-toggle" />
|
<input type="checkbox" id="toolbar-toggle" />
|
||||||
<Consul::HealthCheck::SearchBar
|
<Consul::HealthCheck::SearchBar
|
||||||
@search={{search}}
|
@search={{search}}
|
||||||
@onsearch={{action (mut search) value="target.value"}}
|
@onsearch={{action (mut search) value="target.value"}}
|
||||||
|
|
||||||
@sort={{sort}}
|
@sort={{sort}}
|
||||||
|
|
||||||
@filter={{filters}}
|
@filter={{filters}}
|
||||||
/>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
<DataCollection
|
|
||||||
@type="health-check"
|
|
||||||
@sort={{sort.value}}
|
|
||||||
@filters={{filters}}
|
|
||||||
@search={{search}}
|
|
||||||
@items={{items}}
|
|
||||||
as |collection|>
|
|
||||||
<collection.Collection>
|
|
||||||
<Consul::HealthCheck::List
|
|
||||||
@items={{collection.items}}
|
|
||||||
/>
|
/>
|
||||||
</collection.Collection>
|
{{/if}}
|
||||||
<collection.Empty>
|
|
||||||
<EmptyState>
|
|
||||||
<BlockSlot @name="body">
|
|
||||||
<p>
|
|
||||||
This instance has no health checks{{#if (gt items.length 0)}} matching that search{{/if}}.
|
|
||||||
</p>
|
|
||||||
</BlockSlot>
|
|
||||||
</EmptyState>
|
|
||||||
</collection.Empty>
|
|
||||||
</DataCollection>
|
|
||||||
|
|
||||||
</div>
|
<DataCollection
|
||||||
{{/let}}
|
@type="health-check"
|
||||||
|
@sort={{sort.value}}
|
||||||
|
@filters={{filters}}
|
||||||
|
@search={{search}}
|
||||||
|
@items={{items}}
|
||||||
|
as |collection|>
|
||||||
|
<collection.Collection>
|
||||||
|
<Consul::HealthCheck::List
|
||||||
|
@items={{collection.items}}
|
||||||
|
/>
|
||||||
|
</collection.Collection>
|
||||||
|
<collection.Empty>
|
||||||
|
<EmptyState>
|
||||||
|
<BlockSlot @name="body">
|
||||||
|
<p>
|
||||||
|
This instance has no health checks{{#if (gt items.length 0)}} matching that search{{/if}}.
|
||||||
|
</p>
|
||||||
|
</BlockSlot>
|
||||||
|
</EmptyState>
|
||||||
|
</collection.Empty>
|
||||||
|
</DataCollection>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
{{/let}}
|
||||||
|
</Route>
|
||||||
|
|
|
@ -1,30 +1,34 @@
|
||||||
<div class="tab-section">
|
<Route
|
||||||
<section class="tags">
|
@name={{routeName}}
|
||||||
<h2>Tags</h2>
|
as |route|>
|
||||||
{{#if (gt item.Tags.length 0) }}
|
<div class="tab-section">
|
||||||
<TagList @item={{item}} />
|
<section class="tags">
|
||||||
{{else}}
|
<h2>Tags</h2>
|
||||||
<EmptyState>
|
{{#if (gt item.Tags.length 0) }}
|
||||||
<BlockSlot @name="body">
|
<TagList @item={{item}} />
|
||||||
<p>
|
{{else}}
|
||||||
There are no tags.
|
<EmptyState>
|
||||||
</p>
|
<BlockSlot @name="body">
|
||||||
</BlockSlot>
|
<p>
|
||||||
</EmptyState>
|
There are no tags.
|
||||||
{{/if}}
|
</p>
|
||||||
</section>
|
</BlockSlot>
|
||||||
<section class="metadata">
|
</EmptyState>
|
||||||
<h2>Meta</h2>
|
{{/if}}
|
||||||
{{#if item.Meta}}
|
</section>
|
||||||
<Consul::Metadata::List @items={{entries item.Meta}} />
|
<section class="metadata">
|
||||||
{{else}}
|
<h2>Meta</h2>
|
||||||
<EmptyState>
|
{{#if item.Meta}}
|
||||||
<BlockSlot @name="body">
|
<Consul::Metadata::List @items={{entries item.Meta}} />
|
||||||
<p>
|
{{else}}
|
||||||
This instance has no metadata.
|
<EmptyState>
|
||||||
</p>
|
<BlockSlot @name="body">
|
||||||
</BlockSlot>
|
<p>
|
||||||
</EmptyState>
|
This instance has no metadata.
|
||||||
{{/if}}
|
</p>
|
||||||
</section>
|
</BlockSlot>
|
||||||
</div>
|
</EmptyState>
|
||||||
|
{{/if}}
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</Route>
|
||||||
|
|
|
@ -1,60 +1,64 @@
|
||||||
<div class="tab-section">
|
<Route
|
||||||
{{#let
|
@name={{routeName}}
|
||||||
|
as |route|>
|
||||||
|
<div class="tab-section">
|
||||||
|
{{#let
|
||||||
|
|
||||||
(hash
|
(hash
|
||||||
value=(or sortBy "DestinationName:asc")
|
value=(or sortBy "DestinationName:asc")
|
||||||
change=(action (mut sortBy) value="target.selected")
|
change=(action (mut sortBy) value="target.selected")
|
||||||
)
|
|
||||||
|
|
||||||
(hash
|
|
||||||
searchproperty=(hash
|
|
||||||
value=(if (not-eq searchproperty undefined)
|
|
||||||
(split searchproperty ',')
|
|
||||||
searchProperties
|
|
||||||
)
|
|
||||||
change=(action (mut searchproperty) value="target.selectedItems")
|
|
||||||
default=searchProperties
|
|
||||||
)
|
)
|
||||||
)
|
|
||||||
|
|
||||||
proxy.Service.Proxy.Upstreams
|
(hash
|
||||||
|
searchproperty=(hash
|
||||||
|
value=(if (not-eq searchproperty undefined)
|
||||||
|
(split searchproperty ',')
|
||||||
|
searchProperties
|
||||||
|
)
|
||||||
|
change=(action (mut searchproperty) value="target.selectedItems")
|
||||||
|
default=searchProperties
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
as |sort filters items|}}
|
proxy.Service.Proxy.Upstreams
|
||||||
{{#if (gt items.length 0)}}
|
|
||||||
<input type="checkbox" id="toolbar-toggle" />
|
|
||||||
<Consul::UpstreamInstance::SearchBar
|
|
||||||
@search={{search}}
|
|
||||||
@onsearch={{action (mut search) value="target.value"}}
|
|
||||||
@searchproperties={{searchProperties}}
|
|
||||||
|
|
||||||
@sort={{sort}}
|
as |sort filters items|}}
|
||||||
|
{{#if (gt items.length 0)}}
|
||||||
|
<input type="checkbox" id="toolbar-toggle" />
|
||||||
|
<Consul::UpstreamInstance::SearchBar
|
||||||
|
@search={{search}}
|
||||||
|
@onsearch={{action (mut search) value="target.value"}}
|
||||||
|
@searchproperties={{searchProperties}}
|
||||||
|
|
||||||
@filter={{filters}}
|
@sort={{sort}}
|
||||||
/>
|
|
||||||
{{/if}}
|
@filter={{filters}}
|
||||||
<DataCollection
|
|
||||||
@type="upstream-instance"
|
|
||||||
@sort={{sort.value}}
|
|
||||||
@filters={{filters}}
|
|
||||||
@search={{search}}
|
|
||||||
@items={{items}}
|
|
||||||
as |collection|>
|
|
||||||
<collection.Collection>
|
|
||||||
<Consul::UpstreamInstance::List
|
|
||||||
@items={{collection.items}}
|
|
||||||
@dc={{dc}}
|
|
||||||
@nspace={{nspace}}
|
|
||||||
/>
|
/>
|
||||||
</collection.Collection>
|
{{/if}}
|
||||||
<collection.Empty>
|
<DataCollection
|
||||||
<EmptyState>
|
@type="upstream-instance"
|
||||||
<BlockSlot @name="body">
|
@sort={{sort.value}}
|
||||||
<p>
|
@filters={{filters}}
|
||||||
This service has no upstreams{{#if (gt items.length 0)}} matching that search{{/if}}.
|
@search={{search}}
|
||||||
</p>
|
@items={{items}}
|
||||||
</BlockSlot>
|
as |collection|>
|
||||||
</EmptyState>
|
<collection.Collection>
|
||||||
</collection.Empty>
|
<Consul::UpstreamInstance::List
|
||||||
</DataCollection>
|
@items={{collection.items}}
|
||||||
{{/let}}
|
@dc={{dc}}
|
||||||
</div>
|
@nspace={{nspace}}
|
||||||
|
/>
|
||||||
|
</collection.Collection>
|
||||||
|
<collection.Empty>
|
||||||
|
<EmptyState>
|
||||||
|
<BlockSlot @name="body">
|
||||||
|
<p>
|
||||||
|
This service has no upstreams{{#if (gt items.length 0)}} matching that search{{/if}}.
|
||||||
|
</p>
|
||||||
|
</BlockSlot>
|
||||||
|
</EmptyState>
|
||||||
|
</collection.Empty>
|
||||||
|
</DataCollection>
|
||||||
|
{{/let}}
|
||||||
|
</div>
|
||||||
|
</Route>
|
|
@ -1,8 +1,8 @@
|
||||||
|
{{#let items.firstObject as |item|}}
|
||||||
<Route
|
<Route
|
||||||
@name={{routeName}}
|
@name={{routeName}}
|
||||||
|
@title={{item.Service.Service}}
|
||||||
as |route|>
|
as |route|>
|
||||||
{{#let items.firstObject as |item|}}
|
|
||||||
{{page-title item.Service.Service}}
|
|
||||||
<DataLoader as |loader|>
|
<DataLoader as |loader|>
|
||||||
|
|
||||||
<BlockSlot @name="data">
|
<BlockSlot @name="data">
|
||||||
|
@ -14,7 +14,10 @@ as |route|>
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
|
|
||||||
<BlockSlot @name="error">
|
<BlockSlot @name="error">
|
||||||
<AppError @error={{loader.error}} />
|
<AppError
|
||||||
|
@error={{loader.error}}
|
||||||
|
@login={{route.model.app.login.open}}
|
||||||
|
/>
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
|
|
||||||
<BlockSlot @name="disconnected" as |Notification|>
|
<BlockSlot @name="disconnected" as |Notification|>
|
||||||
|
@ -107,6 +110,7 @@ as |route|>
|
||||||
<BlockSlot @name="content">
|
<BlockSlot @name="content">
|
||||||
<Outlet
|
<Outlet
|
||||||
@name={{routeName}}
|
@name={{routeName}}
|
||||||
|
@model={{route.model}}
|
||||||
as |o|>
|
as |o|>
|
||||||
{{outlet}}
|
{{outlet}}
|
||||||
</Outlet>
|
</Outlet>
|
||||||
|
@ -114,5 +118,5 @@ as |route|>
|
||||||
</AppView>
|
</AppView>
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
</DataLoader>
|
</DataLoader>
|
||||||
{{/let}}
|
</Route>
|
||||||
</Route>
|
{{/let}}
|
|
@ -1,6 +1,6 @@
|
||||||
<Route
|
<Route
|
||||||
@name={{routeName}}
|
@name={{routeName}}
|
||||||
>
|
as |route|>
|
||||||
<div class="tab-section">
|
<div class="tab-section">
|
||||||
{{#let
|
{{#let
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,10 @@
|
||||||
<Outlet
|
<Route
|
||||||
@name={{routeName}}
|
@name={{routeName}}
|
||||||
as |o|>
|
as |route|>
|
||||||
{{outlet}}
|
<Outlet
|
||||||
</Outlet>
|
@name={{routeName}}
|
||||||
|
@model={{route.model}}
|
||||||
|
as |o|>
|
||||||
|
{{outlet}}
|
||||||
|
</Outlet>
|
||||||
|
</Route>
|
||||||
|
|
|
@ -1,9 +1,13 @@
|
||||||
<Consul::Intention::Form
|
<Route
|
||||||
@nspace={{nspace}}
|
@name={{routeName}}
|
||||||
@dc={{dc}}
|
as |route|>
|
||||||
@src={{src}}
|
<Consul::Intention::Form
|
||||||
@autofill={{hash
|
@nspace={{nspace}}
|
||||||
DestinationName=service
|
@dc={{dc}}
|
||||||
}}
|
@src={{src}}
|
||||||
@onsubmit={{transition-to 'dc.services.show.intentions.index'}}
|
@autofill={{hash
|
||||||
/>
|
DestinationName=service
|
||||||
|
}}
|
||||||
|
@onsubmit={{transition-to 'dc.services.show.intentions.index'}}
|
||||||
|
/>
|
||||||
|
</Route>
|
||||||
|
|
|
@ -1,96 +1,100 @@
|
||||||
<DataLoader
|
<Route
|
||||||
@src={{uri
|
@name={{routeName}}
|
||||||
'/${nspace}/${dc}/intentions/for-service/${slug}'
|
as |route|>
|
||||||
(hash
|
<DataLoader
|
||||||
nspace=nspace
|
@src={{uri
|
||||||
dc=dc
|
'/${nspace}/${dc}/intentions/for-service/${slug}'
|
||||||
slug=slug
|
(hash
|
||||||
)
|
nspace=nspace
|
||||||
}}
|
dc=dc
|
||||||
as |api|>
|
slug=slug
|
||||||
<BlockSlot @name="error">
|
|
||||||
<ErrorState @error={{api.error}} />
|
|
||||||
</BlockSlot>
|
|
||||||
<BlockSlot @name="loaded">
|
|
||||||
{{#let
|
|
||||||
|
|
||||||
(hash
|
|
||||||
value=(or sortBy "Action:asc")
|
|
||||||
change=(action (mut sortBy) value="target.selected")
|
|
||||||
)
|
|
||||||
|
|
||||||
(hash
|
|
||||||
access=(hash
|
|
||||||
value=(if access (split access ',') undefined)
|
|
||||||
change=(action (mut access) value="target.selectedItems")
|
|
||||||
)
|
|
||||||
searchproperty=(hash
|
|
||||||
value=(if (not-eq searchproperty undefined)
|
|
||||||
(split searchproperty ',')
|
|
||||||
searchProperties
|
|
||||||
)
|
)
|
||||||
change=(action (mut searchproperty) value="target.selectedItems")
|
}}
|
||||||
default=searchProperties
|
as |api|>
|
||||||
|
<BlockSlot @name="error">
|
||||||
|
<ErrorState @error={{api.error}} />
|
||||||
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="loaded">
|
||||||
|
{{#let
|
||||||
|
|
||||||
|
(hash
|
||||||
|
value=(or sortBy "Action:asc")
|
||||||
|
change=(action (mut sortBy) value="target.selected")
|
||||||
)
|
)
|
||||||
)
|
|
||||||
|
|
||||||
api.data
|
(hash
|
||||||
|
access=(hash
|
||||||
|
value=(if access (split access ',') undefined)
|
||||||
|
change=(action (mut access) value="target.selectedItems")
|
||||||
|
)
|
||||||
|
searchproperty=(hash
|
||||||
|
value=(if (not-eq searchproperty undefined)
|
||||||
|
(split searchproperty ',')
|
||||||
|
searchProperties
|
||||||
|
)
|
||||||
|
change=(action (mut searchproperty) value="target.selectedItems")
|
||||||
|
default=searchProperties
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
as |sort filters items|}}
|
api.data
|
||||||
<div class="tab-section">
|
|
||||||
{{#if (can 'create intentions')}}
|
|
||||||
<Portal @target="app-view-actions">
|
|
||||||
<a data-test-create href={{href-to 'dc.services.show.intentions.create'}} class="type-create">Create</a>
|
|
||||||
</Portal>
|
|
||||||
{{/if}}
|
|
||||||
{{#if (gt items.length 0) }}
|
|
||||||
<Consul::Intention::SearchBar
|
|
||||||
@search={{search}}
|
|
||||||
@onsearch={{action (mut search) value="target.value"}}
|
|
||||||
|
|
||||||
@sort={{sort}}
|
as |sort filters items|}}
|
||||||
|
<div class="tab-section">
|
||||||
|
{{#if (can 'create intentions')}}
|
||||||
|
<Portal @target="app-view-actions">
|
||||||
|
<a data-test-create href={{href-to 'dc.services.show.intentions.create'}} class="type-create">Create</a>
|
||||||
|
</Portal>
|
||||||
|
{{/if}}
|
||||||
|
{{#if (gt items.length 0) }}
|
||||||
|
<Consul::Intention::SearchBar
|
||||||
|
@search={{search}}
|
||||||
|
@onsearch={{action (mut search) value="target.value"}}
|
||||||
|
|
||||||
@filter={{filters}}
|
@sort={{sort}}
|
||||||
/>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
<DataWriter
|
@filter={{filters}}
|
||||||
@sink={{concat '/' dc '/' nspace '/intention/'}}
|
/>
|
||||||
@type="intention"
|
{{/if}}
|
||||||
@ondelete={{refresh-route}}
|
|
||||||
as |writer|>
|
<DataWriter
|
||||||
<BlockSlot @name="content">
|
@sink={{concat '/' dc '/' nspace '/intention/'}}
|
||||||
<DataCollection
|
@type="intention"
|
||||||
@type="intention"
|
@ondelete={{refresh-route}}
|
||||||
@sort={{sort.value}}
|
as |writer|>
|
||||||
@filters={{filters}}
|
<BlockSlot @name="content">
|
||||||
@search={{search}}
|
<DataCollection
|
||||||
@items={{items}}
|
@type="intention"
|
||||||
as |collection|>
|
@sort={{sort.value}}
|
||||||
<collection.Collection>
|
@filters={{filters}}
|
||||||
<Consul::Intention::List
|
@search={{search}}
|
||||||
@items={{collection.items}}
|
@items={{items}}
|
||||||
@check={{search}}
|
as |collection|>
|
||||||
@delete={{writer.delete}}
|
<collection.Collection>
|
||||||
as |list|>
|
<Consul::Intention::List
|
||||||
<list.CustomResourceNotice />
|
@items={{collection.items}}
|
||||||
<list.CheckNotice />
|
@check={{search}}
|
||||||
<list.Table @routeName="dc.services.show.intentions.edit" />
|
@delete={{writer.delete}}
|
||||||
</Consul::Intention::List>
|
as |list|>
|
||||||
</collection.Collection>
|
<list.CustomResourceNotice />
|
||||||
<collection.Empty>
|
<list.CheckNotice />
|
||||||
<EmptyState>
|
<list.Table @routeName="dc.services.show.intentions.edit" />
|
||||||
<BlockSlot @name="body">
|
</Consul::Intention::List>
|
||||||
<p>
|
</collection.Collection>
|
||||||
There are no intentions {{if (gt items.length 0) 'found '}} for this service.
|
<collection.Empty>
|
||||||
</p>
|
<EmptyState>
|
||||||
</BlockSlot>
|
<BlockSlot @name="body">
|
||||||
</EmptyState>
|
<p>
|
||||||
</collection.Empty>
|
There are no intentions {{if (gt items.length 0) 'found '}} for this service.
|
||||||
</DataCollection>
|
</p>
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
</DataWriter>
|
</EmptyState>
|
||||||
</div>
|
</collection.Empty>
|
||||||
{{/let}}
|
</DataCollection>
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
</DataLoader>
|
</DataWriter>
|
||||||
|
</div>
|
||||||
|
{{/let}}
|
||||||
|
</BlockSlot>
|
||||||
|
</DataLoader>
|
||||||
|
</Route>
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
<EventSource @src={{chain}} />
|
<Route
|
||||||
<div class="tab-section">
|
@name={{routeName}}
|
||||||
<Consul::DiscoveryChain
|
as |route|>
|
||||||
@chain={{chain.Chain}}
|
<EventSource @src={{chain}} />
|
||||||
/>
|
<div class="tab-section">
|
||||||
</div>
|
<Consul::DiscoveryChain
|
||||||
|
@chain={{chain.Chain}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Route>
|
||||||
|
|
|
@ -1,68 +1,72 @@
|
||||||
<EventSource @src={{items}} />
|
<Route
|
||||||
<div class="tab-section">
|
@name={{routeName}}
|
||||||
{{#let
|
as |route|>
|
||||||
|
<EventSource @src={{items}} />
|
||||||
|
<div class="tab-section">
|
||||||
|
{{#let
|
||||||
|
|
||||||
(hash
|
(hash
|
||||||
value=(or sortBy "Status:asc")
|
value=(or sortBy "Status:asc")
|
||||||
change=(action (mut sortBy) value="target.selected")
|
change=(action (mut sortBy) value="target.selected")
|
||||||
)
|
|
||||||
|
|
||||||
(hash
|
|
||||||
instance=(hash
|
|
||||||
value=(if instance (split instance ',') undefined)
|
|
||||||
change=(action (mut instance) value="target.selectedItems")
|
|
||||||
)
|
)
|
||||||
searchproperty=(hash
|
|
||||||
value=(if (not-eq searchproperty undefined)
|
(hash
|
||||||
(split searchproperty ',')
|
instance=(hash
|
||||||
searchProperties
|
value=(if instance (split instance ',') undefined)
|
||||||
|
change=(action (mut instance) value="target.selectedItems")
|
||||||
|
)
|
||||||
|
searchproperty=(hash
|
||||||
|
value=(if (not-eq searchproperty undefined)
|
||||||
|
(split searchproperty ',')
|
||||||
|
searchProperties
|
||||||
|
)
|
||||||
|
change=(action (mut searchproperty) value="target.selectedItems")
|
||||||
|
default=searchProperties
|
||||||
)
|
)
|
||||||
change=(action (mut searchproperty) value="target.selectedItems")
|
|
||||||
default=searchProperties
|
|
||||||
)
|
)
|
||||||
)
|
|
||||||
|
|
||||||
items
|
items
|
||||||
|
|
||||||
as |sort filters items|}}
|
as |sort filters items|}}
|
||||||
{{#if (gt items.length 0)}}
|
{{#if (gt items.length 0)}}
|
||||||
<input type="checkbox" id="toolbar-toggle" />
|
<input type="checkbox" id="toolbar-toggle" />
|
||||||
<Consul::Upstream::SearchBar
|
<Consul::Upstream::SearchBar
|
||||||
@search={{search}}
|
@search={{search}}
|
||||||
@onsearch={{action (mut search) value="target.value"}}
|
@onsearch={{action (mut search) value="target.value"}}
|
||||||
|
|
||||||
@sort={{sort}}
|
@sort={{sort}}
|
||||||
|
|
||||||
@filter={{filters}}
|
@filter={{filters}}
|
||||||
/>
|
/>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
<p>
|
<p>
|
||||||
The following services may receive traffic from external services through this gateway. Learn more about configuring gateways in our
|
The following services may receive traffic from external services through this gateway. Learn more about configuring gateways in our
|
||||||
<a href="{{env 'CONSUL_DOCS_URL'}}/connect/terminating-gateway" target="_blank" rel="noopener noreferrer">step-by-step guide</a>.
|
<a href="{{env 'CONSUL_DOCS_URL'}}/connect/terminating-gateway" target="_blank" rel="noopener noreferrer">step-by-step guide</a>.
|
||||||
</p>
|
</p>
|
||||||
<DataCollection
|
<DataCollection
|
||||||
@type="service"
|
@type="service"
|
||||||
@sort={{sort.value}}
|
@sort={{sort.value}}
|
||||||
@filters={{filters}}
|
@filters={{filters}}
|
||||||
@search={{search}}
|
@search={{search}}
|
||||||
@items={{items}}
|
@items={{items}}
|
||||||
as |collection|>
|
as |collection|>
|
||||||
<collection.Collection>
|
<collection.Collection>
|
||||||
<Consul::Service::List
|
<Consul::Service::List
|
||||||
@nspace={{nspace}}
|
@nspace={{nspace}}
|
||||||
@items={{collection.items}}
|
@items={{collection.items}}
|
||||||
>
|
>
|
||||||
</Consul::Service::List>
|
</Consul::Service::List>
|
||||||
</collection.Collection>
|
</collection.Collection>
|
||||||
<collection.Empty>
|
<collection.Empty>
|
||||||
<EmptyState>
|
<EmptyState>
|
||||||
<BlockSlot @name="body">
|
<BlockSlot @name="body">
|
||||||
<p>
|
<p>
|
||||||
There are no linked services{{#if (gt items.length 0)}} matching that search{{/if}}.
|
There are no linked services{{#if (gt items.length 0)}} matching that search{{/if}}.
|
||||||
</p>
|
</p>
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
</EmptyState>
|
</EmptyState>
|
||||||
</collection.Empty>
|
</collection.Empty>
|
||||||
</DataCollection>
|
</DataCollection>
|
||||||
{{/let}}
|
{{/let}}
|
||||||
</div>
|
</div>
|
||||||
|
</Route>
|
||||||
|
|
|
@ -1,15 +1,19 @@
|
||||||
<div class="tab-section">
|
<Route
|
||||||
{{#let (flatten (map-by "Tags" items)) as |tags|}}
|
@name={{routeName}}
|
||||||
{{#if (gt tags.length 0) }}
|
as |route|>
|
||||||
<TagList @item={{hash Tags=tags}} />
|
<div class="tab-section">
|
||||||
{{else}}
|
{{#let (flatten (map-by "Tags" items)) as |tags|}}
|
||||||
<EmptyState>
|
{{#if (gt tags.length 0) }}
|
||||||
<BlockSlot @name="body">
|
<TagList @item={{hash Tags=tags}} />
|
||||||
<p>
|
{{else}}
|
||||||
There are no tags.
|
<EmptyState>
|
||||||
</p>
|
<BlockSlot @name="body">
|
||||||
</BlockSlot>
|
<p>
|
||||||
</EmptyState>
|
There are no tags.
|
||||||
{{/if}}
|
</p>
|
||||||
{{/let}}
|
</BlockSlot>
|
||||||
</div>
|
</EmptyState>
|
||||||
|
{{/if}}
|
||||||
|
{{/let}}
|
||||||
|
</div>
|
||||||
|
</Route>
|
||||||
|
|
|
@ -1,39 +1,43 @@
|
||||||
<EventSource @src={{topology}} />
|
<Route
|
||||||
<div class="tab-section">
|
@name={{routeName}}
|
||||||
{{#if (and (eq topology.Upstreams.length 0) (eq topology.Downstreams.length 0))}}
|
as |route|>
|
||||||
<EmptyState>
|
<EventSource @src={{topology}} />
|
||||||
<BlockSlot @name="header">
|
<div class="tab-section">
|
||||||
<h2>
|
{{#if (and (eq topology.Upstreams.length 0) (eq topology.Downstreams.length 0))}}
|
||||||
No dependencies
|
<EmptyState>
|
||||||
</h2>
|
<BlockSlot @name="header">
|
||||||
</BlockSlot>
|
<h2>
|
||||||
<BlockSlot @name="body">
|
No dependencies
|
||||||
<p>
|
</h2>
|
||||||
This service has neither downstreams nor upstreams, which means that no services are configured to connect with it. Add upstreams and intentions to ensure this service is connected with the rest of your service mesh.
|
</BlockSlot>
|
||||||
</p>
|
<BlockSlot @name="body">
|
||||||
</BlockSlot>
|
<p>
|
||||||
<BlockSlot @name="actions">
|
This service has neither downstreams nor upstreams, which means that no services are configured to connect with it. Add upstreams and intentions to ensure this service is connected with the rest of your service mesh.
|
||||||
<li class="docs-link">
|
</p>
|
||||||
<a href="{{env 'CONSUL_DOCS_URL'}}/connect/registration/service-registration#complete-configuration-example" rel="noopener noreferrer" target="_blank">Documentation on upstreams</a>
|
</BlockSlot>
|
||||||
</li>
|
<BlockSlot @name="actions">
|
||||||
</BlockSlot>
|
<li class="docs-link">
|
||||||
</EmptyState>
|
<a href="{{env 'CONSUL_DOCS_URL'}}/connect/registration/service-registration#complete-configuration-example" rel="noopener noreferrer" target="_blank">Documentation on upstreams</a>
|
||||||
{{else}}
|
</li>
|
||||||
{{#if topology.FilteredByACLs}}
|
</BlockSlot>
|
||||||
<TopologyMetrics::Notice::LimitedAccess />
|
</EmptyState>
|
||||||
|
{{else}}
|
||||||
|
{{#if topology.FilteredByACLs}}
|
||||||
|
<TopologyMetrics::Notice::LimitedAccess />
|
||||||
|
{{/if}}
|
||||||
|
<TopologyMetrics
|
||||||
|
@nspace={{nspace}}
|
||||||
|
@dc={{dc.Name}}
|
||||||
|
@service={{items.firstObject}}
|
||||||
|
@topology={{topology}}
|
||||||
|
@metricsHref={{render-template urls.service (hash
|
||||||
|
Datacenter=dc.Name
|
||||||
|
Service=items.firstObject
|
||||||
|
)}}
|
||||||
|
@isRemoteDC={{not dc.Local}}
|
||||||
|
@hasMetricsProvider={{hasMetricsProvider}}
|
||||||
|
@oncreate={{route-action 'createIntention'}}
|
||||||
|
/>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
<TopologyMetrics
|
</div>
|
||||||
@nspace={{nspace}}
|
</Route>
|
||||||
@dc={{dc.Name}}
|
|
||||||
@service={{items.firstObject}}
|
|
||||||
@topology={{topology}}
|
|
||||||
@metricsHref={{render-template urls.service (hash
|
|
||||||
Datacenter=dc.Name
|
|
||||||
Service=items.firstObject
|
|
||||||
)}}
|
|
||||||
@isRemoteDC={{not dc.Local}}
|
|
||||||
@hasMetricsProvider={{hasMetricsProvider}}
|
|
||||||
@oncreate={{route-action 'createIntention'}}
|
|
||||||
/>
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
|
|
|
@ -1,68 +1,72 @@
|
||||||
<EventSource @src={{items}} />
|
<Route
|
||||||
<div class="tab-section">
|
@name={{routeName}}
|
||||||
{{#let
|
as |route|>
|
||||||
|
<EventSource @src={{items}} />
|
||||||
|
<div class="tab-section">
|
||||||
|
{{#let
|
||||||
|
|
||||||
(hash
|
(hash
|
||||||
value=(or sortBy "Status:asc")
|
value=(or sortBy "Status:asc")
|
||||||
change=(action (mut sortBy) value="target.selected")
|
change=(action (mut sortBy) value="target.selected")
|
||||||
)
|
|
||||||
|
|
||||||
(hash
|
|
||||||
instance=(hash
|
|
||||||
value=(if instance (split instance ',') undefined)
|
|
||||||
change=(action (mut instance) value="target.selectedItems")
|
|
||||||
)
|
)
|
||||||
searchproperty=(hash
|
|
||||||
value=(if (not-eq searchproperty undefined)
|
(hash
|
||||||
(split searchproperty ',')
|
instance=(hash
|
||||||
searchProperties
|
value=(if instance (split instance ',') undefined)
|
||||||
|
change=(action (mut instance) value="target.selectedItems")
|
||||||
|
)
|
||||||
|
searchproperty=(hash
|
||||||
|
value=(if (not-eq searchproperty undefined)
|
||||||
|
(split searchproperty ',')
|
||||||
|
searchProperties
|
||||||
|
)
|
||||||
|
change=(action (mut searchproperty) value="target.selectedItems")
|
||||||
|
default=searchProperties
|
||||||
)
|
)
|
||||||
change=(action (mut searchproperty) value="target.selectedItems")
|
|
||||||
default=searchProperties
|
|
||||||
)
|
)
|
||||||
)
|
|
||||||
|
|
||||||
items
|
items
|
||||||
|
|
||||||
as |sort filters items|}}
|
as |sort filters items|}}
|
||||||
{{#if (gt items.length 0)}}
|
{{#if (gt items.length 0)}}
|
||||||
<input type="checkbox" id="toolbar-toggle" />
|
<input type="checkbox" id="toolbar-toggle" />
|
||||||
<Consul::Upstream::SearchBar
|
<Consul::Upstream::SearchBar
|
||||||
@search={{search}}
|
|
||||||
@onsearch={{action (mut search) value="target.value"}}
|
|
||||||
|
|
||||||
@sort={{sort}}
|
|
||||||
|
|
||||||
@filter={{filters}}
|
|
||||||
/>
|
|
||||||
{{/if}}
|
|
||||||
<p>
|
|
||||||
Upstreams are services that may receive traffic from this gateway. Learn more about configuring gateways in our <a href="{{env 'CONSUL_DOCS_URL'}}/connect/ingress-gateway" target="_blank" rel="noopener noreferrer">documentation</a>.
|
|
||||||
</p>
|
|
||||||
<DataCollection
|
|
||||||
@type="service"
|
|
||||||
@sort={{sort.value}}
|
|
||||||
@filters={{filters}}
|
|
||||||
@search={{search}}
|
@search={{search}}
|
||||||
@items={{items}}
|
@onsearch={{action (mut search) value="target.value"}}
|
||||||
as |collection|>
|
|
||||||
<collection.Collection>
|
@sort={{sort}}
|
||||||
<Consul::Upstream::List
|
|
||||||
@items={{collection.items}}
|
@filter={{filters}}
|
||||||
@dc={{dc}}
|
/>
|
||||||
@nspace={{nspace}}
|
{{/if}}
|
||||||
>
|
<p>
|
||||||
</Consul::Upstream::List>
|
Upstreams are services that may receive traffic from this gateway. Learn more about configuring gateways in our <a href="{{env 'CONSUL_DOCS_URL'}}/connect/ingress-gateway" target="_blank" rel="noopener noreferrer">documentation</a>.
|
||||||
</collection.Collection>
|
</p>
|
||||||
<collection.Empty>
|
<DataCollection
|
||||||
<EmptyState>
|
@type="service"
|
||||||
<BlockSlot @name="body">
|
@sort={{sort.value}}
|
||||||
<p>
|
@filters={{filters}}
|
||||||
There are no upstreams{{#if (gt items.length 0)}} matching that search{{/if}}.
|
@search={{search}}
|
||||||
</p>
|
@items={{items}}
|
||||||
</BlockSlot>
|
as |collection|>
|
||||||
</EmptyState>
|
<collection.Collection>
|
||||||
</collection.Empty>
|
<Consul::Upstream::List
|
||||||
</DataCollection>
|
@items={{collection.items}}
|
||||||
{{/let}}
|
@dc={{dc}}
|
||||||
</div>
|
@nspace={{nspace}}
|
||||||
|
>
|
||||||
|
</Consul::Upstream::List>
|
||||||
|
</collection.Collection>
|
||||||
|
<collection.Empty>
|
||||||
|
<EmptyState>
|
||||||
|
<BlockSlot @name="body">
|
||||||
|
<p>
|
||||||
|
There are no upstreams{{#if (gt items.length 0)}} matching that search{{/if}}.
|
||||||
|
</p>
|
||||||
|
</BlockSlot>
|
||||||
|
</EmptyState>
|
||||||
|
</collection.Empty>
|
||||||
|
</DataCollection>
|
||||||
|
{{/let}}
|
||||||
|
</div>
|
||||||
|
</Route>
|
||||||
|
|
|
@ -1,5 +1,10 @@
|
||||||
<Outlet
|
<Route
|
||||||
@name={{routeName}}
|
@name={{routeName}}
|
||||||
as |o|>
|
as |route|>
|
||||||
{{outlet}}
|
<Outlet
|
||||||
</Outlet>
|
@name={{routeName}}
|
||||||
|
@model={{route.model}}
|
||||||
|
as |o|>
|
||||||
|
{{outlet}}
|
||||||
|
</Outlet>
|
||||||
|
</Route>
|
||||||
|
|
|
@ -51,7 +51,7 @@ Feature: dc / acls / roles / as-many / add-new: Add new
|
||||||
And "[data-notification]" has the "notification-update" class
|
And "[data-notification]" has the "notification-update" class
|
||||||
And "[data-notification]" has the "success" class
|
And "[data-notification]" has the "success" class
|
||||||
Scenario: Add Role that has an existing Policy
|
Scenario: Add Role that has an existing Policy
|
||||||
And I click "#new-role-toggle + div .ember-power-select-trigger"
|
And I click "#new-role .ember-power-select-trigger"
|
||||||
And I click ".ember-power-select-option:first-child"
|
And I click ".ember-power-select-option:first-child"
|
||||||
And I click submit on the roles.form
|
And I click submit on the roles.form
|
||||||
Then a PUT request was made to "/v1/acl/role?dc=datacenter" from yaml
|
Then a PUT request was made to "/v1/acl/role?dc=datacenter" from yaml
|
||||||
|
|
Loading…
Reference in New Issue