mirror of https://github.com/status-im/consul.git
ui: Improved filtering and sorting (#8591)
This commit is contained in:
parent
a9df6ac50b
commit
31ad6e39ca
|
@ -0,0 +1,74 @@
|
||||||
|
<form class="filter-bar">
|
||||||
|
<FreetextFilter
|
||||||
|
@onsearch={{action onsearch}}
|
||||||
|
@value={{search}}
|
||||||
|
@placeholder="Search"
|
||||||
|
/>
|
||||||
|
<div class="filters">
|
||||||
|
<PopoverSelect
|
||||||
|
@position="left"
|
||||||
|
@onchange={{action onfilter.access}}
|
||||||
|
@multiple={{true}}
|
||||||
|
as |components|>
|
||||||
|
<BlockSlot @name="selected">
|
||||||
|
<span>
|
||||||
|
Permissions
|
||||||
|
</span>
|
||||||
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="options">
|
||||||
|
{{#let components.Optgroup components.Option as |Optgroup Option|}}
|
||||||
|
<Option @value="allow" @selected={{contains 'allow' filter.accesses}}>Allow</Option>
|
||||||
|
<Option @value="deny" @selected={{contains 'deny' filter.accesses}}>Deny</Option>
|
||||||
|
{{/let}}
|
||||||
|
</BlockSlot>
|
||||||
|
</PopoverSelect>
|
||||||
|
</div>
|
||||||
|
<div class="sort">
|
||||||
|
<PopoverSelect
|
||||||
|
class="type-sort"
|
||||||
|
data-test-sort-control
|
||||||
|
@position="right"
|
||||||
|
@onchange={{action onsort}}
|
||||||
|
@multiple={{false}}
|
||||||
|
as |components|>
|
||||||
|
<BlockSlot @name="selected">
|
||||||
|
<span>
|
||||||
|
{{#let (from-entries (array
|
||||||
|
(array "Action:asc" "Allow to Deny")
|
||||||
|
(array "Action:desc" "Deny to Allow")
|
||||||
|
(array "SourceName:asc" "Source: A to Z")
|
||||||
|
(array "SourceName:desc" "Source: Z to A")
|
||||||
|
(array "DestinationName:asc" "Destination: A to Z")
|
||||||
|
(array "DestinationName:desc" "Destination: Z to A")
|
||||||
|
(array "Precedence:asc" "Precedence: Ascending")
|
||||||
|
(array "Precedence:desc" "Precedence: Descending")
|
||||||
|
))
|
||||||
|
as |selectable|
|
||||||
|
}}
|
||||||
|
{{get selectable sort}}
|
||||||
|
{{/let}}
|
||||||
|
</span>
|
||||||
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="options">
|
||||||
|
{{#let components.Optgroup components.Option as |Optgroup Option|}}
|
||||||
|
<Optgroup @label="Permission">
|
||||||
|
<Option @value="Action:asc" @selected={{eq "Action:asc" sort}}>Allow to Deny</Option>
|
||||||
|
<Option @value="Action:desc" @selected={{eq "Action:desc" sort}}>Deny to Allow</Option>
|
||||||
|
</Optgroup>
|
||||||
|
<Optgroup @label="Source">
|
||||||
|
<Option @value="SourceName:asc" @selected={{eq "SourceName:asc" sort}}>A to Z</Option>
|
||||||
|
<Option @value="SourceName:desc" @selected={{eq "SourceName:desc" sort}}>Z to A</Option>
|
||||||
|
</Optgroup>
|
||||||
|
<Optgroup @label="Destination">
|
||||||
|
<Option @value="DestinationName:asc" @selected={{eq "DestinationName:asc" sort}}>A to Z</Option>
|
||||||
|
<Option @value="DestinationName:desc" @selected={{eq "DestinationName:desc" sort}}>Z to A</Option>
|
||||||
|
</Optgroup>
|
||||||
|
<Optgroup @label="Precedence">
|
||||||
|
<Option @value="Precedence:asc" @selected={{eq "Precedence:asc" sort}}>Ascending</Option>
|
||||||
|
<Option @value="Precedence:desc" @selected={{eq "Precedence:desc" sort}}>Descending</Option>
|
||||||
|
</Optgroup>
|
||||||
|
{{/let}}
|
||||||
|
</BlockSlot>
|
||||||
|
</PopoverSelect>
|
||||||
|
</div>
|
||||||
|
</form>
|
|
@ -0,0 +1,5 @@
|
||||||
|
import Component from '@ember/component';
|
||||||
|
|
||||||
|
export default Component.extend({
|
||||||
|
tagName: '',
|
||||||
|
});
|
|
@ -0,0 +1,65 @@
|
||||||
|
<form class="filter-bar">
|
||||||
|
<FreetextFilter
|
||||||
|
@onsearch={{action onsearch}}
|
||||||
|
@value={{search}}
|
||||||
|
@placeholder="Search"
|
||||||
|
/>
|
||||||
|
<div class="filters">
|
||||||
|
<PopoverSelect
|
||||||
|
class="type-status"
|
||||||
|
@position="left"
|
||||||
|
@onchange={{action onfilter.status}}
|
||||||
|
@multiple={{true}}
|
||||||
|
as |components|>
|
||||||
|
<BlockSlot @name="selected">
|
||||||
|
<span>
|
||||||
|
Health Status
|
||||||
|
</span>
|
||||||
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="options">
|
||||||
|
{{#let components.Optgroup components.Option as |Optgroup Option|}}
|
||||||
|
<Option class="value-passing" @value="passing" @selected={{contains 'passing' filter.statuses}}>Passing</Option>
|
||||||
|
<Option class="value-warning" @value="warning" @selected={{contains 'warning' filter.statuses}}>Warning</Option>
|
||||||
|
<Option class="value-critical" @value="critical" @selected={{contains 'critical' filter.statuses}}>Failing</Option>
|
||||||
|
<Option class="value-empty" @value="empty" @selected={{contains 'empty' filter.statuses}}>No checks</Option>
|
||||||
|
{{/let}}
|
||||||
|
</BlockSlot>
|
||||||
|
</PopoverSelect>
|
||||||
|
</div>
|
||||||
|
<div class="sort">
|
||||||
|
<PopoverSelect
|
||||||
|
class="type-sort"
|
||||||
|
data-test-sort-control
|
||||||
|
@position="right"
|
||||||
|
@onchange={{action onsort}}
|
||||||
|
@multiple={{false}}
|
||||||
|
as |components|>
|
||||||
|
<BlockSlot @name="selected">
|
||||||
|
<span>
|
||||||
|
{{#let (from-entries (array
|
||||||
|
(array "Node:asc" "A to Z")
|
||||||
|
(array "Node:desc" "Z to A")
|
||||||
|
(array "Status:asc" "Unhealthy to Healthy")
|
||||||
|
(array "Status:desc" "Healthy to Unhealthy")
|
||||||
|
))
|
||||||
|
as |selectable|
|
||||||
|
}}
|
||||||
|
{{get selectable sort}}
|
||||||
|
{{/let}}
|
||||||
|
</span>
|
||||||
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="options">
|
||||||
|
{{#let components.Optgroup components.Option as |Optgroup Option|}}
|
||||||
|
<Optgroup @label="Health Status">
|
||||||
|
<Option @value="Status:asc" @selected={{eq "Status:asc" sort}}>Unhealthy to Healthy</Option>
|
||||||
|
<Option @value="Status:desc" @selected={{eq "Status:desc" sort}}>Healthy to Unhealthy</Option>
|
||||||
|
</Optgroup>
|
||||||
|
<Optgroup @label="Service Name">
|
||||||
|
<Option @value="Node:asc" @selected={{eq "Node:asc" sort}}>A to Z</Option>
|
||||||
|
<Option @value="Node:desc" @selected={{eq "Node:desc" sort}}>Z to A</Option>
|
||||||
|
</Optgroup>
|
||||||
|
{{/let}}
|
||||||
|
</BlockSlot>
|
||||||
|
</PopoverSelect>
|
||||||
|
</div>
|
||||||
|
</form>
|
|
@ -0,0 +1,5 @@
|
||||||
|
import Component from '@ember/component';
|
||||||
|
|
||||||
|
export default Component.extend({
|
||||||
|
tagName: '',
|
||||||
|
});
|
|
@ -0,0 +1,37 @@
|
||||||
|
<form class="filter-bar">
|
||||||
|
<FreetextFilter
|
||||||
|
@onsearch={{action onsearch}}
|
||||||
|
@value={{search}}
|
||||||
|
@placeholder="Search"
|
||||||
|
/>
|
||||||
|
<div class="sort">
|
||||||
|
<PopoverSelect
|
||||||
|
class="type-sort"
|
||||||
|
data-test-sort-control
|
||||||
|
@position="right"
|
||||||
|
@onchange={{action onsort}}
|
||||||
|
@multiple={{false}}
|
||||||
|
as |components|>
|
||||||
|
<BlockSlot @name="selected">
|
||||||
|
<span>
|
||||||
|
{{#let (from-entries (array
|
||||||
|
(array "Name:asc" "A to Z")
|
||||||
|
(array "Name:desc" "Z to A")
|
||||||
|
))
|
||||||
|
as |selectable|
|
||||||
|
}}
|
||||||
|
{{get selectable sort}}
|
||||||
|
{{/let}}
|
||||||
|
</span>
|
||||||
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="options">
|
||||||
|
{{#let components.Optgroup components.Option as |Optgroup Option|}}
|
||||||
|
<Optgroup @label="Name">
|
||||||
|
<Option @value="Name:asc" @selected={{eq "Name:asc" sort}}>A to Z</Option>
|
||||||
|
<Option @value="Name:desc" @selected={{eq "Name:desc" sort}}>Z to A</Option>
|
||||||
|
</Optgroup>
|
||||||
|
{{/let}}
|
||||||
|
</BlockSlot>
|
||||||
|
</PopoverSelect>
|
||||||
|
</div>
|
||||||
|
</form>
|
|
@ -0,0 +1,5 @@
|
||||||
|
import Component from '@ember/component';
|
||||||
|
|
||||||
|
export default Component.extend({
|
||||||
|
tagName: '',
|
||||||
|
});
|
|
@ -0,0 +1,77 @@
|
||||||
|
<form class="filter-bar">
|
||||||
|
<FreetextFilter
|
||||||
|
@onsearch={{action onsearch}}
|
||||||
|
@value={{search}}
|
||||||
|
@placeholder="Search"
|
||||||
|
/>
|
||||||
|
<div class="filters">
|
||||||
|
<PopoverSelect
|
||||||
|
class="select-dcs"
|
||||||
|
@position="left"
|
||||||
|
@onchange={{action onfilter.dc}}
|
||||||
|
@multiple={{true}}
|
||||||
|
as |components|>
|
||||||
|
<BlockSlot @name="selected">
|
||||||
|
<span>
|
||||||
|
Datacenters
|
||||||
|
</span>
|
||||||
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="options">
|
||||||
|
{{#let components.Optgroup components.Option as |Optgroup Option|}}
|
||||||
|
{{#each dcs as |dc|}}
|
||||||
|
<Option @value={{dc.Name}} @selected={{contains dc.Name filter.dcs}}>{{dc.Name}}</Option>
|
||||||
|
{{/each}}
|
||||||
|
{{/let}}
|
||||||
|
<DataSource @src="/*/*/datacenters" @loading="lazy" @onchange={{action (mut dcs) value="data"}} />
|
||||||
|
</BlockSlot>
|
||||||
|
</PopoverSelect>
|
||||||
|
<PopoverSelect
|
||||||
|
class="select-type"
|
||||||
|
@position="left"
|
||||||
|
@onchange={{action onfilter.type}}
|
||||||
|
@multiple={{true}}
|
||||||
|
as |components|>
|
||||||
|
<BlockSlot @name="selected">
|
||||||
|
<span>
|
||||||
|
Type
|
||||||
|
</span>
|
||||||
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="options">
|
||||||
|
{{#let components.Optgroup components.Option as |Optgroup Option|}}
|
||||||
|
<Option @value="global-management" @selected={{contains 'global-management' filter.types}}>Global Management</Option>
|
||||||
|
<Option @value="standard" @selected={{contains 'standard' filter.types}}>Standard</Option>
|
||||||
|
{{/let}}
|
||||||
|
</BlockSlot>
|
||||||
|
</PopoverSelect>
|
||||||
|
</div>
|
||||||
|
<div class="sort">
|
||||||
|
<PopoverSelect
|
||||||
|
class="type-sort"
|
||||||
|
data-test-sort-control
|
||||||
|
@position="right"
|
||||||
|
@onchange={{action onsort}}
|
||||||
|
@multiple={{false}}
|
||||||
|
as |components|>
|
||||||
|
<BlockSlot @name="selected">
|
||||||
|
<span>
|
||||||
|
{{#let (from-entries (array
|
||||||
|
(array "Name:asc" "A to Z")
|
||||||
|
(array "Name:desc" "Z to A")
|
||||||
|
))
|
||||||
|
as |selectable|
|
||||||
|
}}
|
||||||
|
{{get selectable sort}}
|
||||||
|
{{/let}}
|
||||||
|
</span>
|
||||||
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="options">
|
||||||
|
{{#let components.Optgroup components.Option as |Optgroup Option|}}
|
||||||
|
<Optgroup @label="Name">
|
||||||
|
<Option @value="Name:asc" @selected={{eq "Name:asc" sort}}>A to Z</Option>
|
||||||
|
<Option @value="Name:desc" @selected={{eq "Name:desc" sort}}>Z to A</Option>
|
||||||
|
</Optgroup>
|
||||||
|
{{/let}}
|
||||||
|
</BlockSlot>
|
||||||
|
</PopoverSelect>
|
||||||
|
</div>
|
||||||
|
</form>
|
|
@ -0,0 +1,3 @@
|
||||||
|
import Component from '@ember/component';
|
||||||
|
|
||||||
|
export default Component.extend({});
|
|
@ -0,0 +1,43 @@
|
||||||
|
<form class="filter-bar">
|
||||||
|
<FreetextFilter
|
||||||
|
@onsearch={{action onsearch}}
|
||||||
|
@value={{search}}
|
||||||
|
@placeholder="Search"
|
||||||
|
/>
|
||||||
|
<div class="sort">
|
||||||
|
<PopoverSelect
|
||||||
|
class="type-sort"
|
||||||
|
data-test-sort-control
|
||||||
|
@position="right"
|
||||||
|
@onchange={{action onsort}}
|
||||||
|
@multiple={{false}}
|
||||||
|
as |components|>
|
||||||
|
<BlockSlot @name="selected">
|
||||||
|
<span>
|
||||||
|
{{#let (from-entries (array
|
||||||
|
(array "Name:asc" "A to Z")
|
||||||
|
(array "Name:desc" "Z to A")
|
||||||
|
(array "CreateIndex:desc" "Newest to oldest")
|
||||||
|
(array "CreateIndex:asc" "Oldest to newest")
|
||||||
|
))
|
||||||
|
as |selectable|
|
||||||
|
}}
|
||||||
|
{{get selectable sort}}
|
||||||
|
{{/let}}
|
||||||
|
</span>
|
||||||
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="options">
|
||||||
|
{{#let components.Optgroup components.Option as |Optgroup Option|}}
|
||||||
|
<Optgroup @label="Name">
|
||||||
|
<Option @value="Name:asc" @selected={{eq "Name:asc" sort}}>A to Z</Option>
|
||||||
|
<Option @value="Name:desc" @selected={{eq "Name:desc" sort}}>Z to A</Option>
|
||||||
|
</Optgroup>
|
||||||
|
<Optgroup @label="Creation">
|
||||||
|
<Option @value="CreateIndex:desc" @selected={{eq "CreateIndex:desc" sort}}>Newest to oldest</Option>
|
||||||
|
<Option @value="CreateIndex:asc" @selected={{eq "CreateIndex:asc" sort}}>Oldest to newest</Option>
|
||||||
|
</Optgroup>
|
||||||
|
{{/let}}
|
||||||
|
</BlockSlot>
|
||||||
|
</PopoverSelect>
|
||||||
|
</div>
|
||||||
|
</form>
|
|
@ -0,0 +1,3 @@
|
||||||
|
import Component from '@ember/component';
|
||||||
|
|
||||||
|
export default Component.extend({});
|
|
@ -0,0 +1,86 @@
|
||||||
|
<form class="filter-bar">
|
||||||
|
<FreetextFilter
|
||||||
|
@onsearch={{action onsearch}}
|
||||||
|
@value={{search}}
|
||||||
|
@placeholder="Search"
|
||||||
|
/>
|
||||||
|
<div class="filters">
|
||||||
|
<PopoverSelect
|
||||||
|
class="type-status"
|
||||||
|
@position="left"
|
||||||
|
@onchange={{action onfilter.status}}
|
||||||
|
@multiple={{true}}
|
||||||
|
as |components|>
|
||||||
|
<BlockSlot @name="selected">
|
||||||
|
<span>
|
||||||
|
Health Status
|
||||||
|
</span>
|
||||||
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="options">
|
||||||
|
{{#let components.Optgroup components.Option as |Optgroup Option|}}
|
||||||
|
<Option class="value-passing" @value="passing" @selected={{contains 'passing' filter.statuses}}>Passing</Option>
|
||||||
|
<Option class="value-warning" @value="warning" @selected={{contains 'warning' filter.statuses}}>Warning</Option>
|
||||||
|
<Option class="value-critical" @value="critical" @selected={{contains 'critical' filter.statuses}}>Failing</Option>
|
||||||
|
<Option class="value-empty" @value="empty" @selected={{contains 'empty' filter.statuses}}>No checks</Option>
|
||||||
|
{{/let}}
|
||||||
|
</BlockSlot>
|
||||||
|
</PopoverSelect>
|
||||||
|
{{#if (gt sources.length 0)}}
|
||||||
|
<PopoverSelect
|
||||||
|
class="type-source"
|
||||||
|
@position="left"
|
||||||
|
@onchange={{action onfilter.source}}
|
||||||
|
@multiple={{true}}
|
||||||
|
as |components|>
|
||||||
|
<BlockSlot @name="selected">
|
||||||
|
<span>
|
||||||
|
Source
|
||||||
|
</span>
|
||||||
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="options">
|
||||||
|
{{#let components.Optgroup components.Option as |Optgroup Option|}}
|
||||||
|
{{#each sources as |source|}}
|
||||||
|
<Option class={{source}} @value={{source}} @selected={{contains source filter.sources}}>{{source}}</Option>
|
||||||
|
{{/each}}
|
||||||
|
{{/let}}
|
||||||
|
</BlockSlot>
|
||||||
|
</PopoverSelect>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
<div class="sort">
|
||||||
|
<PopoverSelect
|
||||||
|
class="type-sort"
|
||||||
|
data-test-sort-control
|
||||||
|
@position="right"
|
||||||
|
@onchange={{action onsort}}
|
||||||
|
@multiple={{false}}
|
||||||
|
as |components|>
|
||||||
|
<BlockSlot @name="selected">
|
||||||
|
<span>
|
||||||
|
{{#let (from-entries (array
|
||||||
|
(array "Name:asc" "A to Z")
|
||||||
|
(array "Name:desc" "Z to A")
|
||||||
|
(array "Status:asc" "Unhealthy to Healthy")
|
||||||
|
(array "Status:desc" "Healthy to Unhealthy")
|
||||||
|
))
|
||||||
|
as |selectable|
|
||||||
|
}}
|
||||||
|
{{get selectable sort}}
|
||||||
|
{{/let}}
|
||||||
|
</span>
|
||||||
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="options">
|
||||||
|
{{#let components.Optgroup components.Option as |Optgroup Option|}}
|
||||||
|
<Optgroup @label="Health Status">
|
||||||
|
<Option @value="Status:asc" @selected={{eq "Status:asc" sort}}>Unhealthy to Healthy</Option>
|
||||||
|
<Option @value="Status:desc" @selected={{eq "Status:desc" sort}}>Healthy to Unhealthy</Option>
|
||||||
|
</Optgroup>
|
||||||
|
<Optgroup @label="Service Name">
|
||||||
|
<Option @value="Name:asc" @selected={{eq "Name:asc" sort}}>A to Z</Option>
|
||||||
|
<Option @value="Name:desc" @selected={{eq "Name:desc" sort}}>Z to A</Option>
|
||||||
|
</Optgroup>
|
||||||
|
{{/let}}
|
||||||
|
</BlockSlot>
|
||||||
|
</PopoverSelect>
|
||||||
|
</div>
|
||||||
|
</form>
|
|
@ -0,0 +1,5 @@
|
||||||
|
import Component from '@ember/component';
|
||||||
|
|
||||||
|
export default Component.extend({
|
||||||
|
tagName: '',
|
||||||
|
});
|
|
@ -0,0 +1,111 @@
|
||||||
|
<form class="filter-bar">
|
||||||
|
<FreetextFilter
|
||||||
|
@onsearch={{action onsearch}}
|
||||||
|
@value={{search}}
|
||||||
|
@placeholder="Search"
|
||||||
|
/>
|
||||||
|
<div class="filters">
|
||||||
|
<PopoverSelect
|
||||||
|
class="type-status"
|
||||||
|
@position="left"
|
||||||
|
@onchange={{action onfilter.status}}
|
||||||
|
@multiple={{true}}
|
||||||
|
as |components|>
|
||||||
|
<BlockSlot @name="selected">
|
||||||
|
<span>
|
||||||
|
Health Status
|
||||||
|
</span>
|
||||||
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="options">
|
||||||
|
{{#let components.Optgroup components.Option as |Optgroup Option|}}
|
||||||
|
<Option class="value-passing" @value="passing" @selected={{contains 'passing' filter.statuses}}>Passing</Option>
|
||||||
|
<Option class="value-warning" @value="warning" @selected={{contains 'warning' filter.statuses}}>Warning</Option>
|
||||||
|
<Option class="value-critical" @value="critical" @selected={{contains 'critical' filter.statuses}}>Failing</Option>
|
||||||
|
<Option class="value-empty" @value="empty" @selected={{contains 'empty' filter.statuses}}>No checks</Option>
|
||||||
|
{{/let}}
|
||||||
|
</BlockSlot>
|
||||||
|
</PopoverSelect>
|
||||||
|
<PopoverSelect
|
||||||
|
@position="left"
|
||||||
|
@onchange={{action onfilter.type}}
|
||||||
|
@multiple={{true}}
|
||||||
|
as |components|>
|
||||||
|
<BlockSlot @name="selected">
|
||||||
|
<span>
|
||||||
|
Service Type
|
||||||
|
</span>
|
||||||
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="options">
|
||||||
|
{{#let components.Optgroup components.Option as |Optgroup Option|}}
|
||||||
|
<Option @value="service" @selected={{contains 'service' filter.types}}>Service</Option>
|
||||||
|
<Optgroup @label="Gateway">
|
||||||
|
<Option @value="ingress-gateway" @selected={{contains 'ingress-gateway' filter.types}}>Ingress Gateway</Option>
|
||||||
|
<Option @value="terminating-gateway" @selected={{contains 'terminating-gateway' filter.types}}>Terminating Gateway</Option>
|
||||||
|
<Option @value="mesh-gateway" @selected={{contains 'mesh-gateway' filter.types}}>Mesh Gateway</Option>
|
||||||
|
</Optgroup>
|
||||||
|
<Optgroup @label="Mesh">
|
||||||
|
<Option @value="mesh-enabled" @selected={{contains 'mesh-enabled' filter.types}}>In service mesh</Option>
|
||||||
|
<Option @value="mesh-disabled" @selected={{contains 'mesh-disabled' filter.types}}>Not in service mesh</Option>
|
||||||
|
</Optgroup>
|
||||||
|
{{/let}}
|
||||||
|
</BlockSlot>
|
||||||
|
</PopoverSelect>
|
||||||
|
{{#if (gt sources.length 0)}}
|
||||||
|
<PopoverSelect
|
||||||
|
class="type-source"
|
||||||
|
@position="left"
|
||||||
|
@onchange={{action onfilter.source}}
|
||||||
|
@multiple={{true}}
|
||||||
|
as |components|>
|
||||||
|
<BlockSlot @name="selected">
|
||||||
|
<span>
|
||||||
|
Source
|
||||||
|
</span>
|
||||||
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="options">
|
||||||
|
{{#let components.Optgroup components.Option as |Optgroup Option|}}
|
||||||
|
{{#each sources as |source|}}
|
||||||
|
<Option class={{source}} @value={{source}} @selected={{contains source filter.sources}}>{{source}}</Option>
|
||||||
|
{{/each}}
|
||||||
|
{{/let}}
|
||||||
|
</BlockSlot>
|
||||||
|
</PopoverSelect>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
<div class="sort">
|
||||||
|
<PopoverSelect
|
||||||
|
class="type-sort"
|
||||||
|
data-test-sort-control
|
||||||
|
@position="right"
|
||||||
|
@onchange={{action onsort}}
|
||||||
|
@multiple={{false}}
|
||||||
|
as |components|>
|
||||||
|
<BlockSlot @name="selected">
|
||||||
|
<span>
|
||||||
|
{{#let (from-entries (array
|
||||||
|
(array "Name:asc" "A to Z")
|
||||||
|
(array "Name:desc" "Z to A")
|
||||||
|
(array "Status:asc" "Unhealthy to Healthy")
|
||||||
|
(array "Status:desc" "Healthy to Unhealthy")
|
||||||
|
))
|
||||||
|
as |selectable|
|
||||||
|
}}
|
||||||
|
{{get selectable sort}}
|
||||||
|
{{/let}}
|
||||||
|
</span>
|
||||||
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="options">
|
||||||
|
{{#let components.Optgroup components.Option as |Optgroup Option|}}
|
||||||
|
<Optgroup @label="Health Status">
|
||||||
|
<Option @value="Status:asc" @selected={{eq "Status:asc" sort}}>Unhealthy to Healthy</Option>
|
||||||
|
<Option @value="Status:desc" @selected={{eq "Status:desc" sort}}>Healthy to Unhealthy</Option>
|
||||||
|
</Optgroup>
|
||||||
|
<Optgroup @label="Service Name">
|
||||||
|
<Option @value="Name:asc" @selected={{eq "Name:asc" sort}}>A to Z</Option>
|
||||||
|
<Option @value="Name:desc" @selected={{eq "Name:desc" sort}}>Z to A</Option>
|
||||||
|
</Optgroup>
|
||||||
|
{{/let}}
|
||||||
|
</BlockSlot>
|
||||||
|
</PopoverSelect>
|
||||||
|
</div>
|
||||||
|
</form>
|
|
@ -0,0 +1,5 @@
|
||||||
|
import Component from '@ember/component';
|
||||||
|
|
||||||
|
export default Component.extend({
|
||||||
|
tagName: '',
|
||||||
|
});
|
|
@ -0,0 +1,57 @@
|
||||||
|
<form class="filter-bar">
|
||||||
|
<FreetextFilter
|
||||||
|
@onsearch={{action onsearch}}
|
||||||
|
@value={{search}}
|
||||||
|
@placeholder="Search"
|
||||||
|
/>
|
||||||
|
<div class="filters">
|
||||||
|
<PopoverSelect
|
||||||
|
@position="left"
|
||||||
|
@onchange={{action onfilter.type}}
|
||||||
|
@multiple={{true}}
|
||||||
|
as |components|>
|
||||||
|
<BlockSlot @name="selected">
|
||||||
|
<span>
|
||||||
|
Type
|
||||||
|
</span>
|
||||||
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="options">
|
||||||
|
{{#let components.Optgroup components.Option as |Optgroup Option|}}
|
||||||
|
<Option @value="global-management" @selected={{contains 'global-management' filter.types}}>Global Management</Option>
|
||||||
|
<Option @value="global" @selected={{contains 'global' filter.types}}>Global</Option>
|
||||||
|
<Option @value="local" @selected={{contains 'local' filter.types}}>Local</Option>
|
||||||
|
{{/let}}
|
||||||
|
</BlockSlot>
|
||||||
|
</PopoverSelect>
|
||||||
|
</div>
|
||||||
|
<div class="sort">
|
||||||
|
<PopoverSelect
|
||||||
|
class="type-sort"
|
||||||
|
data-test-sort-control
|
||||||
|
@position="right"
|
||||||
|
@onchange={{action onsort}}
|
||||||
|
@multiple={{false}}
|
||||||
|
as |components|>
|
||||||
|
<BlockSlot @name="selected">
|
||||||
|
<span>
|
||||||
|
{{#let (from-entries (array
|
||||||
|
(array "CreateTime:desc" "Newest to oldest")
|
||||||
|
(array "CreateTime:asc" "Oldest to newest")
|
||||||
|
))
|
||||||
|
as |selectable|
|
||||||
|
}}
|
||||||
|
{{get selectable sort}}
|
||||||
|
{{/let}}
|
||||||
|
</span>
|
||||||
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="options">
|
||||||
|
{{#let components.Optgroup components.Option as |Optgroup Option|}}
|
||||||
|
<Optgroup @label="Creation">
|
||||||
|
<Option @value="CreateTime:desc" @selected={{eq "CreateTime:desc" sort}}>Newest to oldest</Option>
|
||||||
|
<Option @value="CreateTime:asc" @selected={{eq "CreateTime:asc" sort}}>Oldest to newest</Option>
|
||||||
|
</Optgroup>
|
||||||
|
{{/let}}
|
||||||
|
</BlockSlot>
|
||||||
|
</PopoverSelect>
|
||||||
|
</div>
|
||||||
|
</form>
|
|
@ -0,0 +1,3 @@
|
||||||
|
import Component from '@ember/component';
|
||||||
|
|
||||||
|
export default Component.extend({});
|
|
@ -0,0 +1,62 @@
|
||||||
|
<form class="filter-bar">
|
||||||
|
<FreetextFilter
|
||||||
|
@onsearch={{action onsearch}}
|
||||||
|
@value={{search}}
|
||||||
|
@placeholder="Search"
|
||||||
|
/>
|
||||||
|
<div class="filters">
|
||||||
|
<PopoverSelect
|
||||||
|
@position="left"
|
||||||
|
@onchange={{action onfilter.instance}}
|
||||||
|
@multiple={{true}}
|
||||||
|
as |components|>
|
||||||
|
<BlockSlot @name="selected">
|
||||||
|
<span>
|
||||||
|
Type
|
||||||
|
</span>
|
||||||
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="options">
|
||||||
|
{{#let components.Optgroup components.Option as |Optgroup Option|}}
|
||||||
|
<Option @value="registered" @selected={{contains 'registered' filter.instances}}>Registered</Option>
|
||||||
|
<Option @value="not-registered" @selected={{contains 'not-registered' filter.instances}}>Not Registered</Option>
|
||||||
|
{{/let}}
|
||||||
|
</BlockSlot>
|
||||||
|
</PopoverSelect>
|
||||||
|
</div>
|
||||||
|
<div class="sort">
|
||||||
|
<PopoverSelect
|
||||||
|
class="type-sort"
|
||||||
|
data-test-sort-control
|
||||||
|
@position="right"
|
||||||
|
@onchange={{action onsort}}
|
||||||
|
@multiple={{false}}
|
||||||
|
as |components|>
|
||||||
|
<BlockSlot @name="selected">
|
||||||
|
<span>
|
||||||
|
{{#let (from-entries (array
|
||||||
|
(array "Name:asc" "A to Z")
|
||||||
|
(array "Name:desc" "Z to A")
|
||||||
|
(array "Status:asc" "Unhealthy to Healthy")
|
||||||
|
(array "Status:desc" "Healthy to Unhealthy")
|
||||||
|
))
|
||||||
|
as |selectable|
|
||||||
|
}}
|
||||||
|
{{get selectable sort}}
|
||||||
|
{{/let}}
|
||||||
|
</span>
|
||||||
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="options">
|
||||||
|
{{#let components.Optgroup components.Option as |Optgroup Option|}}
|
||||||
|
<Optgroup @label="Health Status">
|
||||||
|
<Option @value="Status:asc" @selected={{eq "Status:asc" sort}}>Unhealthy to Healthy</Option>
|
||||||
|
<Option @value="Status:desc" @selected={{eq "Status:desc" sort}}>Healthy to Unhealthy</Option>
|
||||||
|
</Optgroup>
|
||||||
|
<Optgroup @label="Service Name">
|
||||||
|
<Option @value="Name:asc" @selected={{eq "Name:asc" sort}}>A to Z</Option>
|
||||||
|
<Option @value="Name:desc" @selected={{eq "Name:desc" sort}}>Z to A</Option>
|
||||||
|
</Optgroup>
|
||||||
|
{{/let}}
|
||||||
|
</BlockSlot>
|
||||||
|
</PopoverSelect>
|
||||||
|
</div>
|
||||||
|
</form>
|
|
@ -0,0 +1,5 @@
|
||||||
|
import Component from '@ember/component';
|
||||||
|
|
||||||
|
export default Component.extend({
|
||||||
|
tagName: '',
|
||||||
|
});
|
|
@ -6,7 +6,7 @@ export default Component.extend(Slotted, {
|
||||||
tagName: '',
|
tagName: '',
|
||||||
dom: service('dom'),
|
dom: service('dom'),
|
||||||
multiple: false,
|
multiple: false,
|
||||||
subtractive: true,
|
subtractive: false,
|
||||||
onchange: function() {},
|
onchange: function() {},
|
||||||
addOption: function(option) {
|
addOption: function(option) {
|
||||||
if (typeof this._options === 'undefined') {
|
if (typeof this._options === 'undefined') {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
{{#let components.MenuItem as |MenuItem|}}
|
{{#let components.MenuItem as |MenuItem|}}
|
||||||
<MenuItem
|
<MenuItem
|
||||||
class={{if selected 'is-active'}}
|
class={{if selected 'is-active'}}
|
||||||
|
...attributes
|
||||||
@onclick={{action 'click'}}
|
@onclick={{action 'click'}}
|
||||||
@selected={{selected}}
|
@selected={{selected}}
|
||||||
>
|
>
|
||||||
|
|
|
@ -2,6 +2,8 @@ import Controller from '@ember/controller';
|
||||||
export default Controller.extend({
|
export default Controller.extend({
|
||||||
queryParams: {
|
queryParams: {
|
||||||
sortBy: 'sort',
|
sortBy: 'sort',
|
||||||
|
dc: 'dc',
|
||||||
|
type: 'type',
|
||||||
search: {
|
search: {
|
||||||
as: 'filter',
|
as: 'filter',
|
||||||
replace: true,
|
replace: true,
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
import Controller from '@ember/controller';
|
import Controller from '@ember/controller';
|
||||||
|
|
||||||
export default Controller.extend({
|
export default Controller.extend({
|
||||||
queryParams: {
|
queryParams: {
|
||||||
sortBy: 'sort',
|
sortBy: 'sort',
|
||||||
|
access: 'access',
|
||||||
search: {
|
search: {
|
||||||
as: 'filter',
|
as: 'filter',
|
||||||
replace: true,
|
replace: true,
|
||||||
|
|
|
@ -3,6 +3,7 @@ import Controller from '@ember/controller';
|
||||||
export default Controller.extend({
|
export default Controller.extend({
|
||||||
queryParams: {
|
queryParams: {
|
||||||
sortBy: 'sort',
|
sortBy: 'sort',
|
||||||
|
status: 'status',
|
||||||
search: {
|
search: {
|
||||||
as: 'filter',
|
as: 'filter',
|
||||||
replace: true,
|
replace: true,
|
||||||
|
|
|
@ -4,6 +4,9 @@ import { computed } from '@ember/object';
|
||||||
export default Controller.extend({
|
export default Controller.extend({
|
||||||
queryParams: {
|
queryParams: {
|
||||||
sortBy: 'sort',
|
sortBy: 'sort',
|
||||||
|
status: 'status',
|
||||||
|
source: 'source',
|
||||||
|
type: 'type',
|
||||||
search: {
|
search: {
|
||||||
as: 'filter',
|
as: 'filter',
|
||||||
},
|
},
|
||||||
|
@ -13,4 +16,11 @@ export default Controller.extend({
|
||||||
return item.Kind !== 'connect-proxy';
|
return item.Kind !== 'connect-proxy';
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
|
externalSources: computed('services', function() {
|
||||||
|
const sources = this.services.reduce(function(prev, item) {
|
||||||
|
return prev.concat(item.ExternalSources || []);
|
||||||
|
}, []);
|
||||||
|
// unique, non-empty values, alpha sort
|
||||||
|
return [...new Set(sources)].filter(Boolean).sort();
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,11 +1,21 @@
|
||||||
import Controller from '@ember/controller';
|
import Controller from '@ember/controller';
|
||||||
|
import { computed } from '@ember/object';
|
||||||
|
|
||||||
export default Controller.extend({
|
export default Controller.extend({
|
||||||
queryParams: {
|
queryParams: {
|
||||||
sortBy: 'sort',
|
sortBy: 'sort',
|
||||||
|
status: 'status',
|
||||||
|
source: 'source',
|
||||||
search: {
|
search: {
|
||||||
as: 'filter',
|
as: 'filter',
|
||||||
replace: true,
|
replace: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
externalSources: computed('items', function() {
|
||||||
|
const sources = this.items.reduce(function(prev, item) {
|
||||||
|
return prev.concat(item.ExternalSources || []);
|
||||||
|
}, []);
|
||||||
|
// unique, non-empty values, alpha sort
|
||||||
|
return [...new Set(sources)].filter(Boolean).sort();
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
import Controller from '@ember/controller';
|
||||||
|
|
||||||
|
export default Controller.extend({
|
||||||
|
queryParams: {
|
||||||
|
sortBy: 'sort',
|
||||||
|
instance: 'instance',
|
||||||
|
search: {
|
||||||
|
as: 'filter',
|
||||||
|
replace: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
|
@ -0,0 +1,12 @@
|
||||||
|
import Controller from '@ember/controller';
|
||||||
|
|
||||||
|
export default Controller.extend({
|
||||||
|
queryParams: {
|
||||||
|
sortBy: 'sort',
|
||||||
|
instance: 'instance',
|
||||||
|
search: {
|
||||||
|
as: 'filter',
|
||||||
|
replace: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
|
@ -0,0 +1,9 @@
|
||||||
|
export default () => ({ accesses = [] }) => item => {
|
||||||
|
if (accesses.length > 0) {
|
||||||
|
if (accesses.includes(item.Action)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
|
@ -0,0 +1,8 @@
|
||||||
|
export default () => ({ statuses = [] }) => {
|
||||||
|
return item => {
|
||||||
|
if (statuses.length > 0 && !statuses.includes(item.Status)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,28 @@
|
||||||
|
import setHelpers from 'mnemonist/set';
|
||||||
|
export default () => ({ dcs = [], types = [] }) => {
|
||||||
|
const typeIncludes = ['global-management', 'standard'].reduce((prev, item) => {
|
||||||
|
prev[item] = types.includes(item);
|
||||||
|
return prev;
|
||||||
|
}, {});
|
||||||
|
const selectedDcs = new Set(dcs);
|
||||||
|
return item => {
|
||||||
|
let type = true;
|
||||||
|
let dc = true;
|
||||||
|
if (types.length > 0) {
|
||||||
|
type = false;
|
||||||
|
if (typeIncludes['global-management'] && item.isGlobalManagement) {
|
||||||
|
type = true;
|
||||||
|
}
|
||||||
|
if (typeIncludes['standard'] && !item.isGlobalManagement) {
|
||||||
|
type = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (dcs.length > 0) {
|
||||||
|
// if datacenters is undefined it means the policy is applicable to all datacenters
|
||||||
|
dc =
|
||||||
|
typeof item.Datacenters === 'undefined' ||
|
||||||
|
setHelpers.intersectionSize(selectedDcs, new Set(item.Datacenters)) > 0;
|
||||||
|
}
|
||||||
|
return type && dc;
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,19 @@
|
||||||
|
import setHelpers from 'mnemonist/set';
|
||||||
|
export default () => ({ sources = [], statuses = [] }) => {
|
||||||
|
const uniqueSources = new Set(sources);
|
||||||
|
return item => {
|
||||||
|
if (statuses.length > 0) {
|
||||||
|
if (statuses.includes(item.Status)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (sources.length > 0) {
|
||||||
|
if (setHelpers.intersectionSize(uniqueSources, new Set(item.ExternalSources || [])) !== 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,67 @@
|
||||||
|
import setHelpers from 'mnemonist/set';
|
||||||
|
export default () => ({ instances = [], sources = [], statuses = [], types = [] }) => {
|
||||||
|
const uniqueSources = new Set(sources);
|
||||||
|
const typeIncludes = [
|
||||||
|
'ingress-gateway',
|
||||||
|
'terminating-gateway',
|
||||||
|
'mesh-gateway',
|
||||||
|
'service',
|
||||||
|
'mesh-enabled',
|
||||||
|
'mesh-disabled',
|
||||||
|
].reduce((prev, item) => {
|
||||||
|
prev[item] = types.includes(item);
|
||||||
|
return prev;
|
||||||
|
}, {});
|
||||||
|
const instanceIncludes = ['registered', 'not-registered'].reduce((prev, item) => {
|
||||||
|
prev[item] = instances.includes(item);
|
||||||
|
return prev;
|
||||||
|
}, {});
|
||||||
|
return item => {
|
||||||
|
if (statuses.length > 0) {
|
||||||
|
if (statuses.includes(item.MeshStatus)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (instances.length > 0) {
|
||||||
|
if (item.InstanceCount > 0) {
|
||||||
|
if (instanceIncludes['registered']) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (instanceIncludes['not-registered']) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (types.length > 0) {
|
||||||
|
if (typeIncludes['ingress-gateway'] && item.Kind === 'ingress-gateway') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (typeIncludes['terminating-gateway'] && item.Kind === 'terminating-gateway') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (typeIncludes['mesh-gateway'] && item.Kind === 'mesh-gateway') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (typeIncludes['service'] && typeof item.Kind === 'undefined') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (typeIncludes['mesh-enabled'] && typeof item.Proxy !== 'undefined') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (typeIncludes['mesh-disabled'] && typeof item.Proxy === 'undefined') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (sources.length > 0) {
|
||||||
|
if (setHelpers.intersectionSize(uniqueSources, new Set(item.ExternalSources || [])) !== 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,21 @@
|
||||||
|
export default () => ({ types = [] }) => {
|
||||||
|
const typeIncludes = ['global-management', 'global', 'local'].reduce((prev, item) => {
|
||||||
|
prev[item] = types.includes(item);
|
||||||
|
return prev;
|
||||||
|
}, {});
|
||||||
|
return item => {
|
||||||
|
if (types.length > 0) {
|
||||||
|
if (typeIncludes['global-management'] && item.isGlobalManagement) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (typeIncludes['global'] && !item.Local) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (typeIncludes['local'] && item.Local) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,9 @@
|
||||||
|
import Helper from '@ember/component/helper';
|
||||||
|
import { inject as service } from '@ember/service';
|
||||||
|
|
||||||
|
export default Helper.extend({
|
||||||
|
filter: service('filter'),
|
||||||
|
compute([type, filters], hash) {
|
||||||
|
return this.filter.predicate(type)(filters);
|
||||||
|
},
|
||||||
|
});
|
|
@ -1,7 +1,6 @@
|
||||||
import { helper } from '@ember/component/helper';
|
import { helper } from '@ember/component/helper';
|
||||||
import { get } from '@ember/object';
|
import { get } from '@ember/object';
|
||||||
|
import { MANAGEMENT_ID } from 'consul-ui/models/policy';
|
||||||
const MANAGEMENT_ID = '00000000-0000-0000-0000-000000000001';
|
|
||||||
|
|
||||||
export default helper(function policyGroup([items] /*, hash*/) {
|
export default helper(function policyGroup([items] /*, hash*/) {
|
||||||
return items.reduce(
|
return items.reduce(
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import service from 'consul-ui/sort/comparators/service';
|
import service from 'consul-ui/sort/comparators/service';
|
||||||
|
import serviceInstance from 'consul-ui/sort/comparators/service-instance';
|
||||||
import kv from 'consul-ui/sort/comparators/kv';
|
import kv from 'consul-ui/sort/comparators/kv';
|
||||||
import check from 'consul-ui/sort/comparators/check';
|
import check from 'consul-ui/sort/comparators/check';
|
||||||
import intention from 'consul-ui/sort/comparators/intention';
|
import intention from 'consul-ui/sort/comparators/intention';
|
||||||
|
@ -13,6 +14,7 @@ export function initialize(container) {
|
||||||
const Sort = container.resolveRegistration('service:sort');
|
const Sort = container.resolveRegistration('service:sort');
|
||||||
const comparators = {
|
const comparators = {
|
||||||
service: service(),
|
service: service(),
|
||||||
|
serviceInstance: serviceInstance(),
|
||||||
kv: kv(),
|
kv: kv(),
|
||||||
check: check(),
|
check: check(),
|
||||||
intention: intention(),
|
intention: intention(),
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
import Model from 'ember-data/model';
|
import Model from 'ember-data/model';
|
||||||
import attr from 'ember-data/attr';
|
import attr from 'ember-data/attr';
|
||||||
|
import { computed } from '@ember/object';
|
||||||
|
|
||||||
export const PRIMARY_KEY = 'uid';
|
export const PRIMARY_KEY = 'uid';
|
||||||
export const SLUG_KEY = 'ID';
|
export const SLUG_KEY = 'ID';
|
||||||
|
|
||||||
|
export const MANAGEMENT_ID = '00000000-0000-0000-0000-000000000001';
|
||||||
|
|
||||||
export default Model.extend({
|
export default Model.extend({
|
||||||
[PRIMARY_KEY]: attr('string'),
|
[PRIMARY_KEY]: attr('string'),
|
||||||
[SLUG_KEY]: attr('string'),
|
[SLUG_KEY]: attr('string'),
|
||||||
|
@ -19,6 +22,9 @@ export default Model.extend({
|
||||||
// frontend only for ordering where CreateIndex can't be used
|
// frontend only for ordering where CreateIndex can't be used
|
||||||
CreateTime: attr('date', { defaultValue: 0 }),
|
CreateTime: attr('date', { defaultValue: 0 }),
|
||||||
//
|
//
|
||||||
|
isGlobalManagement: computed('ID', function() {
|
||||||
|
return this.ID === MANAGEMENT_ID;
|
||||||
|
}),
|
||||||
Datacenter: attr('string'),
|
Datacenter: attr('string'),
|
||||||
Namespace: attr('string'),
|
Namespace: attr('string'),
|
||||||
SyncTime: attr('number'),
|
SyncTime: attr('number'),
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import Model from 'ember-data/model';
|
import Model from 'ember-data/model';
|
||||||
import attr from 'ember-data/attr';
|
import attr from 'ember-data/attr';
|
||||||
import { belongsTo } from 'ember-data/relationships';
|
import { belongsTo } from 'ember-data/relationships';
|
||||||
import { filter, alias } from '@ember/object/computed';
|
import { computed } from '@ember/object';
|
||||||
|
import { or, filter, alias } from '@ember/object/computed';
|
||||||
|
|
||||||
export const PRIMARY_KEY = 'uid';
|
export const PRIMARY_KEY = 'uid';
|
||||||
export const SLUG_KEY = 'Node.Node,Service.ID';
|
export const SLUG_KEY = 'Node.Node,Service.ID';
|
||||||
|
@ -19,13 +20,52 @@ export default Model.extend({
|
||||||
Checks: attr(),
|
Checks: attr(),
|
||||||
SyncTime: attr('number'),
|
SyncTime: attr('number'),
|
||||||
meta: attr(),
|
meta: attr(),
|
||||||
|
Name: or('Service.ID', 'Service.Service'),
|
||||||
Tags: alias('Service.Tags'),
|
Tags: alias('Service.Tags'),
|
||||||
Meta: alias('Service.Meta'),
|
Meta: alias('Service.Meta'),
|
||||||
Namespace: alias('Service.Namespace'),
|
Namespace: alias('Service.Namespace'),
|
||||||
ServiceChecks: filter('Checks', function(item, i, arr) {
|
ExternalSources: computed('Service.Meta', function() {
|
||||||
|
const sources = Object.entries(this.Service.Meta || {})
|
||||||
|
.filter(([key, value]) => key === 'external-source')
|
||||||
|
.map(([key, value]) => {
|
||||||
|
return value;
|
||||||
|
});
|
||||||
|
return [...new Set(sources)];
|
||||||
|
}),
|
||||||
|
ServiceChecks: filter('Checks.[]', function(item, i, arr) {
|
||||||
return item.ServiceID !== '';
|
return item.ServiceID !== '';
|
||||||
}),
|
}),
|
||||||
NodeChecks: filter('Checks', function(item, i, arr) {
|
NodeChecks: filter('Checks.[]', function(item, i, arr) {
|
||||||
return item.ServiceID === '';
|
return item.ServiceID === '';
|
||||||
}),
|
}),
|
||||||
|
Status: computed('ChecksPassing', 'ChecksWarning', 'ChecksCritical', function() {
|
||||||
|
switch (true) {
|
||||||
|
case this.ChecksCritical.length !== 0:
|
||||||
|
return 'critical';
|
||||||
|
case this.ChecksWarning.length !== 0:
|
||||||
|
return 'warning';
|
||||||
|
case this.ChecksPassing.length !== 0:
|
||||||
|
return 'passing';
|
||||||
|
default:
|
||||||
|
return 'empty';
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
ChecksPassing: computed('Checks.[]', function() {
|
||||||
|
return this.Checks.filter(item => item.Status === 'passing');
|
||||||
|
}),
|
||||||
|
ChecksWarning: computed('Checks.[]', function() {
|
||||||
|
return this.Checks.filter(item => item.Status === 'warning');
|
||||||
|
}),
|
||||||
|
ChecksCritical: computed('Checks.[]', function() {
|
||||||
|
return this.Checks.filter(item => item.Status === 'critical');
|
||||||
|
}),
|
||||||
|
PercentageChecksPassing: computed('Checks.[]', 'ChecksPassing', function() {
|
||||||
|
return (this.ChecksPassing.length / this.Checks.length) * 100;
|
||||||
|
}),
|
||||||
|
PercentageChecksWarning: computed('Checks.[]', 'ChecksWarning', function() {
|
||||||
|
return (this.ChecksWarning.length / this.Checks.length) * 100;
|
||||||
|
}),
|
||||||
|
PercentageChecksCritical: computed('Checks.[]', 'ChecksCritical', function() {
|
||||||
|
return (this.ChecksCritical.length / this.Checks.length) * 100;
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
import Model from 'ember-data/model';
|
import Model from 'ember-data/model';
|
||||||
import attr from 'ember-data/attr';
|
import attr from 'ember-data/attr';
|
||||||
|
import { computed } from '@ember/object';
|
||||||
|
import { MANAGEMENT_ID } from 'consul-ui/models/policy';
|
||||||
|
|
||||||
export const PRIMARY_KEY = 'uid';
|
export const PRIMARY_KEY = 'uid';
|
||||||
export const SLUG_KEY = 'AccessorID';
|
export const SLUG_KEY = 'AccessorID';
|
||||||
|
@ -24,6 +26,9 @@ export default Model.extend({
|
||||||
Datacenter: attr('string'),
|
Datacenter: attr('string'),
|
||||||
Namespace: attr('string'),
|
Namespace: attr('string'),
|
||||||
Local: attr('boolean'),
|
Local: attr('boolean'),
|
||||||
|
isGlobalManagement: computed('Policies.[]', function() {
|
||||||
|
return (this.Policies || []).find(item => item.ID === MANAGEMENT_ID);
|
||||||
|
}),
|
||||||
Policies: attr({
|
Policies: attr({
|
||||||
defaultValue: function() {
|
defaultValue: function() {
|
||||||
return [];
|
return [];
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
import Service from '@ember/service';
|
||||||
|
|
||||||
|
import service from 'consul-ui/filter/predicates/service';
|
||||||
|
import serviceInstance from 'consul-ui/filter/predicates/service-instance';
|
||||||
|
import node from 'consul-ui/filter/predicates/node';
|
||||||
|
import intention from 'consul-ui/filter/predicates/intention';
|
||||||
|
import token from 'consul-ui/filter/predicates/token';
|
||||||
|
import policy from 'consul-ui/filter/predicates/policy';
|
||||||
|
|
||||||
|
const predicates = {
|
||||||
|
service: service(),
|
||||||
|
serviceInstance: serviceInstance(),
|
||||||
|
node: node(),
|
||||||
|
intention: intention(),
|
||||||
|
token: token(),
|
||||||
|
policy: policy(),
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Service.extend({
|
||||||
|
predicate: function(type) {
|
||||||
|
return predicates[type];
|
||||||
|
},
|
||||||
|
});
|
|
@ -16,7 +16,7 @@ export default RepositoryService.extend({
|
||||||
const query = {
|
const query = {
|
||||||
dc: dc,
|
dc: dc,
|
||||||
nspace: nspace,
|
nspace: nspace,
|
||||||
filter: `SourceName == "${slug}" or DestinationName == "${slug}"`,
|
filter: `SourceName == "${slug}" or DestinationName == "${slug}" or SourceName == "*" or DestinationName == "*"`,
|
||||||
};
|
};
|
||||||
if (typeof configuration.cursor !== 'undefined') {
|
if (typeof configuration.cursor !== 'undefined') {
|
||||||
query.index = configuration.cursor;
|
query.index = configuration.cursor;
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
export default () => key => {
|
||||||
|
if (key.startsWith('Status:')) {
|
||||||
|
const [, dir] = key.split(':');
|
||||||
|
const props = [
|
||||||
|
'PercentageChecksPassing',
|
||||||
|
'PercentageChecksWarning',
|
||||||
|
'PercentageChecksCritical',
|
||||||
|
];
|
||||||
|
if (dir === 'asc') {
|
||||||
|
props.reverse();
|
||||||
|
}
|
||||||
|
return function(a, b) {
|
||||||
|
for (let i in props) {
|
||||||
|
let prop = props[i];
|
||||||
|
if (a[prop] === b[prop]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
return a[prop] > b[prop] ? -1 : 1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return key;
|
||||||
|
};
|
|
@ -1,5 +1,2 @@
|
||||||
@import './skin';
|
@import './skin';
|
||||||
@import './layout';
|
@import './layout';
|
||||||
%sort-button {
|
|
||||||
@extend %split-button;
|
|
||||||
}
|
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
width: 16px;
|
width: 16px;
|
||||||
height: 16px;
|
height: 16px;
|
||||||
position: relative;
|
position: relative;
|
||||||
top: 2px;
|
|
||||||
}
|
}
|
||||||
%popover-menu-toggle:checked + label > *::after {
|
%popover-menu-toggle:checked + label > *::after {
|
||||||
@extend %with-chevron-up-mask;
|
@extend %with-chevron-up-mask;
|
||||||
|
|
|
@ -948,6 +948,16 @@
|
||||||
mask-image: $logo-bitbucket-monochrome-svg;
|
mask-image: $logo-bitbucket-monochrome-svg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
%with-logo-consul-color-icon {
|
||||||
|
@extend %with-icon;
|
||||||
|
background-image: $consul-logo-color-svg;
|
||||||
|
}
|
||||||
|
%with-logo-consul-color-mask {
|
||||||
|
@extend %with-mask;
|
||||||
|
-webkit-mask-image: $consul-logo-color-svg;
|
||||||
|
mask-image: $consul-logo-color-svg;
|
||||||
|
}
|
||||||
|
|
||||||
%with-logo-gcp-color-icon {
|
%with-logo-gcp-color-icon {
|
||||||
@extend %with-icon;
|
@extend %with-icon;
|
||||||
background-image: $logo-gcp-color-svg;
|
background-image: $logo-gcp-color-svg;
|
||||||
|
@ -1047,6 +1057,15 @@
|
||||||
-webkit-mask-image: $logo-microsoft-color-svg;
|
-webkit-mask-image: $logo-microsoft-color-svg;
|
||||||
mask-image: $logo-microsoft-color-svg;
|
mask-image: $logo-microsoft-color-svg;
|
||||||
}
|
}
|
||||||
|
%with-logo-nomad-color-icon {
|
||||||
|
@extend %with-icon;
|
||||||
|
background-image: $nomad-logo-color-svg;
|
||||||
|
}
|
||||||
|
%with-logo-nomad-color-mask {
|
||||||
|
@extend %with-mask;
|
||||||
|
-webkit-mask-image: $nomad-logo-color-svg;
|
||||||
|
mask-image: $nomad-logo-color-svg;
|
||||||
|
}
|
||||||
|
|
||||||
%with-logo-okta-color-icon {
|
%with-logo-okta-color-icon {
|
||||||
@extend %with-icon;
|
@extend %with-icon;
|
||||||
|
@ -1098,6 +1117,15 @@
|
||||||
mask-image: $logo-slack-monochrome-svg;
|
mask-image: $logo-slack-monochrome-svg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
%with-logo-terraform-color-icon {
|
||||||
|
@extend %with-icon;
|
||||||
|
background-image: $terraform-logo-color-svg;
|
||||||
|
}
|
||||||
|
%with-logo-terraform-color-mask {
|
||||||
|
@extend %with-mask;
|
||||||
|
-webkit-mask-image: $terraform-logo-color-svg;
|
||||||
|
mask-image: $terraform-logo-color-svg;
|
||||||
|
}
|
||||||
%with-logo-vmware-color-icon {
|
%with-logo-vmware-color-icon {
|
||||||
@extend %with-icon;
|
@extend %with-icon;
|
||||||
background-image: $logo-vmware-color-svg;
|
background-image: $logo-vmware-color-svg;
|
||||||
|
|
|
@ -72,10 +72,10 @@ main {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
#toolbar-toggle:checked + * {
|
#toolbar-toggle:checked + * {
|
||||||
display: block;
|
display: flex;
|
||||||
}
|
}
|
||||||
html.template-service.template-show #toolbar-toggle + * {
|
html.template-service.template-show #toolbar-toggle + * {
|
||||||
display: block;
|
display: flex;
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
}
|
}
|
||||||
html.template-service.template-show .actions {
|
html.template-service.template-show .actions {
|
||||||
|
|
|
@ -3,14 +3,18 @@
|
||||||
.filter-bar {
|
.filter-bar {
|
||||||
@extend %filter-bar;
|
@extend %filter-bar;
|
||||||
}
|
}
|
||||||
%filter-bar {
|
%filter-bar .popover-select {
|
||||||
|
height: 35px;
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 3;
|
z-index: 3;
|
||||||
}
|
}
|
||||||
%filter-bar:not(.with-sort) {
|
%filter-bar [role='menuitem'] {
|
||||||
|
justify-content: normal !important;
|
||||||
|
}
|
||||||
|
html.template-acl.template-list .filter-bar {
|
||||||
@extend %filter-bar-reversed;
|
@extend %filter-bar-reversed;
|
||||||
}
|
}
|
||||||
%filter-bar [role='radiogroup'] {
|
html.template-acl.template-list .filter-bar [role='radiogroup'] {
|
||||||
@extend %expanded-single-select;
|
@extend %expanded-single-select;
|
||||||
}
|
}
|
||||||
%filter-bar span::before {
|
%filter-bar span::before {
|
||||||
|
@ -19,6 +23,11 @@
|
||||||
margin-left: -2px;
|
margin-left: -2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
%filter-bar .popover-menu > [type='checkbox']:checked + label button {
|
||||||
|
color: $blue-500;
|
||||||
|
background-color: $gray-100;
|
||||||
|
}
|
||||||
|
|
||||||
%filter-bar .value-passing span::before {
|
%filter-bar .value-passing span::before {
|
||||||
@extend %with-check-circle-fill-icon, %as-pseudo;
|
@extend %with-check-circle-fill-icon, %as-pseudo;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,34 +5,50 @@
|
||||||
margin-top: 0 !important;
|
margin-top: 0 !important;
|
||||||
margin-bottom: -12px;
|
margin-bottom: -12px;
|
||||||
}
|
}
|
||||||
|
%filter-bar .filters {
|
||||||
|
display: flex;
|
||||||
|
margin-right: 12px;
|
||||||
|
}
|
||||||
|
%filter-bar .filters > *:not(:last-child) {
|
||||||
|
margin-right: 6px;
|
||||||
|
}
|
||||||
%filter-bar + :not(.notice) {
|
%filter-bar + :not(.notice) {
|
||||||
margin-top: 1.8em;
|
margin-top: 1.8em;
|
||||||
}
|
}
|
||||||
|
%filter-bar fieldset {
|
||||||
|
flex: 0 1 auto;
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
%filter-bar-reversed {
|
%filter-bar-reversed {
|
||||||
flex-direction: row-reverse;
|
flex-direction: row-reverse;
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
margin-bottom: 8px !important;
|
margin-bottom: 8px !important;
|
||||||
}
|
}
|
||||||
%filter-bar fieldset {
|
%filter-bar-reversed fieldset {
|
||||||
flex: 0 1 auto;
|
min-width: 210px;
|
||||||
width: auto;
|
width: auto;
|
||||||
}
|
}
|
||||||
%filter-bar fieldset:first-child:not(:last-child) {
|
|
||||||
flex: 1 1 auto;
|
|
||||||
margin-right: 12px;
|
|
||||||
}
|
|
||||||
%filter-bar-reversed fieldset:first-child:not(:last-child) {
|
%filter-bar-reversed fieldset:first-child:not(:last-child) {
|
||||||
flex: 0 1 auto;
|
flex: 0 1 auto;
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
}
|
}
|
||||||
%filter-bar-reversed fieldset {
|
|
||||||
min-width: 210px;
|
|
||||||
width: auto;
|
|
||||||
}
|
|
||||||
%filter-bar-reversed > *:first-child {
|
%filter-bar-reversed > *:first-child {
|
||||||
margin-left: 12px;
|
margin-left: 12px;
|
||||||
}
|
}
|
||||||
|
@media #{$--horizontal-filters} {
|
||||||
|
%filter-bar fieldset:first-child:not(:last-child) {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
margin-right: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
@media #{$--lt-horizontal-filters} {
|
@media #{$--lt-horizontal-filters} {
|
||||||
|
%filter-bar {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
%filter-bar fieldset {
|
||||||
|
flex: 0 1 100%;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
%filter-bar-reversed > *:first-child {
|
%filter-bar-reversed > *:first-child {
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,9 +13,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@media #{$--lt-horizontal-selects} {
|
@media #{$--lt-horizontal-selects} {
|
||||||
%filter-bar label:not(:last-child) {
|
|
||||||
border-bottom: $decor-border-100;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
%filter-bar [role='radiogroup'] label {
|
%filter-bar [role='radiogroup'] label {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
|
@ -14,6 +14,9 @@
|
||||||
right: auto;
|
right: auto;
|
||||||
top: 28px !important;
|
top: 28px !important;
|
||||||
}
|
}
|
||||||
|
%main-nav-horizontal .popover-menu > label > button::after {
|
||||||
|
top: 2px;
|
||||||
|
}
|
||||||
@media #{$--horizontal-nav} {
|
@media #{$--horizontal-nav} {
|
||||||
%main-nav-horizontal > ul,
|
%main-nav-horizontal > ul,
|
||||||
%main-nav-horizontal-panel {
|
%main-nav-horizontal-panel {
|
||||||
|
|
|
@ -1,6 +1,64 @@
|
||||||
.popover-select {
|
.popover-select {
|
||||||
@extend %popover-select;
|
@extend %popover-select;
|
||||||
}
|
}
|
||||||
|
%popover-select label {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
%popover-select label > * {
|
%popover-select label > * {
|
||||||
|
@extend %button;
|
||||||
|
padding: 0 8px !important;
|
||||||
|
height: 100% !important;
|
||||||
|
justify-content: space-between !important;
|
||||||
|
min-width: auto !important;
|
||||||
|
}
|
||||||
|
%popover-select label > *::after {
|
||||||
|
margin-left: 6px;
|
||||||
|
}
|
||||||
|
%popover-select.type-sort label > * {
|
||||||
@extend %sort-button;
|
@extend %sort-button;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
%popover-select.type-access button::before,
|
||||||
|
%popover-select.type-source button::before,
|
||||||
|
%popover-select.type-status button::before {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
%popover-select .value-allow button::before,
|
||||||
|
%popover-select .value-passing button::before {
|
||||||
|
@extend %with-check-circle-fill-mask, %as-pseudo;
|
||||||
|
color: $green-500;
|
||||||
|
}
|
||||||
|
%popover-select .value-warning button::before {
|
||||||
|
@extend %with-alert-triangle-mask, %as-pseudo;
|
||||||
|
color: $orange-500;
|
||||||
|
}
|
||||||
|
%popover-select .value-deny button::before,
|
||||||
|
%popover-select .value-critical button::before {
|
||||||
|
@extend %with-cancel-square-fill-mask, %as-pseudo;
|
||||||
|
color: $red-500;
|
||||||
|
}
|
||||||
|
%popover-select .value-empty button::before {
|
||||||
|
@extend %with-minus-square-fill-mask, %as-pseudo;
|
||||||
|
color: $gray-400;
|
||||||
|
}
|
||||||
|
%popover-select.type-source li button {
|
||||||
|
text-transform: capitalize;
|
||||||
|
}
|
||||||
|
%popover-select.type-source li.aws button {
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
%popover-select .aws button::before {
|
||||||
|
@extend %with-logo-aws-color-icon, %as-pseudo;
|
||||||
|
}
|
||||||
|
%popover-select .kubernetes button::before {
|
||||||
|
@extend %with-logo-kubernetes-color-icon, %as-pseudo;
|
||||||
|
}
|
||||||
|
%popover-select .consul button::before {
|
||||||
|
@extend %with-logo-consul-color-icon, %as-pseudo;
|
||||||
|
}
|
||||||
|
%popover-select .nomad button::before {
|
||||||
|
@extend %with-logo-nomad-color-icon, %as-pseudo;
|
||||||
|
}
|
||||||
|
%popover-select .terraform button::before {
|
||||||
|
@extend %with-logo-terraform-color-icon, %as-pseudo;
|
||||||
|
}
|
||||||
|
|
|
@ -3,115 +3,100 @@
|
||||||
{{else}}
|
{{else}}
|
||||||
{{title 'Access Controls'}}
|
{{title 'Access Controls'}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
{{#let (hash
|
||||||
|
types=(if type (split type ',') undefined)
|
||||||
|
dcs=(if dc (split dc ',') undefined)
|
||||||
|
) as |filters|}}
|
||||||
|
{{#let (or sortBy "Name:asc") as |sort|}}
|
||||||
|
<AppView
|
||||||
|
@class="policy list"
|
||||||
|
@loading={{isLoading}}
|
||||||
|
@authorized={{isAuthorized}}
|
||||||
|
@enabled={{isEnabled}}
|
||||||
|
>
|
||||||
|
<BlockSlot @name="notification" as |status type|>
|
||||||
|
{{partial 'dc/acls/policies/notifications'}}
|
||||||
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="header">
|
||||||
|
<h1>
|
||||||
|
Access Controls
|
||||||
|
</h1>
|
||||||
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="nav">
|
||||||
|
{{#if isAuthorized }}
|
||||||
|
{{partial 'dc/acls/nav'}}
|
||||||
|
{{/if}}
|
||||||
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="disabled">
|
||||||
|
{{partial 'dc/acls/disabled'}}
|
||||||
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="authorization">
|
||||||
|
{{partial 'dc/acls/authorization'}}
|
||||||
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="actions">
|
||||||
|
<a data-test-create href="{{href-to 'dc.acls.policies.create'}}" class="type-create">Create</a>
|
||||||
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="toolbar">
|
||||||
|
{{#if (gt items.length 0) }}
|
||||||
|
<ConsulPolicySearchBar
|
||||||
|
@search={{search}}
|
||||||
|
@onsearch={{action (mut search) value="target.value"}}
|
||||||
|
|
||||||
{{#let (or sortBy "Name:asc") as |sort|}}
|
@sort={{sort}}
|
||||||
<AppView
|
@onsort={{action (mut sortBy) value="target.selected"}}
|
||||||
@class="policy list"
|
|
||||||
@loading={{isLoading}}
|
@filter={{filters}}
|
||||||
@authorized={{isAuthorized}}
|
@onfilter={{hash
|
||||||
@enabled={{isEnabled}}
|
dc=(action (mut dc) value="target.selectedItems")
|
||||||
>
|
type=(action (mut type) value="target.selectedItems")
|
||||||
<BlockSlot @name="notification" as |status type|>
|
}}
|
||||||
{{partial 'dc/acls/policies/notifications'}}
|
|
||||||
</BlockSlot>
|
|
||||||
<BlockSlot @name="header">
|
|
||||||
<h1>
|
|
||||||
Access Controls
|
|
||||||
</h1>
|
|
||||||
</BlockSlot>
|
|
||||||
<BlockSlot @name="nav">
|
|
||||||
{{#if isAuthorized }}
|
|
||||||
{{partial 'dc/acls/nav'}}
|
|
||||||
{{/if}}
|
|
||||||
</BlockSlot>
|
|
||||||
<BlockSlot @name="disabled">
|
|
||||||
{{partial 'dc/acls/disabled'}}
|
|
||||||
</BlockSlot>
|
|
||||||
<BlockSlot @name="authorization">
|
|
||||||
{{partial 'dc/acls/authorization'}}
|
|
||||||
</BlockSlot>
|
|
||||||
<BlockSlot @name="actions">
|
|
||||||
<a data-test-create href="{{href-to 'dc.acls.policies.create'}}" class="type-create">Create</a>
|
|
||||||
</BlockSlot>
|
|
||||||
<BlockSlot @name="toolbar">
|
|
||||||
{{#if (gt items.length 0) }}
|
|
||||||
<SearchBar
|
|
||||||
@value={{search}}
|
|
||||||
@onsearch={{action (mut search) value="target.value"}}
|
|
||||||
class="with-sort"
|
|
||||||
>
|
|
||||||
<BlockSlot @name="secondary">
|
|
||||||
<PopoverSelect
|
|
||||||
@position="right"
|
|
||||||
@onchange={{action (mut sortBy) value='target.selected'}}
|
|
||||||
@multiple={{false}}
|
|
||||||
as |components|>
|
|
||||||
<BlockSlot @name="selected">
|
|
||||||
<span>
|
|
||||||
{{#let (from-entries (array
|
|
||||||
(array "Name:asc" "A to Z")
|
|
||||||
(array "Name:desc" "Z to A")
|
|
||||||
))
|
|
||||||
as |selectable|
|
|
||||||
}}
|
|
||||||
{{get selectable sort}}
|
|
||||||
{{/let}}
|
|
||||||
</span>
|
|
||||||
</BlockSlot>
|
|
||||||
<BlockSlot @name="options">
|
|
||||||
{{#let components.Optgroup components.Option as |Optgroup Option|}}
|
|
||||||
<Optgroup @label="Name">
|
|
||||||
<Option @value="Name:asc" @selected={{eq "Name:asc" sort}}>A to Z</Option>
|
|
||||||
<Option @value="Name:desc" @selected={{eq "Name:desc" sort}}>Z to A</Option>
|
|
||||||
</Optgroup>
|
|
||||||
{{/let}}
|
|
||||||
</BlockSlot>
|
|
||||||
</PopoverSelect>
|
|
||||||
</BlockSlot>
|
|
||||||
</SearchBar>
|
|
||||||
{{/if}}
|
|
||||||
</BlockSlot>
|
|
||||||
<BlockSlot @name="content">
|
|
||||||
{{#let (sort-by (comparator 'policy' sort) items) as |sorted|}}
|
|
||||||
<ChangeableSet @dispatcher={{searchable 'policy' sorted}} @terms={{search}}>
|
|
||||||
<BlockSlot @name="set" as |filtered|>
|
|
||||||
<ConsulPolicyList
|
|
||||||
@items={{filtered}}
|
|
||||||
@ondelete={{action send 'delete'}}
|
|
||||||
/>
|
/>
|
||||||
|
{{/if}}
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
<BlockSlot @name="empty">
|
<BlockSlot @name="content">
|
||||||
<EmptyState @allowLogin={{true}}>
|
{{#let (filter (filter-predicate 'policy' filters) items) as |filtered|}}
|
||||||
<BlockSlot @name="header">
|
{{#let (sort-by (comparator 'policy' sort) filtered) as |sorted|}}
|
||||||
<h2>
|
<ChangeableSet @dispatcher={{searchable 'policy' sorted}} @terms={{search}}>
|
||||||
{{#if (gt items.length 0)}}
|
<BlockSlot @name="set" as |searched|>
|
||||||
No policies found
|
<ConsulPolicyList
|
||||||
{{else}}
|
@items={{searched}}
|
||||||
Welcome to Policies
|
@ondelete={{action send 'delete'}}
|
||||||
{{/if}}
|
/>
|
||||||
</h2>
|
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
<BlockSlot @name="body">
|
<BlockSlot @name="empty">
|
||||||
<p>
|
<EmptyState @allowLogin={{true}}>
|
||||||
{{#if (gt items.length 0)}}
|
<BlockSlot @name="header">
|
||||||
No policies where found matching that search, or you may not have access to view the policies you are searching for.
|
<h2>
|
||||||
{{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 found
|
||||||
{{/if}}
|
{{else}}
|
||||||
</p>
|
Welcome to Policies
|
||||||
|
{{/if}}
|
||||||
|
</h2>
|
||||||
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="body">
|
||||||
|
<p>
|
||||||
|
{{#if (gt items.length 0)}}
|
||||||
|
No policies where found matching that search, or you may not have access to view the policies you are searching for.
|
||||||
|
{{else}}
|
||||||
|
There don't seem to be any policies, or you may not have access to view policies yet.
|
||||||
|
{{/if}}
|
||||||
|
</p>
|
||||||
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="actions">
|
||||||
|
<li class="docs-link">
|
||||||
|
<a href="{{env 'CONSUL_DOCS_URL'}}/commands/acl/policy" rel="noopener noreferrer" target="_blank">Documentation on policies</a>
|
||||||
|
</li>
|
||||||
|
<li class="learn-link">
|
||||||
|
<a href="{{env 'CONSUL_LEARN_URL'}}/consul/security-networking/managing-acl-policies" rel="noopener noreferrer" target="_blank">Read the guide</a>
|
||||||
|
</li>
|
||||||
|
</BlockSlot>
|
||||||
|
</EmptyState>
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
<BlockSlot @name="actions">
|
</ChangeableSet>
|
||||||
<li class="docs-link">
|
{{/let}}
|
||||||
<a href="{{env 'CONSUL_DOCS_URL'}}/commands/acl/policy" rel="noopener noreferrer" target="_blank">Documentation on policies</a>
|
{{/let}}
|
||||||
</li>
|
</BlockSlot>
|
||||||
<li class="learn-link">
|
</AppView>
|
||||||
<a href="{{env 'CONSUL_LEARN_URL'}}/consul/security-networking/managing-acl-policies" rel="noopener noreferrer" target="_blank">Read the guide</a>
|
|
||||||
</li>
|
|
||||||
</BlockSlot>
|
|
||||||
</EmptyState>
|
|
||||||
</BlockSlot>
|
|
||||||
</ChangeableSet>
|
|
||||||
{{/let}}
|
{{/let}}
|
||||||
</BlockSlot>
|
|
||||||
</AppView>
|
|
||||||
{{/let}}
|
{{/let}}
|
|
@ -35,46 +35,13 @@
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
<BlockSlot @name="toolbar">
|
<BlockSlot @name="toolbar">
|
||||||
{{#if (gt items.length 0) }}
|
{{#if (gt items.length 0) }}
|
||||||
<SearchBar
|
<ConsulRoleSearchBar
|
||||||
@value={{search}}
|
@search={{search}}
|
||||||
@onsearch={{action (mut search) value="target.value"}}
|
@onsearch={{action (mut search) value="target.value"}}
|
||||||
class="with-sort"
|
|
||||||
>
|
@sort={{sort}}
|
||||||
<BlockSlot @name="secondary">
|
@onsort={{action (mut sortBy) value="target.selected"}}
|
||||||
<PopoverSelect
|
/>
|
||||||
@position="right"
|
|
||||||
@onchange={{action (mut sortBy) value='target.selected'}}
|
|
||||||
@multiple={{false}}
|
|
||||||
as |components|>
|
|
||||||
<BlockSlot @name="selected">
|
|
||||||
<span>
|
|
||||||
{{#let (from-entries (array
|
|
||||||
(array "Name:asc" "A to Z")
|
|
||||||
(array "Name:desc" "Z to A")
|
|
||||||
(array "CreateIndex:desc" "Newest to oldest")
|
|
||||||
(array "CreateIndex:asc" "Oldest to newest")
|
|
||||||
))
|
|
||||||
as |selectable|
|
|
||||||
}}
|
|
||||||
{{get selectable sort}}
|
|
||||||
{{/let}}
|
|
||||||
</span>
|
|
||||||
</BlockSlot>
|
|
||||||
<BlockSlot @name="options">
|
|
||||||
{{#let components.Optgroup components.Option as |Optgroup Option|}}
|
|
||||||
<Optgroup @label="Name">
|
|
||||||
<Option @value="Name:asc" @selected={{eq "Name:asc" sort}}>A to Z</Option>
|
|
||||||
<Option @value="Name:desc" @selected={{eq "Name:desc" sort}}>Z to A</Option>
|
|
||||||
</Optgroup>
|
|
||||||
<Optgroup @label="Creation">
|
|
||||||
<Option @value="CreateIndex:desc" @selected={{eq "CreateIndex:desc" sort}}>Newest to oldest</Option>
|
|
||||||
<Option @value="CreateIndex:asc" @selected={{eq "CreateIndex:asc" sort}}>Oldest to newest</Option>
|
|
||||||
</Optgroup>
|
|
||||||
{{/let}}
|
|
||||||
</BlockSlot>
|
|
||||||
</PopoverSelect>
|
|
||||||
</BlockSlot>
|
|
||||||
</SearchBar>
|
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
<BlockSlot @name="content">
|
<BlockSlot @name="content">
|
||||||
|
|
|
@ -4,113 +4,97 @@
|
||||||
{{title 'Access Controls'}}
|
{{title 'Access Controls'}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#let (or sortBy "CreateTime:desc") as |sort|}}
|
{{#let (hash
|
||||||
<AppView
|
types=(if type (split type ',') undefined)
|
||||||
@class="token list"
|
) as |filters|}}
|
||||||
@loading={{isLoading}}
|
{{#let (or sortBy "CreateTime:desc") as |sort|}}
|
||||||
@authorized={{isAuthorized}}
|
<AppView
|
||||||
@enabled={{isEnabled}}
|
@class="token list"
|
||||||
>
|
@loading={{isLoading}}
|
||||||
<BlockSlot @name="notification" as |status type subject|>
|
@authorized={{isAuthorized}}
|
||||||
{{partial 'dc/acls/tokens/notifications'}}
|
@enabled={{isEnabled}}
|
||||||
</BlockSlot>
|
>
|
||||||
<BlockSlot @name="header">
|
<BlockSlot @name="notification" as |status type subject|>
|
||||||
<h1>
|
{{partial 'dc/acls/tokens/notifications'}}
|
||||||
Access Controls
|
</BlockSlot>
|
||||||
</h1>
|
<BlockSlot @name="header">
|
||||||
</BlockSlot>
|
<h1>
|
||||||
<BlockSlot @name="nav">
|
Access Controls
|
||||||
{{#if isAuthorized }}
|
</h1>
|
||||||
{{partial 'dc/acls/nav'}}
|
</BlockSlot>
|
||||||
{{/if}}
|
<BlockSlot @name="nav">
|
||||||
</BlockSlot>
|
{{#if isAuthorized }}
|
||||||
<BlockSlot @name="disabled">
|
{{partial 'dc/acls/nav'}}
|
||||||
{{partial 'dc/acls/disabled'}}
|
{{/if}}
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
<BlockSlot @name="authorization">
|
<BlockSlot @name="disabled">
|
||||||
{{partial 'dc/acls/authorization'}}
|
{{partial 'dc/acls/disabled'}}
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
<BlockSlot @name="actions">
|
<BlockSlot @name="authorization">
|
||||||
<a data-test-create href="{{href-to 'dc.acls.tokens.create'}}" class="type-create">Create</a>
|
{{partial 'dc/acls/authorization'}}
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
<BlockSlot @name="toolbar">
|
<BlockSlot @name="actions">
|
||||||
{{#if (gt items.length 0)}}
|
<a data-test-create href="{{href-to 'dc.acls.tokens.create'}}" class="type-create">Create</a>
|
||||||
<SearchBar
|
</BlockSlot>
|
||||||
@value={{search}}
|
<BlockSlot @name="toolbar">
|
||||||
@onsearch={{action (mut search) value="target.value"}}
|
{{#if (gt items.length 0)}}
|
||||||
class="with-sort"
|
<ConsulTokenSearchBar
|
||||||
>
|
@search={{search}}
|
||||||
<BlockSlot @name="secondary">
|
@onsearch={{action (mut search) value="target.value"}}
|
||||||
<PopoverSelect
|
|
||||||
@position="right"
|
@sort={{sort}}
|
||||||
@onchange={{action (mut sortBy) value='target.selected'}}
|
@onsort={{action (mut sortBy) value="target.selected"}}
|
||||||
@multiple={{false}}
|
|
||||||
as |components|>
|
@filter={{filters}}
|
||||||
<BlockSlot @name="selected">
|
@onfilter={{hash
|
||||||
<span>
|
type=(action (mut type) value="target.selectedItems")
|
||||||
{{#let (from-entries (array
|
}}
|
||||||
(array "CreateTime:desc" "Newest to oldest")
|
/>
|
||||||
(array "CreateTime:asc" "Oldest to newest")
|
{{/if}}
|
||||||
))
|
</BlockSlot>
|
||||||
as |selectable|
|
<BlockSlot @name="content">
|
||||||
}}
|
{{#if (token/is-legacy items)}}
|
||||||
{{get selectable sort}}
|
<p data-test-notification-update class="notice info"><strong>Update.</strong> 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>
|
||||||
{{/let}}
|
{{/if}}
|
||||||
</span>
|
{{#let (filter (filter-predicate 'token' filters) items) as |filtered|}}
|
||||||
</BlockSlot>
|
{{#let (sort-by (comparator 'token' sort) filtered) as |sorted|}}
|
||||||
<BlockSlot @name="options">
|
<ChangeableSet @dispatcher={{searchable 'token' sorted}} @terms={{search}}>
|
||||||
{{#let components.Optgroup components.Option as |Optgroup Option|}}
|
<BlockSlot @name="set" as |searched|>
|
||||||
<Optgroup @label="Creation">
|
<ConsulTokenList
|
||||||
<Option @value="CreateTime:desc" @selected={{eq "CreateTime:desc" sort}}>Newest to oldest</Option>
|
@items={{searched}}
|
||||||
<Option @value="CreateTime:asc" @selected={{eq "CreateTime:asc" sort}}>Oldest to newest</Option>
|
@token={{token}}
|
||||||
</Optgroup>
|
@onuse={{action send 'use'}}
|
||||||
|
@ondelete={{action send 'delete'}}
|
||||||
|
@onlogout={{action send 'logout'}}
|
||||||
|
@onclone={{action send 'clone'}}
|
||||||
|
/>
|
||||||
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="empty">
|
||||||
|
<EmptyState @allowLogin={{true}}>
|
||||||
|
<BlockSlot @name="header">
|
||||||
|
<h2>
|
||||||
|
{{#if (gt items.length 0)}}
|
||||||
|
No tokens found
|
||||||
|
{{else}}
|
||||||
|
Welcome to ACL Tokens
|
||||||
|
{{/if}}
|
||||||
|
</h2>
|
||||||
|
</BlockSlot>
|
||||||
|
<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>
|
||||||
|
</BlockSlot>
|
||||||
|
</ChangeableSet>
|
||||||
|
{{/let}}
|
||||||
{{/let}}
|
{{/let}}
|
||||||
</BlockSlot>
|
|
||||||
</PopoverSelect>
|
|
||||||
</BlockSlot>
|
|
||||||
</SearchBar>
|
|
||||||
{{/if}}
|
|
||||||
</BlockSlot>
|
|
||||||
<BlockSlot @name="content">
|
|
||||||
{{#if (token/is-legacy items)}}
|
|
||||||
<p data-test-notification-update class="notice info"><strong>Update.</strong> 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>
|
|
||||||
{{/if}}
|
|
||||||
{{#let (sort-by (comparator 'token' sort) items) as |sorted|}}
|
|
||||||
<ChangeableSet @dispatcher={{searchable 'token' sorted}} @terms={{search}}>
|
|
||||||
<BlockSlot @name="set" as |filtered|>
|
|
||||||
<ConsulTokenList
|
|
||||||
@items={{filtered}}
|
|
||||||
@token={{token}}
|
|
||||||
@onuse={{action send 'use'}}
|
|
||||||
@ondelete={{action send 'delete'}}
|
|
||||||
@onlogout={{action send 'logout'}}
|
|
||||||
@onclone={{action send 'clone'}}
|
|
||||||
/>
|
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
<BlockSlot @name="empty">
|
</AppView>
|
||||||
<EmptyState @allowLogin={{true}}>
|
|
||||||
<BlockSlot @name="header">
|
|
||||||
<h2>
|
|
||||||
{{#if (gt items.length 0)}}
|
|
||||||
No tokens found
|
|
||||||
{{else}}
|
|
||||||
Welcome to ACL Tokens
|
|
||||||
{{/if}}
|
|
||||||
</h2>
|
|
||||||
</BlockSlot>
|
|
||||||
<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>
|
|
||||||
</BlockSlot>
|
|
||||||
</ChangeableSet>
|
|
||||||
{{/let}}
|
{{/let}}
|
||||||
</BlockSlot>
|
|
||||||
</AppView>
|
|
||||||
{{/let}}
|
{{/let}}
|
||||||
|
|
|
@ -6,11 +6,15 @@
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
|
|
||||||
<BlockSlot @name="loaded">
|
<BlockSlot @name="loaded">
|
||||||
|
{{#let api.data as |items|}}
|
||||||
|
{{#let (hash
|
||||||
|
accesses=(if access (split access ',') undefined)
|
||||||
|
) as |filters|}}
|
||||||
{{#let (or sortBy "Action:asc") as |sort|}}
|
{{#let (or sortBy "Action:asc") as |sort|}}
|
||||||
<AppView @class="intention list">
|
<AppView @class="intention list">
|
||||||
<BlockSlot @name="header">
|
<BlockSlot @name="header">
|
||||||
<h1>
|
<h1>
|
||||||
Intentions <em>{{format-number api.data.length}} total</em>
|
Intentions <em>{{format-number items.length}} total</em>
|
||||||
</h1>
|
</h1>
|
||||||
<label for="toolbar-toggle"></label>
|
<label for="toolbar-toggle"></label>
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
|
@ -18,73 +22,34 @@
|
||||||
<a data-test-create href="{{href-to 'dc.intentions.create'}}" class="type-create">Create</a>
|
<a data-test-create href="{{href-to 'dc.intentions.create'}}" class="type-create">Create</a>
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
<BlockSlot @name="toolbar">
|
<BlockSlot @name="toolbar">
|
||||||
{{#if (gt api.data.length 0) }}
|
{{#if (gt items.length 0) }}
|
||||||
<SearchBar
|
<ConsulIntentionSearchBar
|
||||||
@value={{search}}
|
@search={{search}}
|
||||||
@onsearch={{action (mut search) value="target.value"}}
|
@onsearch={{action (mut search) value="target.value"}}
|
||||||
class="with-sort"
|
|
||||||
>
|
@sort={{sort}}
|
||||||
<BlockSlot @name="secondary">
|
@onsort={{action (mut sortBy) value="target.selected"}}
|
||||||
<PopoverSelect
|
|
||||||
@position="right"
|
@filter={{filters}}
|
||||||
@onchange={{action (mut sortBy) value='target.selected'}}
|
@onfilter={{hash
|
||||||
@multiple={{false}}
|
access=(action (mut access) value="target.selectedItems")
|
||||||
as |components|>
|
}}
|
||||||
<BlockSlot @name="selected">
|
/>
|
||||||
<span>
|
{{/if}}
|
||||||
{{#let (from-entries (array
|
|
||||||
(array "Action:asc" "Allow to Deny")
|
|
||||||
(array "Action:desc" "Deny to Allow")
|
|
||||||
(array "SourceName:asc" "Source: A to Z")
|
|
||||||
(array "SourceName:desc" "Source: Z to A")
|
|
||||||
(array "DestinationName:asc" "Destination: A to Z")
|
|
||||||
(array "DestinationName:desc" "Destination: Z to A")
|
|
||||||
(array "Precedence:asc" "Precedence: Ascending")
|
|
||||||
(array "Precedence:desc" "Precedence: Descending")
|
|
||||||
))
|
|
||||||
as |selectable|
|
|
||||||
}}
|
|
||||||
{{get selectable sort}}
|
|
||||||
{{/let}}
|
|
||||||
</span>
|
|
||||||
</BlockSlot>
|
|
||||||
<BlockSlot @name="options">
|
|
||||||
{{#let components.Optgroup components.Option as |Optgroup Option|}}
|
|
||||||
<Optgroup @label="Permission">
|
|
||||||
<Option @value="Action:asc" @selected={{eq "Action:asc" sort}}>Allow to Deny</Option>
|
|
||||||
<Option @value="Action:desc" @selected={{eq "Action:desc" sort}}>Deny to Allow</Option>
|
|
||||||
</Optgroup>
|
|
||||||
<Optgroup @label="Source">
|
|
||||||
<Option @value="SourceName:asc" @selected={{eq "SourceName:asc" sort}}>A to Z</Option>
|
|
||||||
<Option @value="SourceName:desc" @selected={{eq "SourceName:desc" sort}}>Z to A</Option>
|
|
||||||
</Optgroup>
|
|
||||||
<Optgroup @label="Destination">
|
|
||||||
<Option @value="DestinationName:asc" @selected={{eq "DestinationName:asc" sort}}>A to Z</Option>
|
|
||||||
<Option @value="DestinationName:desc" @selected={{eq "DestinationName:desc" sort}}>Z to A</Option>
|
|
||||||
</Optgroup>
|
|
||||||
<Optgroup @label="Precedence">
|
|
||||||
<Option @value="Precedence:asc" @selected={{eq "Precedence:asc" sort}}>Ascending</Option>
|
|
||||||
<Option @value="Precedence:desc" @selected={{eq "Precedence:desc" sort}}>Descending</Option>
|
|
||||||
</Optgroup>
|
|
||||||
{{/let}}
|
|
||||||
</BlockSlot>
|
|
||||||
</PopoverSelect>
|
|
||||||
</BlockSlot>
|
|
||||||
</SearchBar>
|
|
||||||
{{/if}}
|
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
<BlockSlot @name="content">
|
<BlockSlot @name="content">
|
||||||
{{#let (sort-by (comparator 'intention' sort) api.data) as |sorted|}}
|
{{#let (filter (filter-predicate 'intention' filters) items) as |filtered|}}
|
||||||
|
{{#let (sort-by (comparator 'intention' sort) filtered) as |sorted|}}
|
||||||
<ChangeableSet @dispatcher={{searchable 'intention' sorted}} @terms={{search}}>
|
<ChangeableSet @dispatcher={{searchable 'intention' sorted}} @terms={{search}}>
|
||||||
<BlockSlot @name="content" as |filtered|>
|
<BlockSlot @name="content" as |searched|>
|
||||||
<ConsulIntentionList
|
<ConsulIntentionList
|
||||||
@items={{filtered}}
|
@items={{searched}}
|
||||||
@ondelete={{refresh-route}}
|
@ondelete={{refresh-route}}
|
||||||
>
|
>
|
||||||
<EmptyState @allowLogin={{true}}>
|
<EmptyState @allowLogin={{true}}>
|
||||||
<BlockSlot @name="header">
|
<BlockSlot @name="header">
|
||||||
<h2>
|
<h2>
|
||||||
{{#if (gt api.data.length 0)}}
|
{{#if (gt items.length 0)}}
|
||||||
No intentions found
|
No intentions found
|
||||||
{{else}}
|
{{else}}
|
||||||
Welcome to Intentions
|
Welcome to Intentions
|
||||||
|
@ -93,7 +58,7 @@
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
<BlockSlot @name="body">
|
<BlockSlot @name="body">
|
||||||
<p>
|
<p>
|
||||||
{{#if (gt api.data.length 0)}}
|
{{#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.
|
No intentions where found matching that search, or you may not have access to view the intentions you are searching for.
|
||||||
{{else}}
|
{{else}}
|
||||||
There don't seem to be any intentions, or you may not have access to view intentions yet.
|
There don't seem to be any intentions, or you may not have access to view intentions yet.
|
||||||
|
@ -112,9 +77,12 @@
|
||||||
</ConsulIntentionList>
|
</ConsulIntentionList>
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
</ChangeableSet>
|
</ChangeableSet>
|
||||||
{{/let}}
|
{{/let}}
|
||||||
|
{{/let}}
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
</AppView>
|
</AppView>
|
||||||
{{/let}}
|
{{/let}}
|
||||||
|
{{/let}}
|
||||||
|
{{/let}}
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
</DataLoader>
|
</DataLoader>
|
|
@ -23,46 +23,48 @@
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
<BlockSlot @name="toolbar">
|
<BlockSlot @name="toolbar">
|
||||||
{{#if (gt items.length 0) }}
|
{{#if (gt items.length 0) }}
|
||||||
<SearchBar
|
<form class="filter-bar with-sort">
|
||||||
@value={{search}}
|
<FreetextFilter
|
||||||
@onsearch={{action (mut search) value="target.value"}}
|
@onsearch={{action (mut search) value="target.value"}}
|
||||||
class="with-sort"
|
@value={{search}}
|
||||||
>
|
@placeholder="Search"
|
||||||
<BlockSlot @name="secondary">
|
/>
|
||||||
<PopoverSelect
|
<div class="sort">
|
||||||
@position="right"
|
<PopoverSelect
|
||||||
@onchange={{action (mut sortBy) value='target.selected'}}
|
class="type-sort"
|
||||||
@multiple={{false}}
|
@position="right"
|
||||||
as |components|>
|
@onchange={{action (mut sortBy) value='target.selected'}}
|
||||||
<BlockSlot @name="selected">
|
@multiple={{false}}
|
||||||
<span>
|
as |components|>
|
||||||
{{#let (from-entries (array
|
<BlockSlot @name="selected">
|
||||||
(array "Key:asc" "A to Z")
|
<span>
|
||||||
(array "Key:desc" "Z to A")
|
{{#let (from-entries (array
|
||||||
(array "isFolder:desc" "Folders to Keys")
|
(array "Key:asc" "A to Z")
|
||||||
(array "isFolder:asc" "Keys to Folders")
|
(array "Key:desc" "Z to A")
|
||||||
))
|
(array "isFolder:desc" "Folders to Keys")
|
||||||
as |selectable|
|
(array "isFolder:asc" "Keys to Folders")
|
||||||
}}
|
))
|
||||||
{{get selectable sort}}
|
as |selectable|
|
||||||
{{/let}}
|
}}
|
||||||
</span>
|
{{get selectable sort}}
|
||||||
</BlockSlot>
|
{{/let}}
|
||||||
<BlockSlot @name="options">
|
</span>
|
||||||
{{#let components.Optgroup components.Option as |Optgroup Option|}}
|
</BlockSlot>
|
||||||
<Optgroup @label="Name">
|
<BlockSlot @name="options">
|
||||||
<Option @value="Key:asc" @selected={{eq "Key:asc" sort}}>A to Z</Option>
|
{{#let components.Optgroup components.Option as |Optgroup Option|}}
|
||||||
<Option @value="Key:desc" @selected={{eq "Key:desc" sort}}>Z to A</Option>
|
<Optgroup @label="Name">
|
||||||
</Optgroup>
|
<Option @value="Key:asc" @selected={{eq "Key:asc" sort}}>A to Z</Option>
|
||||||
<Optgroup @label="Type">
|
<Option @value="Key:desc" @selected={{eq "Key:desc" sort}}>Z to A</Option>
|
||||||
<Option @value="isFolder:desc" @selected={{eq "isFolder:desc" sort}}>Folders to Keys</Option>
|
</Optgroup>
|
||||||
<Option @value="isFolder:asc" @selected={{eq "isFolder:asc" sort}}>Keys to Folders</Option>
|
<Optgroup @label="Type">
|
||||||
</Optgroup>
|
<Option @value="isFolder:desc" @selected={{eq "isFolder:desc" sort}}>Folders to Keys</Option>
|
||||||
{{/let}}
|
<Option @value="isFolder:asc" @selected={{eq "isFolder:asc" sort}}>Keys to Folders</Option>
|
||||||
</BlockSlot>
|
</Optgroup>
|
||||||
</PopoverSelect>
|
{{/let}}
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
</SearchBar>
|
</PopoverSelect>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
<BlockSlot @name="actions">
|
<BlockSlot @name="actions">
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
{{title 'Nodes'}}
|
{{title 'Nodes'}}
|
||||||
<EventSource @src={{items}} />
|
<EventSource @src={{items}} />
|
||||||
|
{{#let (hash
|
||||||
|
statuses=(if status (split status ',') undefined)
|
||||||
|
) as |filters|}}
|
||||||
{{#let (or sortBy "Node:asc") as |sort|}}
|
{{#let (or sortBy "Node:asc") as |sort|}}
|
||||||
<AppView @class="node list">
|
<AppView @class="node list">
|
||||||
<BlockSlot @name="header">
|
<BlockSlot @name="header">
|
||||||
|
@ -10,53 +13,26 @@
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
<BlockSlot @name="toolbar">
|
<BlockSlot @name="toolbar">
|
||||||
{{#if (gt items.length 0) }}
|
{{#if (gt items.length 0) }}
|
||||||
<SearchBar
|
<ConsulNodeSearchBar
|
||||||
@value={{search}}
|
@search={{search}}
|
||||||
@onsearch={{action (mut search) value="target.value"}}
|
@onsearch={{action (mut search) value="target.value"}}
|
||||||
class="with-sort"
|
|
||||||
>
|
@sort={{sort}}
|
||||||
<BlockSlot @name="secondary">
|
@onsort={{action (mut sortBy) value="target.selected"}}
|
||||||
<PopoverSelect
|
|
||||||
@position="right"
|
@filter={{filters}}
|
||||||
@onchange={{action (mut sortBy) value='target.selected'}}
|
@onfilter={{hash
|
||||||
@multiple={{false}}
|
status=(action (mut status) value="target.selectedItems")
|
||||||
as |components|>
|
}}
|
||||||
<BlockSlot @name="selected">
|
/>
|
||||||
<span>
|
|
||||||
{{#let (from-entries (array
|
|
||||||
(array "Node:asc" "A to Z")
|
|
||||||
(array "Node:desc" "Z to A")
|
|
||||||
(array "Status:asc" "Unhealthy to Healthy")
|
|
||||||
(array "Status:desc" "Healthy to Unhealthy")
|
|
||||||
))
|
|
||||||
as |selectable|
|
|
||||||
}}
|
|
||||||
{{get selectable sort}}
|
|
||||||
{{/let}}
|
|
||||||
</span>
|
|
||||||
</BlockSlot>
|
|
||||||
<BlockSlot @name="options">
|
|
||||||
{{#let components.Optgroup components.Option as |Optgroup Option|}}
|
|
||||||
<Optgroup @label="Name">
|
|
||||||
<Option @value="Node:asc" @selected={{eq "Node:asc" sort}}>A to Z</Option>
|
|
||||||
<Option @value="Node:desc" @selected={{eq "Node:desc" sort}}>Z to A</Option>
|
|
||||||
</Optgroup>
|
|
||||||
<Optgroup @label="Health Status">
|
|
||||||
<Option @value="Status:asc" @selected={{eq "Status:asc" sort}}>Unhealthy to Healthy</Option>
|
|
||||||
<Option @value="Status:desc" @selected={{eq "Status:desc" sort}}>Healthy to Unhealthy</Option>
|
|
||||||
</Optgroup>
|
|
||||||
{{/let}}
|
|
||||||
</BlockSlot>
|
|
||||||
</PopoverSelect>
|
|
||||||
</BlockSlot>
|
|
||||||
</SearchBar>
|
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
<BlockSlot @name="content">
|
<BlockSlot @name="content">
|
||||||
{{#let (sort-by (comparator 'node' sort) items) as |sorted|}}
|
{{#let (filter (filter-predicate 'node' filters) items) as |filtered|}}
|
||||||
|
{{#let (sort-by (comparator 'node' sort) filtered) as |sorted|}}
|
||||||
<ChangeableSet @dispatcher={{searchable 'node' sorted}} @terms={{search}}>
|
<ChangeableSet @dispatcher={{searchable 'node' sorted}} @terms={{search}}>
|
||||||
<BlockSlot @name="set" as |filtered|>
|
<BlockSlot @name="set" as |searched|>
|
||||||
<ConsulNodeList @items={{filtered}} @leader={{leader}} />
|
<ConsulNodeList @items={{searched}} @leader={{leader}} />
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
<BlockSlot @name="empty">
|
<BlockSlot @name="empty">
|
||||||
<EmptyState>
|
<EmptyState>
|
||||||
|
@ -68,7 +44,9 @@
|
||||||
</EmptyState>
|
</EmptyState>
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
</ChangeableSet>
|
</ChangeableSet>
|
||||||
{{/let}}
|
{{/let}}
|
||||||
|
{{/let}}
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
</AppView>
|
</AppView>
|
||||||
|
{{/let}}
|
||||||
{{/let}}
|
{{/let}}
|
|
@ -15,40 +15,14 @@
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
<BlockSlot @name="toolbar">
|
<BlockSlot @name="toolbar">
|
||||||
{{#if (gt items.length 0)}}
|
{{#if (gt items.length 0)}}
|
||||||
<SearchBar
|
<ConsulNspaceSearchBar
|
||||||
@value={{search}}
|
@search={{search}}
|
||||||
@onsearch={{action (mut search) value="target.value"}}
|
@onsearch={{action (mut search) value="target.value"}}
|
||||||
class="with-sort"
|
|
||||||
>
|
@sort={{sort}}
|
||||||
<BlockSlot @name="secondary">
|
@onsort={{action (mut sortBy) value="target.selected"}}
|
||||||
<PopoverSelect
|
|
||||||
@position="right"
|
/>
|
||||||
@onchange={{action (mut sortBy) value='target.selected'}}
|
|
||||||
@multiple={{false}}
|
|
||||||
as |components|>
|
|
||||||
<BlockSlot @name="selected">
|
|
||||||
<span>
|
|
||||||
{{#let (from-entries (array
|
|
||||||
(array "Name:asc" "A to Z")
|
|
||||||
(array "Name:desc" "Z to A")
|
|
||||||
))
|
|
||||||
as |selectable|
|
|
||||||
}}
|
|
||||||
{{get selectable sort}}
|
|
||||||
{{/let}}
|
|
||||||
</span>
|
|
||||||
</BlockSlot>
|
|
||||||
<BlockSlot @name="options">
|
|
||||||
{{#let components.Optgroup components.Option as |Optgroup Option|}}
|
|
||||||
<Optgroup @label="Name">
|
|
||||||
<Option @value="Name:asc" @selected={{eq "Name:asc" sort}}>A to Z</Option>
|
|
||||||
<Option @value="Name:desc" @selected={{eq "Name:desc" sort}}>Z to A</Option>
|
|
||||||
</Optgroup>
|
|
||||||
{{/let}}
|
|
||||||
</BlockSlot>
|
|
||||||
</PopoverSelect>
|
|
||||||
</BlockSlot>
|
|
||||||
</SearchBar>
|
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
<BlockSlot @name="content">
|
<BlockSlot @name="content">
|
||||||
|
|
|
@ -1,65 +1,46 @@
|
||||||
{{title 'Services'}}
|
{{title 'Services'}}
|
||||||
<EventSource @src={{items}} />
|
<EventSource @src={{items}} />
|
||||||
{{#let (or sortBy "Name:asc") as |sort|}}
|
{{#let (hash
|
||||||
|
statuses=(if status (split status ',') undefined)
|
||||||
|
types=(if type (split type ',') undefined)
|
||||||
|
sources=(if source (split source ',') undefined)
|
||||||
|
) as |filters|}}
|
||||||
|
{{#let (or sortBy "Name:asc") as |sort|}}
|
||||||
<AppView @class="service list">
|
<AppView @class="service list">
|
||||||
<BlockSlot @name="notification" as |status type|>
|
<BlockSlot @name="notification" as |status type|>
|
||||||
{{partial 'dc/services/notifications'}}
|
{{partial 'dc/services/notifications'}}
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
<BlockSlot @name="header">
|
<BlockSlot @name="header">
|
||||||
<h1>
|
<h1>
|
||||||
Services <em>{{format-number services.length}} total</em>
|
Services <em>{{format-number services.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 services.length 0) }}
|
{{#if (gt services.length 0) }}
|
||||||
<SearchBar
|
<ConsulServiceSearchBar
|
||||||
@value={{search}}
|
@sources={{externalSources}}
|
||||||
|
@search={{search}}
|
||||||
@onsearch={{action (mut search) value="target.value"}}
|
@onsearch={{action (mut search) value="target.value"}}
|
||||||
class="with-sort"
|
|
||||||
>
|
@sort={{sort}}
|
||||||
<BlockSlot @name="secondary">
|
@onsort={{action (mut sortBy) value="target.selected"}}
|
||||||
<PopoverSelect
|
|
||||||
@position="right"
|
@filter={{filters}}
|
||||||
@onchange={{action (mut sortBy) value='target.selected'}}
|
@onfilter={{hash
|
||||||
@multiple={{false}}
|
status=(action (mut status) value="target.selectedItems")
|
||||||
as |components|>
|
type=(action (mut type) value="target.selectedItems")
|
||||||
<BlockSlot @name="selected">
|
source=(action (mut source) value="target.selectedItems")
|
||||||
<span>
|
}}
|
||||||
{{#let (from-entries (array
|
/>
|
||||||
(array "Name:asc" "A to Z")
|
|
||||||
(array "Name:desc" "Z to A")
|
|
||||||
(array "Status:asc" "Unhealthy to Healthy")
|
|
||||||
(array "Status:desc" "Healthy to Unhealthy")
|
|
||||||
))
|
|
||||||
as |selectable|
|
|
||||||
}}
|
|
||||||
{{get selectable sort}}
|
|
||||||
{{/let}}
|
|
||||||
</span>
|
|
||||||
</BlockSlot>
|
|
||||||
<BlockSlot @name="options">
|
|
||||||
{{#let components.Optgroup components.Option as |Optgroup Option|}}
|
|
||||||
<Optgroup @label="Health Status">
|
|
||||||
<Option @value="Status:asc" @selected={{eq "Status:asc" sort}}>Unhealthy to Healthy</Option>
|
|
||||||
<Option @value="Status:desc" @selected={{eq "Status:desc" sort}}>Healthy to Unhealthy</Option>
|
|
||||||
</Optgroup>
|
|
||||||
<Optgroup @label="Service Name">
|
|
||||||
<Option @value="Name:asc" @selected={{eq "Name:asc" sort}}>A to Z</Option>
|
|
||||||
<Option @value="Name:desc" @selected={{eq "Name:desc" sort}}>Z to A</Option>
|
|
||||||
</Optgroup>
|
|
||||||
{{/let}}
|
|
||||||
</BlockSlot>
|
|
||||||
</PopoverSelect>
|
|
||||||
</BlockSlot>
|
|
||||||
</SearchBar>
|
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
<BlockSlot @name="content">
|
<BlockSlot @name="content">
|
||||||
{{#let (sort-by (comparator 'service' sort) services) as |sorted|}}
|
{{#let (filter (filter-predicate 'service' filters) services) as |filtered|}}
|
||||||
|
{{#let (sort-by (comparator 'service' sort) filtered) as |sorted|}}
|
||||||
<ChangeableSet @dispatcher={{searchable 'service' sorted}} @terms={{search}}>
|
<ChangeableSet @dispatcher={{searchable 'service' sorted}} @terms={{search}}>
|
||||||
<BlockSlot @name="set" as |filtered|>
|
<BlockSlot @name="set" as |searched|>
|
||||||
<ConsulServiceList @items={{filtered}}/>
|
<ConsulServiceList @items={{searched}} />
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
<BlockSlot @name="empty">
|
<BlockSlot @name="empty">
|
||||||
<EmptyState @allowLogin={{true}}>
|
<EmptyState @allowLogin={{true}}>
|
||||||
|
@ -92,7 +73,9 @@
|
||||||
</EmptyState>
|
</EmptyState>
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
</ChangeableSet>
|
</ChangeableSet>
|
||||||
{{/let}}
|
{{/let}}
|
||||||
|
{{/let}}
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
</AppView>
|
</AppView>
|
||||||
|
{{/let}}
|
||||||
{{/let}}
|
{{/let}}
|
||||||
|
|
|
@ -1,15 +1,32 @@
|
||||||
<div id="instances" class="tab-section">
|
<div id="instances" class="tab-section">
|
||||||
<div role="tabpanel">
|
<div role="tabpanel">
|
||||||
{{#if (gt items.length 0) }}
|
{{#let (hash
|
||||||
|
statuses=(if status (split status ',') undefined)
|
||||||
|
sources=(if source (split source ',') undefined)
|
||||||
|
) as |filters|}}
|
||||||
|
{{#let (or sortBy "Name:asc") as |sort|}}
|
||||||
|
{{#if (gt items.length 0) }}
|
||||||
<input type="checkbox" id="toolbar-toggle" />
|
<input type="checkbox" id="toolbar-toggle" />
|
||||||
<SearchBar
|
<ConsulServiceInstanceSearchBar
|
||||||
@value={{search}}
|
@sources={{externalSources}}
|
||||||
|
@search={{search}}
|
||||||
@onsearch={{action (mut search) value="target.value"}}
|
@onsearch={{action (mut search) value="target.value"}}
|
||||||
/>
|
|
||||||
{{/if}}
|
@sort={{sort}}
|
||||||
<ChangeableSet @dispatcher={{searchable 'serviceInstance' items}} @terms={{search}}>
|
@onsort={{action (mut sortBy) value="target.selected"}}
|
||||||
<BlockSlot @name="set" as |filtered|>
|
|
||||||
<ConsulServiceInstanceList @routeName="dc.services.instance" @items={{filtered}}/>
|
@filter={{filters}}
|
||||||
|
@onfilter={{hash
|
||||||
|
status=(action (mut status) value="target.selectedItems")
|
||||||
|
source=(action (mut source) value="target.selectedItems")
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{{/if}}
|
||||||
|
{{#let (filter (filter-predicate 'serviceInstance' filters) items) as |filtered|}}
|
||||||
|
{{#let (sort-by (comparator 'serviceInstance' sort) filtered) as |sorted|}}
|
||||||
|
<ChangeableSet @dispatcher={{searchable 'serviceInstance' sorted}} @terms={{search}}>
|
||||||
|
<BlockSlot @name="set" as |searched|>
|
||||||
|
<ConsulServiceInstanceList @routeName="dc.services.instance" @items={{searched}}/>
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
<BlockSlot @name="empty">
|
<BlockSlot @name="empty">
|
||||||
<EmptyState>
|
<EmptyState>
|
||||||
|
@ -21,5 +38,9 @@
|
||||||
</EmptyState>
|
</EmptyState>
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
</ChangeableSet>
|
</ChangeableSet>
|
||||||
|
{{/let}}
|
||||||
|
{{/let}}
|
||||||
|
{{/let}}
|
||||||
|
{{/let}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -3,87 +3,55 @@
|
||||||
<ErrorState @error={{api.error}} />
|
<ErrorState @error={{api.error}} />
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
<BlockSlot @name="loaded">
|
<BlockSlot @name="loaded">
|
||||||
|
{{#let api.data as |items|}}
|
||||||
|
{{#let (hash
|
||||||
|
accesses=(if access (split access ',') undefined)
|
||||||
|
) as |filters|}}
|
||||||
{{#let (or sortBy "Action:asc") as |sort|}}
|
{{#let (or sortBy "Action:asc") as |sort|}}
|
||||||
<div id="intentions" class="tab-section">
|
<div id="intentions" class="tab-section">
|
||||||
<div role="tabpanel">
|
<div role="tabpanel">
|
||||||
<Portal @target="app-view-actions">
|
<Portal @target="app-view-actions">
|
||||||
<a data-test-create href={{href-to 'dc.services.show.intentions.create'}} class="type-create">Create</a>
|
<a data-test-create href={{href-to 'dc.services.show.intentions.create'}} class="type-create">Create</a>
|
||||||
</Portal>
|
</Portal>
|
||||||
{{#if (gt api.data.length 0) }}
|
{{#if (gt items.length 0) }}
|
||||||
<SearchBar
|
<ConsulIntentionSearchBar
|
||||||
@value={{search}}
|
@search={{search}}
|
||||||
@onsearch={{action (mut search) value="target.value"}}
|
@onsearch={{action (mut search) value="target.value"}}
|
||||||
class="with-sort"
|
|
||||||
>
|
@sort={{sort}}
|
||||||
<BlockSlot @name="secondary">
|
@onsort={{action (mut sortBy) value="target.selected"}}
|
||||||
<PopoverSelect
|
|
||||||
@position="right"
|
@filter={{filters}}
|
||||||
@onchange={{action (mut sortBy) value='target.selected'}}
|
@onfilter={{hash
|
||||||
@multiple={{false}}
|
access=(action (mut access) value="target.selectedItems")
|
||||||
as |components|>
|
}}
|
||||||
<BlockSlot @name="selected">
|
/>
|
||||||
<span>
|
{{/if}}
|
||||||
{{#let (from-entries (array
|
{{#let (filter (filter-predicate 'intention' filters) items) as |filtered|}}
|
||||||
(array "Action:asc" "Allow to Deny")
|
{{#let (sort-by (comparator 'intention' sort) filtered) as |sorted|}}
|
||||||
(array "Action:desc" "Deny to Allow")
|
|
||||||
(array "SourceName:asc" "Source: A to Z")
|
|
||||||
(array "SourceName:desc" "Source: Z to A")
|
|
||||||
(array "DestinationName:asc" "Destination: A to Z")
|
|
||||||
(array "DestinationName:desc" "Destination: Z to A")
|
|
||||||
(array "Precedence:asc" "Precedence: Ascending")
|
|
||||||
(array "Precedence:desc" "Precedence: Descending")
|
|
||||||
))
|
|
||||||
as |selectable|
|
|
||||||
}}
|
|
||||||
{{get selectable sort}}
|
|
||||||
{{/let}}
|
|
||||||
</span>
|
|
||||||
</BlockSlot>
|
|
||||||
<BlockSlot @name="options">
|
|
||||||
{{#let components.Optgroup components.Option as |Optgroup Option|}}
|
|
||||||
<Optgroup @label="Permission">
|
|
||||||
<Option @value="Action:asc" @selected={{eq "Action:asc" sort}}>Allow to Deny</Option>
|
|
||||||
<Option @value="Action:desc" @selected={{eq "Action:desc" sort}}>Deny to Allow</Option>
|
|
||||||
</Optgroup>
|
|
||||||
<Optgroup @label="Source">
|
|
||||||
<Option @value="SourceName:asc" @selected={{eq "SourceName:asc" sort}}>A to Z</Option>
|
|
||||||
<Option @value="SourceName:desc" @selected={{eq "SourceName:desc" sort}}>Z to A</Option>
|
|
||||||
</Optgroup>
|
|
||||||
<Optgroup @label="Destination">
|
|
||||||
<Option @value="DestinationName:asc" @selected={{eq "DestinationName:asc" sort}}>A to Z</Option>
|
|
||||||
<Option @value="DestinationName:desc" @selected={{eq "DestinationName:desc" sort}}>Z to A</Option>
|
|
||||||
</Optgroup>
|
|
||||||
<Optgroup @label="Precedence">
|
|
||||||
<Option @value="Precedence:asc" @selected={{eq "Precedence:asc" sort}}>Ascending</Option>
|
|
||||||
<Option @value="Precedence:desc" @selected={{eq "Precedence:desc" sort}}>Descending</Option>
|
|
||||||
</Optgroup>
|
|
||||||
{{/let}}
|
|
||||||
</BlockSlot>
|
|
||||||
</PopoverSelect>
|
|
||||||
</BlockSlot>
|
|
||||||
</SearchBar>
|
|
||||||
{{/if}}
|
|
||||||
{{#let (sort-by (comparator 'intention' sort) api.data) as |sorted|}}
|
|
||||||
<ChangeableSet @dispatcher={{searchable 'intention' sorted}} @terms={{search}}>
|
<ChangeableSet @dispatcher={{searchable 'intention' sorted}} @terms={{search}}>
|
||||||
<BlockSlot @name="content" as |filtered|>
|
<BlockSlot @name="content" as |searched|>
|
||||||
<ConsulIntentionList
|
<ConsulIntentionList
|
||||||
@items={{filtered}}
|
@items={{searched}}
|
||||||
@ondelete={{refresh-route}}
|
@ondelete={{refresh-route}}
|
||||||
@routeName="dc.services.show.intentions.edit"
|
@routeName="dc.services.show.intentions.edit"
|
||||||
>
|
>
|
||||||
<EmptyState>
|
<EmptyState>
|
||||||
<BlockSlot @name="body">
|
<BlockSlot @name="body">
|
||||||
<p>
|
<p>
|
||||||
There are no intentions {{if (gt intentions.length 0) 'found '}} for this service.
|
There are no intentions {{if (gt items.length 0) 'found '}} for this service.
|
||||||
</p>
|
</p>
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
</EmptyState>
|
</EmptyState>
|
||||||
</ConsulIntentionList>
|
</ConsulIntentionList>
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
</ChangeableSet>
|
</ChangeableSet>
|
||||||
{{/let}}
|
{{/let}}
|
||||||
|
{{/let}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{/let}}
|
{{/let}}
|
||||||
|
{{/let}}
|
||||||
|
{{/let}}
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
</DataLoader>
|
</DataLoader>
|
||||||
|
|
|
@ -1,22 +1,50 @@
|
||||||
<div id="services" class="tab-section">
|
<div id="services" class="tab-section">
|
||||||
<div role="tabpanel">
|
<div role="tabpanel">
|
||||||
{{#if (gt gatewayServices.length 0)}}
|
{{#let (hash
|
||||||
<p>
|
instances=(if instance (split instance ',') undefined)
|
||||||
The following services may receive traffic from external services through this gateway. Learn more about configuring gateways in our
|
) as |filters|}}
|
||||||
<a href="{{env 'CONSUL_DOCS_URL'}}/connect/terminating-gateway" target="_blank" rel="noopener noreferrer">step-by-step guide</a>.
|
{{#let (or sortBy "Name:asc") as |sort|}}
|
||||||
</p>
|
{{#if (gt gatewayServices.length 0)}}
|
||||||
<ConsulServiceList
|
<input type="checkbox" id="toolbar-toggle" />
|
||||||
@items={{gatewayServices}}
|
<ConsulUpstreamSearchBar
|
||||||
@nspace={{nspace}}
|
@search={{search}}
|
||||||
/>
|
@onsearch={{action (mut search) value="target.value"}}
|
||||||
{{else}}
|
|
||||||
<EmptyState>
|
@sort={{sort}}
|
||||||
<BlockSlot @name="body">
|
@onsort={{action (mut sortBy) value="target.selected"}}
|
||||||
<p>
|
|
||||||
There are no linked services.
|
@filter={{filters}}
|
||||||
</p>
|
@onfilter={{hash
|
||||||
</BlockSlot>
|
instance=(action (mut instance) value="target.selectedItems")
|
||||||
</EmptyState>
|
}}
|
||||||
{{/if}}
|
/>
|
||||||
|
{{/if}}
|
||||||
|
<p>
|
||||||
|
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>.
|
||||||
|
</p>
|
||||||
|
{{#let (filter (filter-predicate 'service' filters) gatewayServices) as |filtered|}}
|
||||||
|
{{#let (sort-by (comparator 'service' sort) filtered) as |sorted|}}
|
||||||
|
<ChangeableSet @dispatcher={{searchable 'service' sorted}} @terms={{search}}>
|
||||||
|
<BlockSlot @name="set" as |searched|>
|
||||||
|
<ConsulServiceList
|
||||||
|
@items={{searched}}
|
||||||
|
@nspace={{nspace}}
|
||||||
|
/>
|
||||||
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="empty">
|
||||||
|
<EmptyState>
|
||||||
|
<BlockSlot @name="body">
|
||||||
|
<p>
|
||||||
|
There are no linked services.
|
||||||
|
</p>
|
||||||
|
</BlockSlot>
|
||||||
|
</EmptyState>
|
||||||
|
</BlockSlot>
|
||||||
|
</ChangeableSet>
|
||||||
|
{{/let}}
|
||||||
|
{{/let}}
|
||||||
|
{{/let}}
|
||||||
|
{{/let}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,18 +1,49 @@
|
||||||
<div id="upstreams" class="tab-section">
|
<div id="upstreams" class="tab-section">
|
||||||
<div role="tabpanel">
|
<div role="tabpanel">
|
||||||
{{#if (gt gatewayServices.length 0)}}
|
{{#let (hash
|
||||||
|
instances=(if instance (split instance ',') undefined)
|
||||||
|
) as |filters|}}
|
||||||
|
{{#let (or sortBy "Name:asc") as |sort|}}
|
||||||
|
{{#if (gt gatewayServices.length 0)}}
|
||||||
|
<input type="checkbox" id="toolbar-toggle" />
|
||||||
|
<ConsulUpstreamSearchBar
|
||||||
|
@search={{search}}
|
||||||
|
@onsearch={{action (mut search) value="target.value"}}
|
||||||
|
|
||||||
|
@sort={{sort}}
|
||||||
|
@onsort={{action (mut sortBy) value="target.selected"}}
|
||||||
|
|
||||||
|
@filter={{filters}}
|
||||||
|
@onfilter={{hash
|
||||||
|
instance=(action (mut instance) value="target.selectedItems")
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{{/if}}
|
||||||
<p>
|
<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>.
|
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>
|
</p>
|
||||||
<ConsulUpstreamList @items={{gatewayServices}} @dc={{dc}} @nspace={{nspace}} />
|
{{#let (filter (filter-predicate 'service' filters) gatewayServices) as |filtered|}}
|
||||||
{{else}}
|
{{#let (sort-by (comparator 'service' sort) filtered) as |sorted|}}
|
||||||
<EmptyState>
|
<ChangeableSet @dispatcher={{searchable 'service' sorted}} @terms={{search}}>
|
||||||
<BlockSlot @name="body">
|
<BlockSlot @name="set" as |searched|>
|
||||||
<p>
|
<ConsulServiceList
|
||||||
There are no upstreams.
|
@items={{searched}}
|
||||||
</p>
|
@nspace={{nspace}}
|
||||||
</BlockSlot>
|
/>
|
||||||
</EmptyState>
|
</BlockSlot>
|
||||||
{{/if}}
|
<BlockSlot @name="empty">
|
||||||
|
<EmptyState>
|
||||||
|
<BlockSlot @name="body">
|
||||||
|
<p>
|
||||||
|
There are no upstreams.
|
||||||
|
</p>
|
||||||
|
</BlockSlot>
|
||||||
|
</EmptyState>
|
||||||
|
</BlockSlot>
|
||||||
|
</ChangeableSet>
|
||||||
|
{{/let}}
|
||||||
|
{{/let}}
|
||||||
|
{{/let}}
|
||||||
|
{{/let}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,36 +1,36 @@
|
||||||
@setupApplicationTest
|
@setupApplicationTest
|
||||||
Feature: dc / acls / policies / sorting
|
Feature: dc / acls / policies / sorting
|
||||||
Scenario: Sorting Policies
|
Scenario: Sorting Policies
|
||||||
Given 1 datacenter model with the value "dc-1"
|
Given 1 datacenter model with the value "dc-1"
|
||||||
And 4 policy models from yaml
|
And 4 policy models from yaml
|
||||||
---
|
---
|
||||||
- Name: "system-A"
|
- Name: "system-A"
|
||||||
- Name: "system-D"
|
- Name: "system-D"
|
||||||
- Name: "system-C"
|
- Name: "system-C"
|
||||||
- Name: "system-B"
|
- Name: "system-B"
|
||||||
---
|
---
|
||||||
When I visit the policies page for yaml
|
When I visit the policies page for yaml
|
||||||
---
|
---
|
||||||
dc: dc-1
|
dc: dc-1
|
||||||
---
|
---
|
||||||
Then the url should be /dc-1/acls/policies
|
Then the url should be /dc-1/acls/policies
|
||||||
Then I see 4 policy models
|
Then I see 4 policy models
|
||||||
When I click selected on the sort
|
When I click selected on the sort
|
||||||
When I click options.1.button on the sort
|
When I click options.1.button on the sort
|
||||||
Then I see name on the policies vertically like yaml
|
Then I see name on the policies vertically like yaml
|
||||||
---
|
---
|
||||||
- "system-D"
|
- "system-D"
|
||||||
- "system-C"
|
- "system-C"
|
||||||
- "system-B"
|
- "system-B"
|
||||||
- "system-A"
|
- "system-A"
|
||||||
---
|
---
|
||||||
When I click selected on the sort
|
When I click selected on the sort
|
||||||
When I click options.0.button on the sort
|
When I click options.0.button on the sort
|
||||||
Then I see name on the policies vertically like yaml
|
Then I see name on the policies vertically like yaml
|
||||||
---
|
---
|
||||||
- "system-A"
|
- "system-A"
|
||||||
- "system-B"
|
- "system-B"
|
||||||
- "system-C"
|
- "system-C"
|
||||||
- "system-D"
|
- "system-D"
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
@ -1,73 +1,73 @@
|
||||||
@setupApplicationTest
|
@setupApplicationTest
|
||||||
Feature: dc / nodes / sorting
|
Feature: dc / nodes / sorting
|
||||||
Scenario:
|
Scenario:
|
||||||
Given 1 datacenter model with the value "dc-1"
|
Given 1 datacenter model with the value "dc-1"
|
||||||
And 6 node models from yaml
|
And 6 node models from yaml
|
||||||
---
|
---
|
||||||
- Node: Node-A
|
- Node: Node-A
|
||||||
Checks:
|
Checks:
|
||||||
- Status: critical
|
- Status: critical
|
||||||
- Node: Node-B
|
- Node: Node-B
|
||||||
Checks:
|
Checks:
|
||||||
- Status: passing
|
- Status: passing
|
||||||
- Node: Node-C
|
- Node: Node-C
|
||||||
Checks:
|
Checks:
|
||||||
- Status: warning
|
- Status: warning
|
||||||
- Node: Node-D
|
- Node: Node-D
|
||||||
Checks:
|
Checks:
|
||||||
- Status: critical
|
- Status: critical
|
||||||
- Node: Node-E
|
- Node: Node-E
|
||||||
Checks:
|
Checks:
|
||||||
- Status: critical
|
- Status: critical
|
||||||
- Node: Node-F
|
- Node: Node-F
|
||||||
Checks:
|
Checks:
|
||||||
- Status: warning
|
- Status: warning
|
||||||
---
|
---
|
||||||
When I visit the nodes page for yaml
|
When I visit the nodes page for yaml
|
||||||
---
|
---
|
||||||
dc: dc-1
|
dc: dc-1
|
||||||
---
|
---
|
||||||
When I click selected on the sort
|
When I click selected on the sort
|
||||||
When I click options.3.button on the sort
|
When I click options.0.button on the sort
|
||||||
Then I see name on the nodes vertically like yaml
|
Then I see name on the nodes vertically like yaml
|
||||||
---
|
---
|
||||||
- Node-B
|
- Node-A
|
||||||
- Node-C
|
- Node-D
|
||||||
- Node-F
|
- Node-E
|
||||||
- Node-A
|
- Node-C
|
||||||
- Node-D
|
- Node-F
|
||||||
- Node-E
|
- Node-B
|
||||||
---
|
---
|
||||||
When I click selected on the sort
|
When I click selected on the sort
|
||||||
When I click options.2.button on the sort
|
When I click options.1.button on the sort
|
||||||
Then I see name on the nodes vertically like yaml
|
Then I see name on the nodes vertically like yaml
|
||||||
---
|
---
|
||||||
- Node-A
|
- Node-B
|
||||||
- Node-D
|
- Node-C
|
||||||
- Node-E
|
- Node-F
|
||||||
- Node-C
|
- Node-A
|
||||||
- Node-F
|
- Node-D
|
||||||
- Node-B
|
- Node-E
|
||||||
---
|
---
|
||||||
When I click selected on the sort
|
When I click selected on the sort
|
||||||
When I click options.0.button on the sort
|
When I click options.2.button on the sort
|
||||||
Then I see name on the nodes vertically like yaml
|
Then I see name on the nodes vertically like yaml
|
||||||
---
|
---
|
||||||
- Node-A
|
- Node-A
|
||||||
- Node-B
|
- Node-B
|
||||||
- Node-C
|
- Node-C
|
||||||
- Node-D
|
- Node-D
|
||||||
- Node-E
|
- Node-E
|
||||||
- Node-F
|
- Node-F
|
||||||
---
|
---
|
||||||
When I click selected on the sort
|
When I click selected on the sort
|
||||||
When I click options.1.button on the sort
|
When I click options.3.button on the sort
|
||||||
Then I see name on the nodes vertically like yaml
|
Then I see name on the nodes vertically like yaml
|
||||||
---
|
---
|
||||||
- Node-F
|
- Node-F
|
||||||
- Node-E
|
- Node-E
|
||||||
- Node-D
|
- Node-D
|
||||||
- Node-C
|
- Node-C
|
||||||
- Node-B
|
- Node-B
|
||||||
- Node-A
|
- Node-A
|
||||||
---
|
---
|
||||||
|
|
|
@ -28,6 +28,7 @@ Feature: dc / services / show / upstreams
|
||||||
---
|
---
|
||||||
And the title should be "ingress-gateway-1 - Consul"
|
And the title should be "ingress-gateway-1 - Consul"
|
||||||
When I click upstreams on the tabs
|
When I click upstreams on the tabs
|
||||||
|
And I see upstreamsIsSelected on the tabs
|
||||||
Then I see 3 service models on the tabs.upstreamsTab component
|
Then I see 3 service models on the tabs.upstreamsTab component
|
||||||
Scenario: Don't see the Upstreams tab
|
Scenario: Don't see the Upstreams tab
|
||||||
Given 1 datacenter model with the value "dc1"
|
Given 1 datacenter model with the value "dc1"
|
||||||
|
|
|
@ -40,15 +40,28 @@ Feature: dc / services / sorting
|
||||||
dc: dc-1
|
dc: dc-1
|
||||||
---
|
---
|
||||||
When I click selected on the sort
|
When I click selected on the sort
|
||||||
When I click options.3.button on the sort
|
# unhealthy / healthy
|
||||||
|
When I click options.0.button on the sort
|
||||||
Then I see name on the services vertically like yaml
|
Then I see name on the services vertically like yaml
|
||||||
---
|
---
|
||||||
|
- Service-B
|
||||||
|
- Service-C
|
||||||
|
- Service-A
|
||||||
|
- Service-D
|
||||||
- Service-F
|
- Service-F
|
||||||
- Service-E
|
- Service-E
|
||||||
|
---
|
||||||
|
When I click selected on the sort
|
||||||
|
# healthy / unhealthy
|
||||||
|
When I click options.1.button on the sort
|
||||||
|
Then I see name on the services vertically like yaml
|
||||||
|
---
|
||||||
|
- Service-E
|
||||||
|
- Service-F
|
||||||
- Service-D
|
- Service-D
|
||||||
|
- Service-A
|
||||||
- Service-C
|
- Service-C
|
||||||
- Service-B
|
- Service-B
|
||||||
- Service-A
|
|
||||||
---
|
---
|
||||||
When I click selected on the sort
|
When I click selected on the sort
|
||||||
When I click options.2.button on the sort
|
When I click options.2.button on the sort
|
||||||
|
@ -62,24 +75,13 @@ Feature: dc / services / sorting
|
||||||
- Service-F
|
- Service-F
|
||||||
---
|
---
|
||||||
When I click selected on the sort
|
When I click selected on the sort
|
||||||
When I click options.0.button on the sort
|
When I click options.3.button on the sort
|
||||||
Then I see name on the services vertically like yaml
|
Then I see name on the services vertically like yaml
|
||||||
---
|
---
|
||||||
- Service-B
|
|
||||||
- Service-C
|
|
||||||
- Service-A
|
|
||||||
- Service-D
|
|
||||||
- Service-F
|
- Service-F
|
||||||
- Service-E
|
- Service-E
|
||||||
---
|
|
||||||
When I click selected on the sort
|
|
||||||
When I click options.1.button on the sort
|
|
||||||
Then I see name on the services vertically like yaml
|
|
||||||
---
|
|
||||||
- Service-E
|
|
||||||
- Service-F
|
|
||||||
- Service-D
|
- Service-D
|
||||||
- Service-A
|
|
||||||
- Service-C
|
- Service-C
|
||||||
- Service-B
|
- Service-B
|
||||||
|
- Service-A
|
||||||
---
|
---
|
||||||
|
|
|
@ -2,6 +2,6 @@ export default function(visitable, creatable, policies, popoverSelect) {
|
||||||
return creatable({
|
return creatable({
|
||||||
visit: visitable('/:dc/acls/policies'),
|
visit: visitable('/:dc/acls/policies'),
|
||||||
policies: policies(),
|
policies: policies(),
|
||||||
sort: popoverSelect(),
|
sort: popoverSelect('[data-test-sort-control]'),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ export default function(visitable, creatable, roles, popoverSelect) {
|
||||||
return {
|
return {
|
||||||
visit: visitable('/:dc/acls/roles'),
|
visit: visitable('/:dc/acls/roles'),
|
||||||
roles: roles(),
|
roles: roles(),
|
||||||
sort: popoverSelect(),
|
sort: popoverSelect('[data-test-sort-control]'),
|
||||||
...creatable(),
|
...creatable(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ export default function(visitable, creatable, text, tokens, popoverSelect) {
|
||||||
visit: visitable('/:dc/acls/tokens'),
|
visit: visitable('/:dc/acls/tokens'),
|
||||||
update: text('[data-test-notification-update]'),
|
update: text('[data-test-notification-update]'),
|
||||||
tokens: tokens(),
|
tokens: tokens(),
|
||||||
sort: popoverSelect(),
|
sort: popoverSelect('[data-test-sort-control]'),
|
||||||
...creatable(),
|
...creatable(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ export default function(visitable, creatable, clickable, intentions, popoverSele
|
||||||
return creatable({
|
return creatable({
|
||||||
visit: visitable('/:dc/intentions'),
|
visit: visitable('/:dc/intentions'),
|
||||||
intentions: intentions(),
|
intentions: intentions(),
|
||||||
sort: popoverSelect(),
|
sort: popoverSelect('[data-test-sort-control]'),
|
||||||
create: clickable('[data-test-create]'),
|
create: clickable('[data-test-create]'),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,6 @@ export default function(visitable, text, clickable, attribute, collection, popov
|
||||||
visit: visitable('/:dc/nodes'),
|
visit: visitable('/:dc/nodes'),
|
||||||
nodes: collection('.consul-node-list [data-test-list-row]', node),
|
nodes: collection('.consul-node-list [data-test-list-row]', node),
|
||||||
home: clickable('[data-test-home]'),
|
home: clickable('[data-test-home]'),
|
||||||
sort: popoverSelect(),
|
sort: popoverSelect('[data-test-sort-control]'),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,6 @@ export default function(visitable, creatable, nspaces, popoverSelect) {
|
||||||
return creatable({
|
return creatable({
|
||||||
visit: visitable('/:dc/namespaces'),
|
visit: visitable('/:dc/namespaces'),
|
||||||
nspaces: nspaces(),
|
nspaces: nspaces(),
|
||||||
sort: popoverSelect(),
|
sort: popoverSelect('[data-test-sort-control]'),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,6 @@ export default function(visitable, clickable, text, attribute, present, collecti
|
||||||
visit: visitable('/:dc/services'),
|
visit: visitable('/:dc/services'),
|
||||||
services: collection('.consul-service-list > ul > li:not(:first-child)', service),
|
services: collection('.consul-service-list > ul > li:not(:first-child)', service),
|
||||||
home: clickable('[data-test-home]'),
|
home: clickable('[data-test-home]'),
|
||||||
sort: popoverSelect(),
|
sort: popoverSelect('[data-test-sort-control]'),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@ export default function(visitable, attribute, collection, text, intentions, filt
|
||||||
intentions: intentions(),
|
intentions: intentions(),
|
||||||
};
|
};
|
||||||
page.tabs.upstreamsTab = {
|
page.tabs.upstreamsTab = {
|
||||||
services: collection('.consul-upstream-list > ul > li:not(:first-child)', {
|
services: collection('.consul-service-list > ul > li:not(:first-child)', {
|
||||||
name: text('[data-test-service-name]'),
|
name: text('[data-test-service-name]'),
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
import factory from 'consul-ui/filter/predicates/intention';
|
||||||
|
import { module, test } from 'qunit';
|
||||||
|
|
||||||
|
module('Unit | Filter | Predicates | intention', function() {
|
||||||
|
const predicate = factory();
|
||||||
|
|
||||||
|
test('it returns items depending on Action', function(assert) {
|
||||||
|
const items = [
|
||||||
|
{
|
||||||
|
Action: 'allow',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Action: 'deny',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
let expected, actual;
|
||||||
|
|
||||||
|
expected = [items[0]];
|
||||||
|
actual = items.filter(
|
||||||
|
predicate({
|
||||||
|
accesses: ['allow'],
|
||||||
|
})
|
||||||
|
);
|
||||||
|
assert.deepEqual(actual, expected);
|
||||||
|
|
||||||
|
expected = [items[1]];
|
||||||
|
actual = items.filter(
|
||||||
|
predicate({
|
||||||
|
accesses: ['deny'],
|
||||||
|
})
|
||||||
|
);
|
||||||
|
assert.deepEqual(actual, expected);
|
||||||
|
|
||||||
|
expected = items;
|
||||||
|
actual = items.filter(
|
||||||
|
predicate({
|
||||||
|
accesses: ['allow', 'deny'],
|
||||||
|
})
|
||||||
|
);
|
||||||
|
assert.deepEqual(actual, expected);
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,171 @@
|
||||||
|
import factory from 'consul-ui/filter/predicates/service';
|
||||||
|
import { module, test } from 'qunit';
|
||||||
|
|
||||||
|
module('Unit | Filter | Predicates | service', function() {
|
||||||
|
const predicate = factory();
|
||||||
|
|
||||||
|
test('it returns registered/unregistered items depending on instance count', function(assert) {
|
||||||
|
const items = [
|
||||||
|
{
|
||||||
|
InstanceCount: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
InstanceCount: 0,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
let expected, actual;
|
||||||
|
|
||||||
|
expected = [items[0]];
|
||||||
|
actual = items.filter(
|
||||||
|
predicate({
|
||||||
|
instances: ['registered'],
|
||||||
|
})
|
||||||
|
);
|
||||||
|
assert.deepEqual(actual, expected);
|
||||||
|
|
||||||
|
expected = [items[1]];
|
||||||
|
actual = items.filter(
|
||||||
|
predicate({
|
||||||
|
instances: ['not-registered'],
|
||||||
|
})
|
||||||
|
);
|
||||||
|
assert.deepEqual(actual, expected);
|
||||||
|
|
||||||
|
expected = items;
|
||||||
|
actual = items.filter(
|
||||||
|
predicate({
|
||||||
|
instances: ['registered', 'not-registered'],
|
||||||
|
})
|
||||||
|
);
|
||||||
|
assert.deepEqual(actual, expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('it returns items depending on status', function(assert) {
|
||||||
|
const items = [
|
||||||
|
{
|
||||||
|
MeshStatus: 'passing',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MeshStatus: 'warning',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MeshStatus: 'critical',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
let expected, actual;
|
||||||
|
|
||||||
|
expected = [items[0]];
|
||||||
|
actual = items.filter(
|
||||||
|
predicate({
|
||||||
|
statuses: ['passing'],
|
||||||
|
})
|
||||||
|
);
|
||||||
|
assert.deepEqual(actual, expected);
|
||||||
|
|
||||||
|
expected = [items[1]];
|
||||||
|
actual = items.filter(
|
||||||
|
predicate({
|
||||||
|
statuses: ['warning'],
|
||||||
|
})
|
||||||
|
);
|
||||||
|
assert.deepEqual(actual, expected);
|
||||||
|
|
||||||
|
expected = items;
|
||||||
|
actual = items.filter(
|
||||||
|
predicate({
|
||||||
|
statuses: ['passing', 'warning', 'critical'],
|
||||||
|
})
|
||||||
|
);
|
||||||
|
assert.deepEqual(actual, expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('it returns items depending on service type', function(assert) {
|
||||||
|
const items = [
|
||||||
|
{
|
||||||
|
Kind: 'ingress-gateway',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Kind: 'mesh-gateway',
|
||||||
|
},
|
||||||
|
{},
|
||||||
|
];
|
||||||
|
|
||||||
|
let expected, actual;
|
||||||
|
|
||||||
|
expected = [items[0]];
|
||||||
|
actual = items.filter(
|
||||||
|
predicate({
|
||||||
|
types: ['ingress-gateway'],
|
||||||
|
})
|
||||||
|
);
|
||||||
|
assert.deepEqual(actual, expected);
|
||||||
|
|
||||||
|
expected = [items[1]];
|
||||||
|
actual = items.filter(
|
||||||
|
predicate({
|
||||||
|
types: ['mesh-gateway'],
|
||||||
|
})
|
||||||
|
);
|
||||||
|
assert.deepEqual(actual, expected);
|
||||||
|
|
||||||
|
expected = items;
|
||||||
|
actual = items.filter(
|
||||||
|
predicate({
|
||||||
|
types: ['ingress-gateway', 'mesh-gateway', 'service'],
|
||||||
|
})
|
||||||
|
);
|
||||||
|
assert.deepEqual(actual, expected);
|
||||||
|
});
|
||||||
|
test('it returns items depending on a mixture of properties', function(assert) {
|
||||||
|
const items = [
|
||||||
|
{
|
||||||
|
Kind: 'ingress-gateway',
|
||||||
|
MeshStatus: 'passing',
|
||||||
|
InstanceCount: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Kind: 'mesh-gateway',
|
||||||
|
MeshStatus: 'warning',
|
||||||
|
InstanceCount: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MeshStatus: 'critical',
|
||||||
|
InstanceCount: 0,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
let expected, actual;
|
||||||
|
|
||||||
|
expected = [items[0]];
|
||||||
|
actual = items.filter(
|
||||||
|
predicate({
|
||||||
|
types: ['ingress-gateway'],
|
||||||
|
statuses: ['passing'],
|
||||||
|
instances: ['registered'],
|
||||||
|
})
|
||||||
|
);
|
||||||
|
assert.deepEqual(actual, expected);
|
||||||
|
|
||||||
|
expected = [items[1]];
|
||||||
|
actual = items.filter(
|
||||||
|
predicate({
|
||||||
|
types: ['mesh-gateway'],
|
||||||
|
statuses: ['warning'],
|
||||||
|
instances: ['registered'],
|
||||||
|
})
|
||||||
|
);
|
||||||
|
assert.deepEqual(actual, expected);
|
||||||
|
|
||||||
|
expected = items;
|
||||||
|
actual = items.filter(
|
||||||
|
predicate({
|
||||||
|
types: ['ingress-gateway', 'mesh-gateway', 'service'],
|
||||||
|
statuses: ['passing', 'warning', 'critical'],
|
||||||
|
instances: ['registered', 'not-registered'],
|
||||||
|
})
|
||||||
|
);
|
||||||
|
assert.deepEqual(actual, expected);
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in New Issue