ui: Search/sort improvements (#9183)
This commit is contained in:
parent
b8d6e195ed
commit
8a954f0639
|
@ -1,4 +1,7 @@
|
||||||
<form class="consul-intention-search-bar filter-bar">
|
<form
|
||||||
|
class="consul-intention-search-bar filter-bar"
|
||||||
|
...attributes
|
||||||
|
>
|
||||||
<FreetextFilter
|
<FreetextFilter
|
||||||
@onsearch={{action onsearch}}
|
@onsearch={{action onsearch}}
|
||||||
@value={{search}}
|
@value={{search}}
|
||||||
|
@ -19,6 +22,7 @@
|
||||||
{{#let components.Optgroup components.Option as |Optgroup Option|}}
|
{{#let components.Optgroup components.Option as |Optgroup Option|}}
|
||||||
<Option class="value-allow" @value="allow" @selected={{contains 'allow' filter.accesses}}>Allow</Option>
|
<Option class="value-allow" @value="allow" @selected={{contains 'allow' filter.accesses}}>Allow</Option>
|
||||||
<Option class="value-deny" @value="deny" @selected={{contains 'deny' filter.accesses}}>Deny</Option>
|
<Option class="value-deny" @value="deny" @selected={{contains 'deny' filter.accesses}}>Deny</Option>
|
||||||
|
<Option class="value-" @value="app-aware" @selected={{contains 'app-aware' filter.accesses}}>App aware</Option>
|
||||||
{{/let}}
|
{{/let}}
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
</PopoverSelect>
|
</PopoverSelect>
|
||||||
|
|
|
@ -1,57 +1,59 @@
|
||||||
{{#if (gt items.length 0)}}
|
{{#if (gt items.length 0)}}
|
||||||
<ListCollection @items={{items}} @linkable={{action "isLinkable"}} class="consul-nspace-list" as |item|>
|
<ListCollection @items={{items}} @linkable={{action "isLinkable"}} class="consul-nspace-list" as |item|>
|
||||||
<BlockSlot @name="header">
|
<BlockSlot @name="header">
|
||||||
{{#if item.DeletedAt}}
|
{{#if item.DeletedAt}}
|
||||||
<p>
|
<p>
|
||||||
Deleting {{item.Name}}...
|
Deleting {{item.Name}}...
|
||||||
</p>
|
</p>
|
||||||
{{else}}
|
{{else}}
|
||||||
<a data-test-nspace={{item.Name}} href={{href-to 'dc.nspaces.edit' item.Name}}>{{item.Name}}</a>
|
<a data-test-nspace={{item.Name}} href={{href-to 'dc.nspaces.edit' item.Name}}>{{item.Name}}</a>
|
||||||
{{/if}}
|
|
||||||
</BlockSlot>
|
|
||||||
<BlockSlot @name="details">
|
|
||||||
<dl>
|
|
||||||
<dt>Description</dt>
|
|
||||||
<dd data-test-description>
|
|
||||||
{{item.Description}}
|
|
||||||
</dd>
|
|
||||||
</dl>
|
|
||||||
{{#if (env 'CONSUL_ACLS_ENABLED')}}
|
|
||||||
<Consul::Token::Ruleset::List @item={{item}} />
|
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
<BlockSlot @name="actions" as |Actions|>
|
<BlockSlot @name="details">
|
||||||
{{#if (not item.DeletedAt)}}
|
<dl>
|
||||||
<Actions as |Action|>
|
<dt>Description</dt>
|
||||||
<Action data-test-edit-action @href={{href-to 'dc.nspaces.edit' item.Name}}>
|
<dd data-test-description>
|
||||||
<BlockSlot @name="label">
|
{{item.Description}}
|
||||||
Edit
|
</dd>
|
||||||
</BlockSlot>
|
</dl>
|
||||||
</Action>
|
{{#if (env 'CONSUL_ACLS_ENABLED')}}
|
||||||
{{#if (not-eq item.Name 'default') }}
|
<Consul::Token::Ruleset::List @item={{item}} />
|
||||||
<Action data-test-delete-action @onclick={{action ondelete item}} class="dangerous">
|
{{/if}}
|
||||||
<BlockSlot @name="label">
|
</BlockSlot>
|
||||||
Delete
|
<BlockSlot @name="actions" as |Actions|>
|
||||||
</BlockSlot>
|
{{#if (not item.DeletedAt)}}
|
||||||
<BlockSlot @name="confirmation" as |Confirmation|>
|
<Actions as |Action|>
|
||||||
<Confirmation class="warning">
|
<Action data-test-edit-action @href={{href-to 'dc.nspaces.edit' item.Name}}>
|
||||||
<BlockSlot @name="header">
|
<BlockSlot @name="label">
|
||||||
Confirm delete
|
Edit
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
<BlockSlot @name="body">
|
</Action>
|
||||||
<p>
|
{{#if (not-eq item.Name 'default') }}
|
||||||
Are you sure you want to delete this namespace?
|
<Action data-test-delete-action @onclick={{action ondelete item}} class="dangerous">
|
||||||
</p>
|
<BlockSlot @name="label">
|
||||||
</BlockSlot>
|
Delete
|
||||||
<BlockSlot @name="confirm" as |Confirm|>
|
</BlockSlot>
|
||||||
<Confirm>Delete</Confirm>
|
<BlockSlot @name="confirmation" as |Confirmation|>
|
||||||
</BlockSlot>
|
<Confirmation class="warning">
|
||||||
</Confirmation>
|
<BlockSlot @name="header">
|
||||||
</BlockSlot>
|
Confirm delete
|
||||||
</Action>
|
</BlockSlot>
|
||||||
{{/if}}
|
<BlockSlot @name="body">
|
||||||
</Actions>
|
<p>
|
||||||
{{/if}}
|
Are you sure you want to delete this namespace?
|
||||||
</BlockSlot>
|
</p>
|
||||||
</ListCollection>
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="confirm" as |Confirm|>
|
||||||
|
<Confirm>Delete</Confirm>
|
||||||
|
</BlockSlot>
|
||||||
|
</Confirmation>
|
||||||
|
</BlockSlot>
|
||||||
|
</Action>
|
||||||
|
{{/if}}
|
||||||
|
</Actions>
|
||||||
|
{{/if}}
|
||||||
|
</BlockSlot>
|
||||||
|
</ListCollection>
|
||||||
|
{{else}}
|
||||||
|
{{yield to="empty"}}
|
||||||
{{/if}}
|
{{/if}}
|
|
@ -28,7 +28,7 @@
|
||||||
<PopoverSelect
|
<PopoverSelect
|
||||||
class="select-type"
|
class="select-type"
|
||||||
@position="left"
|
@position="left"
|
||||||
@onchange={{action onfilter.type}}
|
@onchange={{action onfilter.kind}}
|
||||||
@multiple={{true}}
|
@multiple={{true}}
|
||||||
as |components|>
|
as |components|>
|
||||||
<BlockSlot @name="selected">
|
<BlockSlot @name="selected">
|
||||||
|
@ -38,8 +38,8 @@
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
<BlockSlot @name="options">
|
<BlockSlot @name="options">
|
||||||
{{#let components.Optgroup components.Option as |Optgroup Option|}}
|
{{#let components.Optgroup components.Option as |Optgroup Option|}}
|
||||||
<Option @value="global-management" @selected={{contains 'global-management' filter.types}}>Global Management</Option>
|
<Option @value="global-management" @selected={{contains 'global-management' filter.kinds}}>Global Management</Option>
|
||||||
<Option @value="standard" @selected={{contains 'standard' filter.types}}>Standard</Option>
|
<Option @value="standard" @selected={{contains 'standard' filter.kinds}}>Standard</Option>
|
||||||
{{/let}}
|
{{/let}}
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
</PopoverSelect>
|
</PopoverSelect>
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
</PopoverSelect>
|
</PopoverSelect>
|
||||||
<PopoverSelect
|
<PopoverSelect
|
||||||
@position="left"
|
@position="left"
|
||||||
@onchange={{action onfilter.type}}
|
@onchange={{action onfilter.kind}}
|
||||||
@multiple={{true}}
|
@multiple={{true}}
|
||||||
as |components|>
|
as |components|>
|
||||||
<BlockSlot @name="selected">
|
<BlockSlot @name="selected">
|
||||||
|
@ -37,15 +37,15 @@
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
<BlockSlot @name="options">
|
<BlockSlot @name="options">
|
||||||
{{#let components.Optgroup components.Option as |Optgroup Option|}}
|
{{#let components.Optgroup components.Option as |Optgroup Option|}}
|
||||||
<Option @value="service" @selected={{contains 'service' filter.types}}>Service</Option>
|
<Option @value="service" @selected={{contains 'service' filter.kinds}}>Service</Option>
|
||||||
<Optgroup @label="Gateway">
|
<Optgroup @label="Gateway">
|
||||||
<Option @value="ingress-gateway" @selected={{contains 'ingress-gateway' filter.types}}>Ingress Gateway</Option>
|
<Option @value="ingress-gateway" @selected={{contains 'ingress-gateway' filter.kinds}}>Ingress Gateway</Option>
|
||||||
<Option @value="terminating-gateway" @selected={{contains 'terminating-gateway' filter.types}}>Terminating Gateway</Option>
|
<Option @value="terminating-gateway" @selected={{contains 'terminating-gateway' filter.kinds}}>Terminating Gateway</Option>
|
||||||
<Option @value="mesh-gateway" @selected={{contains 'mesh-gateway' filter.types}}>Mesh Gateway</Option>
|
<Option @value="mesh-gateway" @selected={{contains 'mesh-gateway' filter.kinds}}>Mesh Gateway</Option>
|
||||||
</Optgroup>
|
</Optgroup>
|
||||||
<Optgroup @label="Mesh">
|
<Optgroup @label="Mesh">
|
||||||
<Option @value="in-mesh" @selected={{contains 'in-mesh' filter.types}}>In service mesh</Option>
|
<Option @value="in-mesh" @selected={{contains 'in-mesh' filter.kinds}}>In service mesh</Option>
|
||||||
<Option @value="not-in-mesh" @selected={{contains 'not-in-mesh' filter.types}}>Not in service mesh</Option>
|
<Option @value="not-in-mesh" @selected={{contains 'not-in-mesh' filter.kinds}}>Not in service mesh</Option>
|
||||||
</Optgroup>
|
</Optgroup>
|
||||||
{{/let}}
|
{{/let}}
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
<div class="filters">
|
<div class="filters">
|
||||||
<PopoverSelect
|
<PopoverSelect
|
||||||
@position="left"
|
@position="left"
|
||||||
@onchange={{action onfilter.type}}
|
@onchange={{action onfilter.kind}}
|
||||||
@multiple={{true}}
|
@multiple={{true}}
|
||||||
as |components|>
|
as |components|>
|
||||||
<BlockSlot @name="selected">
|
<BlockSlot @name="selected">
|
||||||
|
@ -17,9 +17,9 @@
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
<BlockSlot @name="options">
|
<BlockSlot @name="options">
|
||||||
{{#let components.Optgroup components.Option as |Optgroup Option|}}
|
{{#let components.Optgroup components.Option as |Optgroup Option|}}
|
||||||
<Option @value="global-management" @selected={{contains 'global-management' filter.types}}>Global Management</Option>
|
<Option @value="global-management" @selected={{contains 'global-management' filter.kinds}}>Global Management</Option>
|
||||||
<Option @value="global" @selected={{contains 'global' filter.types}}>Global</Option>
|
<Option @value="global" @selected={{contains 'global' filter.kinds}}>Global</Option>
|
||||||
<Option @value="local" @selected={{contains 'local' filter.types}}>Local</Option>
|
<Option @value="local" @selected={{contains 'local' filter.kinds}}>Local</Option>
|
||||||
{{/let}}
|
{{/let}}
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
</PopoverSelect>
|
</PopoverSelect>
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
import Controller from '@ember/controller';
|
import Controller from '@ember/controller';
|
||||||
|
|
||||||
export default class IndexController extends Controller {
|
export default class IndexController extends Controller {
|
||||||
queryParams = {
|
queryParams = {
|
||||||
sortBy: 'sort',
|
sortBy: 'sort',
|
||||||
dc: 'dc',
|
dc: 'dc',
|
||||||
type: 'type',
|
kind: 'kind',
|
||||||
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 class IndexController extends Controller {
|
export default class IndexController extends Controller {
|
||||||
queryParams = {
|
queryParams = {
|
||||||
sortBy: 'sort',
|
sortBy: 'sort',
|
||||||
|
kind: 'kind',
|
||||||
search: {
|
search: {
|
||||||
as: 'filter',
|
as: 'filter',
|
||||||
replace: true,
|
replace: true,
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import Controller from '@ember/controller';
|
import Controller from '@ember/controller';
|
||||||
export default class IndexController extends Controller {
|
export default class IndexController extends Controller {
|
||||||
queryParams = {
|
queryParams = {
|
||||||
|
sortBy: 'sort',
|
||||||
search: {
|
search: {
|
||||||
as: 'filter',
|
as: 'filter',
|
||||||
replace: true,
|
replace: true,
|
||||||
|
|
|
@ -6,9 +6,10 @@ export default class IndexController extends Controller {
|
||||||
sortBy: 'sort',
|
sortBy: 'sort',
|
||||||
status: 'status',
|
status: 'status',
|
||||||
source: 'source',
|
source: 'source',
|
||||||
type: 'type',
|
kind: 'kind',
|
||||||
search: {
|
search: {
|
||||||
as: 'filter',
|
as: 'filter',
|
||||||
|
replace: true,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ import Controller from '@ember/controller';
|
||||||
export default class IndexController extends Controller {
|
export default class IndexController extends Controller {
|
||||||
queryParams = {
|
queryParams = {
|
||||||
sortBy: 'sort',
|
sortBy: 'sort',
|
||||||
|
access: 'access',
|
||||||
search: {
|
search: {
|
||||||
as: 'filter',
|
as: 'filter',
|
||||||
replace: true,
|
replace: true,
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
export default () => ({ accesses = [] }) => item => {
|
import { andOr } from 'consul-ui/utils/filter';
|
||||||
if (accesses.length > 0) {
|
|
||||||
if (accesses.includes(item.Action)) {
|
export default andOr({
|
||||||
return true;
|
accesses: {
|
||||||
}
|
allow: (item, value) => item.Action === value,
|
||||||
return false;
|
deny: (item, value) => item.Action === value,
|
||||||
}
|
'app-aware': (item, value) => typeof item.Action === 'undefined',
|
||||||
return true;
|
},
|
||||||
};
|
});
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
export default () => ({ statuses = [] }) => {
|
import setHelpers from 'mnemonist/set';
|
||||||
return item => {
|
import { andOr } from 'consul-ui/utils/filter';
|
||||||
if (statuses.length > 0 && !statuses.includes(item.Status)) {
|
|
||||||
return false;
|
export default andOr({
|
||||||
}
|
statuses: {
|
||||||
return true;
|
passing: (item, value) => item.Status === value,
|
||||||
};
|
warning: (item, value) => item.Status === value,
|
||||||
};
|
critical: (item, value) => item.Status === value,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
|
@ -1,28 +1,15 @@
|
||||||
import setHelpers from 'mnemonist/set';
|
import setHelpers from 'mnemonist/set';
|
||||||
export default () => ({ dcs = [], types = [] }) => {
|
import { andOr } from 'consul-ui/utils/filter';
|
||||||
const typeIncludes = ['global-management', 'standard'].reduce((prev, item) => {
|
|
||||||
prev[item] = types.includes(item);
|
export default andOr({
|
||||||
return prev;
|
kinds: {
|
||||||
}, {});
|
'global-management': (item, value) => item.isGlobalManagement,
|
||||||
const selectedDcs = new Set(dcs);
|
standard: (item, value) => !item.isGlobalManagement,
|
||||||
return item => {
|
},
|
||||||
let type = true;
|
dcs: (item, values) => {
|
||||||
let dc = true;
|
return (
|
||||||
if (types.length > 0) {
|
typeof item.Datacenters === 'undefined' ||
|
||||||
type = false;
|
setHelpers.intersectionSize(values, new Set(item.Datacenters)) > 0
|
||||||
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;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
|
@ -1,19 +1,13 @@
|
||||||
import setHelpers from 'mnemonist/set';
|
import setHelpers from 'mnemonist/set';
|
||||||
export default () => ({ sources = [], statuses = [] }) => {
|
import { andOr } from 'consul-ui/utils/filter';
|
||||||
const uniqueSources = new Set(sources);
|
|
||||||
return item => {
|
export default andOr({
|
||||||
if (statuses.length > 0) {
|
statuses: {
|
||||||
if (statuses.includes(item.Status)) {
|
passing: (item, value) => item.Status === value,
|
||||||
return true;
|
warning: (item, value) => item.Status === value,
|
||||||
}
|
critical: (item, value) => item.Status === value,
|
||||||
return false;
|
},
|
||||||
}
|
sources: (item, values) => {
|
||||||
if (sources.length > 0) {
|
return setHelpers.intersectionSize(values, new Set(item.ExternalSources || [])) !== 0;
|
||||||
if (setHelpers.intersectionSize(uniqueSources, new Set(item.ExternalSources || [])) !== 0) {
|
},
|
||||||
return true;
|
});
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
|
@ -1,71 +1,25 @@
|
||||||
import setHelpers from 'mnemonist/set';
|
import setHelpers from 'mnemonist/set';
|
||||||
export default () => ({ instances = [], sources = [], statuses = [], types = [] }) => {
|
import { andOr } from 'consul-ui/utils/filter';
|
||||||
const uniqueSources = new Set(sources);
|
|
||||||
const typeIncludes = [
|
export default andOr({
|
||||||
'ingress-gateway',
|
kinds: {
|
||||||
'terminating-gateway',
|
'ingress-gateway': (item, value) => item.Kind === value,
|
||||||
'mesh-gateway',
|
'terminating-gateway': (item, value) => item.Kind === value,
|
||||||
'service',
|
'mesh-gateway': (item, value) => item.Kind === value,
|
||||||
'in-mesh',
|
service: (item, value) => !item.Kind,
|
||||||
'not-in-mesh',
|
'in-mesh': (item, value) => item.InMesh,
|
||||||
].reduce((prev, item) => {
|
'not-in-mesh': (item, value) => !item.InMesh,
|
||||||
prev[item] = types.includes(item);
|
},
|
||||||
return prev;
|
statuses: {
|
||||||
}, {});
|
passing: (item, value) => item.MeshStatus === value,
|
||||||
const instanceIncludes = ['registered', 'not-registered'].reduce((prev, item) => {
|
warning: (item, value) => item.MeshStatus === value,
|
||||||
prev[item] = instances.includes(item);
|
critical: (item, value) => item.MeshStatus === value,
|
||||||
return prev;
|
},
|
||||||
}, {});
|
instances: {
|
||||||
return item => {
|
registered: (item, value) => item.InstanceCount > 0,
|
||||||
if (statuses.length > 0) {
|
'not-registered': (item, value) => item.InstanceCount === 0,
|
||||||
if (statuses.includes(item.MeshStatus)) {
|
},
|
||||||
return true;
|
sources: (item, values) => {
|
||||||
}
|
return setHelpers.intersectionSize(values, new Set(item.ExternalSources || [])) !== 0;
|
||||||
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['in-mesh']) {
|
|
||||||
if (item.InMesh) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (typeIncludes['not-in-mesh']) {
|
|
||||||
if (!item.InMesh) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (sources.length > 0) {
|
|
||||||
if (setHelpers.intersectionSize(uniqueSources, new Set(item.ExternalSources || [])) !== 0) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
|
@ -1,21 +1,10 @@
|
||||||
export default () => ({ types = [] }) => {
|
import setHelpers from 'mnemonist/set';
|
||||||
const typeIncludes = ['global-management', 'global', 'local'].reduce((prev, item) => {
|
import { andOr } from 'consul-ui/utils/filter';
|
||||||
prev[item] = types.includes(item);
|
|
||||||
return prev;
|
export default andOr({
|
||||||
}, {});
|
kinds: {
|
||||||
return item => {
|
'global-management': (item, value) => item.isGlobalManagement,
|
||||||
if (types.length > 0) {
|
global: (item, value) => !item.Local,
|
||||||
if (typeIncludes['global-management'] && item.isGlobalManagement) {
|
local: (item, value) => item.Local,
|
||||||
return true;
|
},
|
||||||
}
|
});
|
||||||
if (typeIncludes['global'] && !item.Local) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (typeIncludes['local'] && item.Local) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
|
@ -5,11 +5,12 @@ import { hash } from 'rsvp';
|
||||||
import WithPolicyActions from 'consul-ui/mixins/policy/with-actions';
|
import WithPolicyActions from 'consul-ui/mixins/policy/with-actions';
|
||||||
|
|
||||||
export default class IndexRoute extends Route.extend(WithPolicyActions) {
|
export default class IndexRoute extends Route.extend(WithPolicyActions) {
|
||||||
@service('repository/policy')
|
@service('repository/policy') repo;
|
||||||
repo;
|
|
||||||
|
|
||||||
queryParams = {
|
queryParams = {
|
||||||
sortBy: 'sort',
|
sortBy: 'sort',
|
||||||
|
dc: 'dc',
|
||||||
|
kind: 'kind',
|
||||||
search: {
|
search: {
|
||||||
as: 'filter',
|
as: 'filter',
|
||||||
replace: true,
|
replace: true,
|
||||||
|
|
|
@ -5,8 +5,7 @@ import { hash } from 'rsvp';
|
||||||
import WithRoleActions from 'consul-ui/mixins/role/with-actions';
|
import WithRoleActions from 'consul-ui/mixins/role/with-actions';
|
||||||
|
|
||||||
export default class IndexRoute extends Route.extend(WithRoleActions) {
|
export default class IndexRoute extends Route.extend(WithRoleActions) {
|
||||||
@service('repository/role')
|
@service('repository/role') repo;
|
||||||
repo;
|
|
||||||
|
|
||||||
queryParams = {
|
queryParams = {
|
||||||
sortBy: 'sort',
|
sortBy: 'sort',
|
||||||
|
|
|
@ -3,15 +3,14 @@ import Route from 'consul-ui/routing/route';
|
||||||
import { hash } from 'rsvp';
|
import { hash } from 'rsvp';
|
||||||
import { get } from '@ember/object';
|
import { get } from '@ember/object';
|
||||||
import WithTokenActions from 'consul-ui/mixins/token/with-actions';
|
import WithTokenActions from 'consul-ui/mixins/token/with-actions';
|
||||||
export default class IndexRoute extends Route.extend(WithTokenActions) {
|
|
||||||
@service('repository/token')
|
|
||||||
repo;
|
|
||||||
|
|
||||||
@service('settings')
|
export default class IndexRoute extends Route.extend(WithTokenActions) {
|
||||||
settings;
|
@service('repository/token') repo;
|
||||||
|
@service('settings') settings;
|
||||||
|
|
||||||
queryParams = {
|
queryParams = {
|
||||||
sortBy: 'sort',
|
sortBy: 'sort',
|
||||||
|
kind: 'kind',
|
||||||
search: {
|
search: {
|
||||||
as: 'filter',
|
as: 'filter',
|
||||||
replace: true,
|
replace: true,
|
||||||
|
|
|
@ -3,6 +3,7 @@ import Route from 'consul-ui/routing/route';
|
||||||
export default class IndexRoute extends Route {
|
export default class IndexRoute extends Route {
|
||||||
queryParams = {
|
queryParams = {
|
||||||
sortBy: 'sort',
|
sortBy: 'sort',
|
||||||
|
access: 'access',
|
||||||
search: {
|
search: {
|
||||||
as: 'filter',
|
as: 'filter',
|
||||||
replace: true,
|
replace: true,
|
||||||
|
|
|
@ -5,16 +5,16 @@ import { get, action } from '@ember/object';
|
||||||
import isFolder from 'consul-ui/utils/isFolder';
|
import isFolder from 'consul-ui/utils/isFolder';
|
||||||
|
|
||||||
export default class IndexRoute extends Route {
|
export default class IndexRoute extends Route {
|
||||||
|
@service('repository/kv') repo;
|
||||||
|
|
||||||
queryParams = {
|
queryParams = {
|
||||||
|
sortBy: 'sort',
|
||||||
search: {
|
search: {
|
||||||
as: 'filter',
|
as: 'filter',
|
||||||
replace: true,
|
replace: true,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@service('repository/kv')
|
|
||||||
repo;
|
|
||||||
|
|
||||||
beforeModel() {
|
beforeModel() {
|
||||||
// we are index or folder, so if the key doesn't have a trailing slash
|
// we are index or folder, so if the key doesn't have a trailing slash
|
||||||
// add one to force a fake findBySlug
|
// add one to force a fake findBySlug
|
||||||
|
|
|
@ -3,11 +3,11 @@ import Route from 'consul-ui/routing/route';
|
||||||
import { hash } from 'rsvp';
|
import { hash } from 'rsvp';
|
||||||
|
|
||||||
export default class IndexRoute extends Route {
|
export default class IndexRoute extends Route {
|
||||||
@service('data-source/service')
|
@service('data-source/service') data;
|
||||||
data;
|
|
||||||
|
|
||||||
queryParams = {
|
queryParams = {
|
||||||
sortBy: 'sort',
|
sortBy: 'sort',
|
||||||
|
status: 'status',
|
||||||
search: {
|
search: {
|
||||||
as: 'filter',
|
as: 'filter',
|
||||||
replace: true,
|
replace: true,
|
||||||
|
|
|
@ -4,11 +4,8 @@ import { hash } from 'rsvp';
|
||||||
|
|
||||||
import WithNspaceActions from 'consul-ui/mixins/nspace/with-actions';
|
import WithNspaceActions from 'consul-ui/mixins/nspace/with-actions';
|
||||||
export default class IndexRoute extends Route.extend(WithNspaceActions) {
|
export default class IndexRoute extends Route.extend(WithNspaceActions) {
|
||||||
@service('data-source/service')
|
@service('data-source/service') data;
|
||||||
data;
|
@service('repository/nspace') repo;
|
||||||
|
|
||||||
@service('repository/nspace')
|
|
||||||
repo;
|
|
||||||
|
|
||||||
queryParams = {
|
queryParams = {
|
||||||
sortBy: 'sort',
|
sortBy: 'sort',
|
||||||
|
|
|
@ -3,18 +3,17 @@ import Route from 'consul-ui/routing/route';
|
||||||
import { hash } from 'rsvp';
|
import { hash } from 'rsvp';
|
||||||
|
|
||||||
export default class IndexRoute extends Route {
|
export default class IndexRoute extends Route {
|
||||||
@service('data-source/service')
|
@service('data-source/service') data;
|
||||||
data;
|
|
||||||
|
|
||||||
queryParams = {
|
queryParams = {
|
||||||
|
sortBy: 'sort',
|
||||||
|
status: 'status',
|
||||||
|
source: 'source',
|
||||||
|
kind: 'kind',
|
||||||
search: {
|
search: {
|
||||||
as: 'filter',
|
as: 'filter',
|
||||||
replace: true,
|
replace: true,
|
||||||
},
|
},
|
||||||
// temporary support of old style status
|
|
||||||
status: {
|
|
||||||
as: 'status',
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
model(params) {
|
model(params) {
|
||||||
|
|
|
@ -2,6 +2,9 @@ import Route from 'consul-ui/routing/route';
|
||||||
|
|
||||||
export default class InstancesRoute extends Route {
|
export default class InstancesRoute extends Route {
|
||||||
queryParams = {
|
queryParams = {
|
||||||
|
sortBy: 'sort',
|
||||||
|
status: 'status',
|
||||||
|
source: 'source',
|
||||||
search: {
|
search: {
|
||||||
as: 'filter',
|
as: 'filter',
|
||||||
replace: true,
|
replace: true,
|
||||||
|
|
|
@ -2,6 +2,8 @@ import Route from 'consul-ui/routing/route';
|
||||||
|
|
||||||
export default class IndexRoute extends Route {
|
export default class IndexRoute extends Route {
|
||||||
queryParams = {
|
queryParams = {
|
||||||
|
sortBy: 'sort',
|
||||||
|
access: 'access',
|
||||||
search: {
|
search: {
|
||||||
as: 'filter',
|
as: 'filter',
|
||||||
replace: true,
|
replace: true,
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
{{title 'Access Controls'}}
|
{{title 'Access Controls'}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#let (hash
|
{{#let (hash
|
||||||
types=(if type (split type ',') undefined)
|
kinds=(if kind (split kind ',') undefined)
|
||||||
dcs=(if dc (split dc ',') undefined)
|
dcs=(if dc (split dc ',') undefined)
|
||||||
) as |filters|}}
|
) as |filters|}}
|
||||||
{{#let (or sortBy "Name:asc") as |sort|}}
|
{{#let (or sortBy "Name:asc") as |sort|}}
|
||||||
|
@ -45,7 +45,7 @@
|
||||||
@filter={{filters}}
|
@filter={{filters}}
|
||||||
@onfilter={{hash
|
@onfilter={{hash
|
||||||
dc=(action (mut dc) value="target.selectedItems")
|
dc=(action (mut dc) value="target.selectedItems")
|
||||||
type=(action (mut type) value="target.selectedItems")
|
kind=(action (mut kind) value="target.selectedItems")
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#let (hash
|
{{#let (hash
|
||||||
types=(if type (split type ',') undefined)
|
kinds=(if kind (split kind ',') undefined)
|
||||||
) as |filters|}}
|
) as |filters|}}
|
||||||
{{#let (or sortBy "CreateTime:desc") as |sort|}}
|
{{#let (or sortBy "CreateTime:desc") as |sort|}}
|
||||||
<AppView
|
<AppView
|
||||||
|
@ -44,7 +44,7 @@
|
||||||
|
|
||||||
@filter={{filters}}
|
@filter={{filters}}
|
||||||
@onfilter={{hash
|
@onfilter={{hash
|
||||||
type=(action (mut type) value="target.selectedItems")
|
kind=(action (mut kind) value="target.selectedItems")
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
|
@ -32,39 +32,41 @@
|
||||||
<BlockSlot @name="content">
|
<BlockSlot @name="content">
|
||||||
{{#let (sort-by (comparator 'nspace' sort) items) as |sorted|}}
|
{{#let (sort-by (comparator 'nspace' sort) items) as |sorted|}}
|
||||||
<ChangeableSet @dispatcher={{searchable 'nspace' sorted}} @terms={{search}}>
|
<ChangeableSet @dispatcher={{searchable 'nspace' sorted}} @terms={{search}}>
|
||||||
<BlockSlot @name="set" as |filtered|>
|
<BlockSlot @name="content" as |filtered|>
|
||||||
<Consul::Nspace::List
|
<Consul::Nspace::List
|
||||||
@items={{filtered}}
|
@items={{filtered}}
|
||||||
@ondelete={{queue (action send 'delete')}}
|
@ondelete={{queue (action send 'delete')}}
|
||||||
>
|
>
|
||||||
<EmptyState @allowLogin={{true}}>
|
<:empty>
|
||||||
<BlockSlot @name="header">
|
<EmptyState @allowLogin={{true}}>
|
||||||
<h2>
|
<BlockSlot @name="header">
|
||||||
{{#if (gt items.length 0)}}
|
<h2>
|
||||||
No namespaces found
|
{{#if (gt items.length 0)}}
|
||||||
{{else}}
|
No namespaces found
|
||||||
Welcome to Namespaces
|
{{else}}
|
||||||
{{/if}}
|
Welcome to Namespaces
|
||||||
</h2>
|
{{/if}}
|
||||||
</BlockSlot>
|
</h2>
|
||||||
<BlockSlot @name="body">
|
</BlockSlot>
|
||||||
<p>
|
<BlockSlot @name="body">
|
||||||
{{#if (gt items.length 0)}}
|
<p>
|
||||||
No namespaces where found matching that search, or you may not have access to view the namespaces you are searching for.
|
{{#if (gt items.length 0)}}
|
||||||
{{else}}
|
No namespaces where found matching that search, or you may not have access to view the namespaces you are searching for.
|
||||||
There don't seem to be any namespaces, or you may not have access to view namespaces yet.
|
{{else}}
|
||||||
{{/if}}
|
There don't seem to be any namespaces, or you may not have access to view namespaces yet.
|
||||||
</p>
|
{{/if}}
|
||||||
</BlockSlot>
|
</p>
|
||||||
<BlockSlot @name="actions">
|
</BlockSlot>
|
||||||
<li class="docs-link">
|
<BlockSlot @name="actions">
|
||||||
<a href="{{env 'CONSUL_DOCS_URL'}}/commands/namespace" rel="noopener noreferrer" target="_blank">Documentation on namespaces</a>
|
<li class="docs-link">
|
||||||
</li>
|
<a href="{{env 'CONSUL_DOCS_URL'}}/commands/namespace" rel="noopener noreferrer" target="_blank">Documentation on namespaces</a>
|
||||||
<li class="learn-link">
|
</li>
|
||||||
<a href="{{env 'CONSUL_DOCS_LEARN_URL'}}/consul/namespaces/secure-namespaces" rel="noopener noreferrer" target="_blank">Read the guide</a>
|
<li class="learn-link">
|
||||||
</li>
|
<a href="{{env 'CONSUL_DOCS_LEARN_URL'}}/consul/namespaces/secure-namespaces" rel="noopener noreferrer" target="_blank">Read the guide</a>
|
||||||
</BlockSlot>
|
</li>
|
||||||
</EmptyState>
|
</BlockSlot>
|
||||||
|
</EmptyState>
|
||||||
|
</:empty>
|
||||||
</Consul::Nspace::List>
|
</Consul::Nspace::List>
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
</ChangeableSet>
|
</ChangeableSet>
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<EventSource @src={{items}} />
|
<EventSource @src={{items}} />
|
||||||
{{#let (hash
|
{{#let (hash
|
||||||
statuses=(if status (split status ',') undefined)
|
statuses=(if status (split status ',') undefined)
|
||||||
types=(if type (split type ',') undefined)
|
kinds=(if kind (split kind ',') undefined)
|
||||||
sources=(if source (split source ',') undefined)
|
sources=(if source (split source ',') undefined)
|
||||||
) as |filters|}}
|
) as |filters|}}
|
||||||
{{#let (or sortBy "Name:asc") as |sort|}}
|
{{#let (or sortBy "Name:asc") as |sort|}}
|
||||||
|
@ -32,7 +32,7 @@
|
||||||
@filter={{filters}}
|
@filter={{filters}}
|
||||||
@onfilter={{hash
|
@onfilter={{hash
|
||||||
status=(action (mut status) value="target.selectedItems")
|
status=(action (mut status) value="target.selectedItems")
|
||||||
type=(action (mut type) value="target.selectedItems")
|
kind=(action (mut kind) value="target.selectedItems")
|
||||||
source=(action (mut source) value="target.selectedItems")
|
source=(action (mut source) value="target.selectedItems")
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
import setHelpers from 'mnemonist/set';
|
||||||
|
|
||||||
|
const createPossibles = function(predicates) {
|
||||||
|
// create arrays of allowed values
|
||||||
|
return Object.entries(predicates).reduce((prev, [key, value]) => {
|
||||||
|
if (typeof value !== 'function') {
|
||||||
|
prev[key] = new Set(Object.keys(value));
|
||||||
|
} else {
|
||||||
|
prev[key] = null;
|
||||||
|
}
|
||||||
|
return prev;
|
||||||
|
}, {});
|
||||||
|
};
|
||||||
|
const sanitize = function(values, possibles) {
|
||||||
|
return Object.keys(possibles).reduce((prev, key) => {
|
||||||
|
// only set the value if the value has a length of > 0
|
||||||
|
const value = typeof values[key] === 'undefined' ? [] : values[key];
|
||||||
|
if (value.length > 0) {
|
||||||
|
if (possibles[key] !== null) {
|
||||||
|
// only include possible values
|
||||||
|
prev[key] = [...setHelpers.intersection(possibles[key], new Set(value))];
|
||||||
|
} else {
|
||||||
|
// only unique values
|
||||||
|
prev[key] = [...new Set(value)];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return prev;
|
||||||
|
}, {});
|
||||||
|
};
|
||||||
|
const execute = function(item, values, predicates) {
|
||||||
|
// every/and the top level values
|
||||||
|
return Object.entries(values).every(([key, values]) => {
|
||||||
|
let predicate = predicates[key];
|
||||||
|
if (typeof predicate === 'function') {
|
||||||
|
return predicate(item, values);
|
||||||
|
} else {
|
||||||
|
// if the top level values can have multiple values some/or them
|
||||||
|
return values.some(val => predicate[val](item, val));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
// exports a function that requires a hash of predicates passed in
|
||||||
|
export const andOr = predicates => {
|
||||||
|
// figure out all possible values from the hash of predicates
|
||||||
|
const possibles = createPossibles(predicates);
|
||||||
|
return () => values => {
|
||||||
|
// this is what is called post injection
|
||||||
|
// the actual user values are passed in here so 'sanitize' them which is
|
||||||
|
// basically checking against the possibles
|
||||||
|
values = sanitize(values, possibles);
|
||||||
|
// this is your actual filter predicate
|
||||||
|
return item => {
|
||||||
|
return execute(item, values, predicates);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
|
@ -97,7 +97,7 @@ module('Unit | Filter | Predicates | service', function() {
|
||||||
expected = [items[0]];
|
expected = [items[0]];
|
||||||
actual = items.filter(
|
actual = items.filter(
|
||||||
predicate({
|
predicate({
|
||||||
types: ['ingress-gateway'],
|
kinds: ['ingress-gateway'],
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
assert.deepEqual(actual, expected);
|
assert.deepEqual(actual, expected);
|
||||||
|
@ -105,7 +105,7 @@ module('Unit | Filter | Predicates | service', function() {
|
||||||
expected = [items[1]];
|
expected = [items[1]];
|
||||||
actual = items.filter(
|
actual = items.filter(
|
||||||
predicate({
|
predicate({
|
||||||
types: ['mesh-gateway'],
|
kinds: ['mesh-gateway'],
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
assert.deepEqual(actual, expected);
|
assert.deepEqual(actual, expected);
|
||||||
|
@ -113,7 +113,7 @@ module('Unit | Filter | Predicates | service', function() {
|
||||||
expected = items;
|
expected = items;
|
||||||
actual = items.filter(
|
actual = items.filter(
|
||||||
predicate({
|
predicate({
|
||||||
types: ['ingress-gateway', 'mesh-gateway', 'service'],
|
kinds: ['ingress-gateway', 'mesh-gateway', 'service'],
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
assert.deepEqual(actual, expected);
|
assert.deepEqual(actual, expected);
|
||||||
|
@ -141,7 +141,7 @@ module('Unit | Filter | Predicates | service', function() {
|
||||||
expected = [items[0]];
|
expected = [items[0]];
|
||||||
actual = items.filter(
|
actual = items.filter(
|
||||||
predicate({
|
predicate({
|
||||||
types: ['ingress-gateway'],
|
kinds: ['ingress-gateway'],
|
||||||
statuses: ['passing'],
|
statuses: ['passing'],
|
||||||
instances: ['registered'],
|
instances: ['registered'],
|
||||||
})
|
})
|
||||||
|
@ -151,7 +151,7 @@ module('Unit | Filter | Predicates | service', function() {
|
||||||
expected = [items[1]];
|
expected = [items[1]];
|
||||||
actual = items.filter(
|
actual = items.filter(
|
||||||
predicate({
|
predicate({
|
||||||
types: ['mesh-gateway'],
|
kinds: ['mesh-gateway'],
|
||||||
statuses: ['warning'],
|
statuses: ['warning'],
|
||||||
instances: ['registered'],
|
instances: ['registered'],
|
||||||
})
|
})
|
||||||
|
@ -161,7 +161,7 @@ module('Unit | Filter | Predicates | service', function() {
|
||||||
expected = items;
|
expected = items;
|
||||||
actual = items.filter(
|
actual = items.filter(
|
||||||
predicate({
|
predicate({
|
||||||
types: ['ingress-gateway', 'mesh-gateway', 'service'],
|
kinds: ['ingress-gateway', 'mesh-gateway', 'service'],
|
||||||
statuses: ['passing', 'warning', 'critical'],
|
statuses: ['passing', 'warning', 'critical'],
|
||||||
instances: ['registered', 'not-registered'],
|
instances: ['registered', 'not-registered'],
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in New Issue