mirror of https://github.com/hashicorp/consul
UI: Move legacy ACLs to use the new searchables/changeable-sets (#4933)
parent
c532e53d9a
commit
104e6bac71
|
@ -1,11 +1,12 @@
|
||||||
import Controller from '@ember/controller';
|
import Controller from '@ember/controller';
|
||||||
import { computed, get } from '@ember/object';
|
import { computed, get } from '@ember/object';
|
||||||
import WithFiltering from 'consul-ui/mixins/with-filtering';
|
import WithFiltering from 'consul-ui/mixins/with-filtering';
|
||||||
|
import WithSearching from 'consul-ui/mixins/with-searching';
|
||||||
import ucfirst from 'consul-ui/utils/ucfirst';
|
import ucfirst from 'consul-ui/utils/ucfirst';
|
||||||
const countType = function(items, type) {
|
const countType = function(items, type) {
|
||||||
return type === '' ? get(items, 'length') : items.filterBy('Type', type).length;
|
return type === '' ? get(items, 'length') : items.filterBy('Type', type).length;
|
||||||
};
|
};
|
||||||
export default Controller.extend(WithFiltering, {
|
export default Controller.extend(WithSearching, WithFiltering, {
|
||||||
queryParams: {
|
queryParams: {
|
||||||
type: {
|
type: {
|
||||||
as: 'type',
|
as: 'type',
|
||||||
|
@ -15,6 +16,17 @@ export default Controller.extend(WithFiltering, {
|
||||||
replace: true,
|
replace: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
init: function() {
|
||||||
|
this.searchParams = {
|
||||||
|
acl: 's',
|
||||||
|
};
|
||||||
|
this._super(...arguments);
|
||||||
|
},
|
||||||
|
searchable: computed('filtered', function() {
|
||||||
|
return get(this, 'searchables.acl')
|
||||||
|
.add(get(this, 'filtered'))
|
||||||
|
.search(get(this, this.searchParams.acl));
|
||||||
|
}),
|
||||||
typeFilters: computed('items', function() {
|
typeFilters: computed('items', function() {
|
||||||
const items = get(this, 'items');
|
const items = get(this, 'items');
|
||||||
return ['', 'management', 'client'].map(function(item) {
|
return ['', 'management', 'client'].map(function(item) {
|
||||||
|
@ -27,18 +39,8 @@ export default Controller.extend(WithFiltering, {
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
// TODO: This should be using a searchable
|
filter: function(item, { type = '' }) {
|
||||||
filter: function(item, { s = '', type = '' }) {
|
return type === '' || get(item, 'Type') === type;
|
||||||
const sLower = s.toLowerCase();
|
|
||||||
return (
|
|
||||||
(get(item, 'Name')
|
|
||||||
.toLowerCase()
|
|
||||||
.indexOf(sLower) !== -1 ||
|
|
||||||
get(item, 'ID')
|
|
||||||
.toLowerCase()
|
|
||||||
.indexOf(sLower) !== -1) &&
|
|
||||||
(type === '' || get(item, 'Type') === type)
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
sendClone: function(item) {
|
sendClone: function(item) {
|
||||||
|
|
|
@ -2,6 +2,7 @@ import intention from 'consul-ui/search/filters/intention';
|
||||||
import token from 'consul-ui/search/filters/token';
|
import token from 'consul-ui/search/filters/token';
|
||||||
import policy from 'consul-ui/search/filters/policy';
|
import policy from 'consul-ui/search/filters/policy';
|
||||||
import kv from 'consul-ui/search/filters/kv';
|
import kv from 'consul-ui/search/filters/kv';
|
||||||
|
import acl from 'consul-ui/search/filters/acl';
|
||||||
import node from 'consul-ui/search/filters/node';
|
import node from 'consul-ui/search/filters/node';
|
||||||
// service instance
|
// service instance
|
||||||
import nodeService from 'consul-ui/search/filters/node/service';
|
import nodeService from 'consul-ui/search/filters/node/service';
|
||||||
|
@ -16,6 +17,7 @@ export function initialize(application) {
|
||||||
const searchables = {
|
const searchables = {
|
||||||
intention: intention(filterable),
|
intention: intention(filterable),
|
||||||
token: token(filterable),
|
token: token(filterable),
|
||||||
|
acl: acl(filterable),
|
||||||
policy: policy(filterable),
|
policy: policy(filterable),
|
||||||
kv: kv(filterable),
|
kv: kv(filterable),
|
||||||
healthyNode: node(filterable),
|
healthyNode: node(filterable),
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
import { get } from '@ember/object';
|
||||||
|
export default function(filterable) {
|
||||||
|
return filterable(function(item, { s = '' }) {
|
||||||
|
const sLower = s.toLowerCase();
|
||||||
|
return (
|
||||||
|
get(item, 'Name')
|
||||||
|
.toLowerCase()
|
||||||
|
.indexOf(sLower) !== -1 ||
|
||||||
|
get(item, 'ID')
|
||||||
|
.toLowerCase()
|
||||||
|
.indexOf(sLower) !== -1
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
{{!<form>}}
|
{{!<form>}}
|
||||||
{{freetext-filter onchange=(action onchange) value=search placeholder="Search by name/token"}}
|
{{freetext-filter searchable=searchable value=search placeholder="Search by name/token"}}
|
||||||
{{radio-group name="type" value=type items=filters onchange=(action onchange)}}
|
{{radio-group name="type" value=type items=filters onchange=(action onchange)}}
|
||||||
{{!</form>}}
|
{{!</form>}}
|
||||||
|
|
|
@ -13,87 +13,90 @@
|
||||||
{{/block-slot}}
|
{{/block-slot}}
|
||||||
{{#block-slot 'toolbar'}}
|
{{#block-slot 'toolbar'}}
|
||||||
{{#if (gt items.length 0) }}
|
{{#if (gt items.length 0) }}
|
||||||
{{acl-filter filters=typeFilters search=filters.s type=filters.type onchange=(action 'filter')}}
|
{{acl-filter searchable=searchable filters=typeFilters search=filters.s type=filters.type onchange=(action 'filter')}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{/block-slot}}
|
{{/block-slot}}
|
||||||
{{#block-slot 'content'}}
|
{{#block-slot 'content'}}
|
||||||
{{#if (gt filtered.length 0)}}
|
{{#changeable-set dispatcher=searchable}}
|
||||||
{{#tabular-collection
|
{{#block-slot 'set' as |filtered|}}
|
||||||
items=(sort-by 'Name:asc' filtered) as |item index|
|
{{#tabular-collection
|
||||||
}}
|
items=(sort-by 'Name:asc' filtered) as |item index|
|
||||||
{{#block-slot 'header'}}
|
}}
|
||||||
<th>Name</th>
|
{{#block-slot 'header'}}
|
||||||
<th>Type</th>
|
<th>Name</th>
|
||||||
{{/block-slot}}
|
<th>Type</th>
|
||||||
{{#block-slot 'row'}}
|
{{/block-slot}}
|
||||||
<td data-test-acl="{{item.Name}}">
|
{{#block-slot 'row'}}
|
||||||
<a href={{href-to 'dc.acls.edit' item.ID}}>{{item.Name}}</a>
|
<td data-test-acl="{{item.Name}}">
|
||||||
</td>
|
<a href={{href-to 'dc.acls.edit' item.ID}}>{{item.Name}}</a>
|
||||||
<td>
|
</td>
|
||||||
{{#if (eq item.Type 'management')}}
|
<td>
|
||||||
<strong>{{item.Type}}</strong>
|
{{#if (eq item.Type 'management')}}
|
||||||
{{else}}
|
<strong>{{item.Type}}</strong>
|
||||||
<span>{{item.Type}}</span>
|
{{else}}
|
||||||
{{/if}}
|
<span>{{item.Type}}</span>
|
||||||
</td>
|
{{/if}}
|
||||||
{{/block-slot}}
|
</td>
|
||||||
{{#block-slot 'actions' as |index change checked|}}
|
{{/block-slot}}
|
||||||
{{#confirmation-dialog confirming=false index=index}}
|
{{#block-slot 'actions' as |index change checked|}}
|
||||||
{{#block-slot 'action' as |confirm|}}
|
{{#confirmation-dialog confirming=false index=index}}
|
||||||
{{#action-group index=index onchange=(action change) checked=(if (eq checked index) 'checked')}}
|
{{#block-slot 'action' as |confirm|}}
|
||||||
<ul>
|
{{#action-group index=index onchange=(action change) checked=(if (eq checked index) 'checked')}}
|
||||||
<li>
|
<ul>
|
||||||
<a data-test-edit href={{href-to 'dc.acls.edit' item.ID}}>Edit</a>
|
<li>
|
||||||
</li>
|
<a data-test-edit href={{href-to 'dc.acls.edit' item.ID}}>Edit</a>
|
||||||
{{#if (eq item.ID token.SecretID) }}
|
</li>
|
||||||
<li>
|
{{#if (eq item.ID token.SecretID) }}
|
||||||
<a data-test-logout onclick={{queue (action confirm 'logout' item) (action change)}}>Stop using</a>
|
<li>
|
||||||
</li>
|
<a data-test-logout onclick={{queue (action confirm 'logout' item) (action change)}}>Stop using</a>
|
||||||
{{else}}
|
</li>
|
||||||
|
{{else}}
|
||||||
|
|
||||||
<li>
|
<li>
|
||||||
<a data-test-use onclick={{queue (action confirm 'use' item) (action change)}}>Use</a>
|
<a data-test-use onclick={{queue (action confirm 'use' item) (action change)}}>Use</a>
|
||||||
</li>
|
</li>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
<li>
|
<li>
|
||||||
<a data-test-clone onclick={{action 'sendClone' item}}>Clone</a>
|
<a data-test-clone onclick={{action 'sendClone' item}}>Clone</a>
|
||||||
</li>
|
</li>
|
||||||
{{# if (not-eq item.ID 'anonymous') }}
|
{{# if (not-eq item.ID 'anonymous') }}
|
||||||
<li>
|
<li>
|
||||||
<a data-test-delete onclick={{action confirm 'delete' item}}>Delete</a>
|
<a data-test-delete onclick={{action confirm 'delete' item}}>Delete</a>
|
||||||
</li>
|
</li>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</ul>
|
</ul>
|
||||||
{{/action-group}}
|
{{/action-group}}
|
||||||
{{/block-slot}}
|
{{/block-slot}}
|
||||||
{{#block-slot 'dialog' as |execute cancel message name|}}
|
{{#block-slot 'dialog' as |execute cancel message name|}}
|
||||||
<p>
|
<p>
|
||||||
{{#if (eq name 'delete')}}
|
{{#if (eq name 'delete')}}
|
||||||
Are you sure you want to delete this ACL token?
|
Are you sure you want to delete this ACL token?
|
||||||
{{else if (eq name 'logout')}}
|
{{else if (eq name 'logout')}}
|
||||||
Are you sure you want to stop using this ACL token? This will log you out.
|
Are you sure you want to stop using this ACL token? This will log you out.
|
||||||
{{ else if (eq name 'use')}}
|
{{ else if (eq name 'use')}}
|
||||||
Are you sure you want to use this ACL token?
|
Are you sure you want to use this ACL token?
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</p>
|
</p>
|
||||||
<button type="button" class="type-delete" {{action execute}}>
|
<button type="button" class="type-delete" {{action execute}}>
|
||||||
{{#if (eq name 'delete')}}
|
{{#if (eq name 'delete')}}
|
||||||
Confirm Delete
|
Confirm Delete
|
||||||
{{else if (eq name 'logout')}}
|
{{else if (eq name 'logout')}}
|
||||||
Confirm Logout
|
Confirm Logout
|
||||||
{{ else if (eq name 'use')}}
|
{{ else if (eq name 'use')}}
|
||||||
Confirm Use
|
Confirm Use
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</button>
|
</button>
|
||||||
<button type="button" class="type-cancel" {{action cancel}}>Cancel</button>
|
<button type="button" class="type-cancel" {{action cancel}}>Cancel</button>
|
||||||
{{/block-slot}}
|
{{/block-slot}}
|
||||||
{{/confirmation-dialog}}
|
{{/confirmation-dialog}}
|
||||||
{{/block-slot}}
|
{{/block-slot}}
|
||||||
{{/tabular-collection}}
|
{{/tabular-collection}}
|
||||||
{{else}}
|
{{/block-slot}}
|
||||||
<p>
|
{{#block-slot 'empty'}}
|
||||||
There are no ACLs.
|
<p>
|
||||||
</p>
|
There are no ACLs.
|
||||||
{{/if}}
|
</p>
|
||||||
|
{{/block-slot}}
|
||||||
|
{{/changeable-set}}
|
||||||
{{/block-slot}}
|
{{/block-slot}}
|
||||||
{{/app-view}}
|
{{/app-view}}
|
|
@ -2,7 +2,7 @@ import { moduleFor, test } from 'ember-qunit';
|
||||||
|
|
||||||
moduleFor('controller:dc/acls/index', 'Unit | Controller | dc/acls/index', {
|
moduleFor('controller:dc/acls/index', 'Unit | Controller | dc/acls/index', {
|
||||||
// Specify the other units that are required for this test.
|
// Specify the other units that are required for this test.
|
||||||
// needs: ['controller:foo']
|
needs: ['service:search', 'service:dom'],
|
||||||
});
|
});
|
||||||
|
|
||||||
// Replace this with your real tests.
|
// Replace this with your real tests.
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
import getFilter from 'consul-ui/search/filters/acl';
|
||||||
|
import { module, test } from 'qunit';
|
||||||
|
|
||||||
|
module('Unit | Search | Filter | acl');
|
||||||
|
|
||||||
|
const filter = getFilter(cb => cb);
|
||||||
|
test('items are found by properties', function(assert) {
|
||||||
|
[
|
||||||
|
{
|
||||||
|
ID: 'HIT-id',
|
||||||
|
Name: 'name',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: 'id',
|
||||||
|
Name: 'name-HIT',
|
||||||
|
},
|
||||||
|
].forEach(function(item) {
|
||||||
|
const actual = filter(item, {
|
||||||
|
s: 'hit',
|
||||||
|
});
|
||||||
|
assert.ok(actual);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
test('items are not found', function(assert) {
|
||||||
|
[
|
||||||
|
{
|
||||||
|
ID: 'id',
|
||||||
|
Name: 'name',
|
||||||
|
},
|
||||||
|
].forEach(function(item) {
|
||||||
|
const actual = filter(item, {
|
||||||
|
s: 'hit',
|
||||||
|
});
|
||||||
|
assert.notOk(actual);
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in New Issue