UI: Move legacy ACLs to use the new searchables/changeable-sets (#4933)

pull/5729/head
John Cowen 2018-11-19 14:49:16 +00:00 committed by John Cowen
parent c532e53d9a
commit 104e6bac71
7 changed files with 150 additions and 93 deletions

View File

@ -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) {

View File

@ -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),

View File

@ -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
);
});
}

View File

@ -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>}}

View File

@ -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}}

View File

@ -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.

View File

@ -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);
});
});