-
Notifications
You must be signed in to change notification settings - Fork 4.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adds support for ACL Roles and Service Identities CRUD, along with necessary changes to Tokens, and the CSS improvements required. Also includes refinements/improvements for easier testing of deeply nested components. 1. ember-data adapter/serializer/model triplet for Roles 2. repository, form/validations and searching filter for Roles 3. Moves potentially, repeated, or soon to to repeated functionality into a mixin (mainly for 'many policy' relationships) 4. A few styling tweaks for little edge cases around roles 5. Router additions, Route, Controller and templates for Roles Also see: * UI: ACL Roles cont. plus Service Identities (#5661 and #5720)
- Loading branch information
Showing
208 changed files
with
4,141 additions
and
944 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
import Adapter, { | ||
REQUEST_CREATE, | ||
REQUEST_UPDATE, | ||
DATACENTER_QUERY_PARAM as API_DATACENTER_KEY, | ||
} from './application'; | ||
|
||
import { PRIMARY_KEY, SLUG_KEY } from 'consul-ui/models/role'; | ||
import { FOREIGN_KEY as DATACENTER_KEY } from 'consul-ui/models/dc'; | ||
import { OK as HTTP_OK } from 'consul-ui/utils/http/status'; | ||
import { PUT as HTTP_PUT } from 'consul-ui/utils/http/method'; | ||
|
||
import WithPolicies from 'consul-ui/mixins/policy/as-many'; | ||
|
||
export default Adapter.extend(WithPolicies, { | ||
urlForQuery: function(query, modelName) { | ||
return this.appendURL('acl/roles', [], this.cleanQuery(query)); | ||
}, | ||
urlForQueryRecord: function(query, modelName) { | ||
if (typeof query.id === 'undefined') { | ||
throw new Error('You must specify an id'); | ||
} | ||
return this.appendURL('acl/role', [query.id], this.cleanQuery(query)); | ||
}, | ||
urlForCreateRecord: function(modelName, snapshot) { | ||
return this.appendURL('acl/role', [], { | ||
[API_DATACENTER_KEY]: snapshot.attr(DATACENTER_KEY), | ||
}); | ||
}, | ||
urlForUpdateRecord: function(id, modelName, snapshot) { | ||
return this.appendURL('acl/role', [snapshot.attr(SLUG_KEY)], { | ||
[API_DATACENTER_KEY]: snapshot.attr(DATACENTER_KEY), | ||
}); | ||
}, | ||
urlForDeleteRecord: function(id, modelName, snapshot) { | ||
return this.appendURL('acl/role', [snapshot.attr(SLUG_KEY)], { | ||
[API_DATACENTER_KEY]: snapshot.attr(DATACENTER_KEY), | ||
}); | ||
}, | ||
handleResponse: function(status, headers, payload, requestData) { | ||
let response = payload; | ||
if (status === HTTP_OK) { | ||
const url = this.parseURL(requestData.url); | ||
switch (true) { | ||
case response === true: | ||
response = this.handleBooleanResponse(url, response, PRIMARY_KEY, SLUG_KEY); | ||
break; | ||
case Array.isArray(response): | ||
response = this.handleBatchResponse(url, response, PRIMARY_KEY, SLUG_KEY); | ||
break; | ||
default: | ||
response = this.handleSingleResponse(url, response, PRIMARY_KEY, SLUG_KEY); | ||
} | ||
} | ||
return this._super(status, headers, response, requestData); | ||
}, | ||
methodForRequest: function(params) { | ||
switch (params.requestType) { | ||
case REQUEST_CREATE: | ||
return HTTP_PUT; | ||
} | ||
return this._super(...arguments); | ||
}, | ||
dataForRequest: function(params) { | ||
const data = this._super(...arguments); | ||
switch (params.requestType) { | ||
case REQUEST_UPDATE: | ||
case REQUEST_CREATE: | ||
return data.role; | ||
} | ||
return data; | ||
}, | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
import Component from '@ember/component'; | ||
import { get, set, computed } from '@ember/object'; | ||
import { alias } from '@ember/object/computed'; | ||
import { inject as service } from '@ember/service'; | ||
import { Promise } from 'rsvp'; | ||
|
||
import SlotsMixin from 'block-slots'; | ||
import WithListeners from 'consul-ui/mixins/with-listeners'; | ||
|
||
export default Component.extend(SlotsMixin, WithListeners, { | ||
onchange: function() {}, | ||
|
||
error: function() {}, | ||
type: '', | ||
|
||
dom: service('dom'), | ||
container: service('search'), | ||
formContainer: service('form'), | ||
|
||
item: alias('form.data'), | ||
|
||
selectedOptions: alias('items'), | ||
|
||
init: function() { | ||
this._super(...arguments); | ||
this.searchable = get(this, 'container').searchable(get(this, 'type')); | ||
this.form = get(this, 'formContainer').form(get(this, 'type')); | ||
this.form.clear({ Datacenter: get(this, 'dc') }); | ||
}, | ||
options: computed('selectedOptions.[]', 'allOptions.[]', function() { | ||
// It's not massively important here that we are defaulting `items` and | ||
// losing reference as its just to figure out the diff | ||
let options = get(this, 'allOptions') || []; | ||
const items = get(this, 'selectedOptions') || []; | ||
if (get(items, 'length') > 0) { | ||
// find a proper ember-data diff | ||
options = options.filter(item => !items.findBy('ID', get(item, 'ID'))); | ||
this.searchable.add(options); | ||
} | ||
return options; | ||
}), | ||
actions: { | ||
search: function(term) { | ||
// TODO: make sure we can either search before things are loaded | ||
// or wait until we are loaded, guess power select take care of that | ||
return new Promise(resolve => { | ||
const remove = this.listen(this.searchable, 'change', function(e) { | ||
remove(); | ||
resolve(e.target.data); | ||
}); | ||
this.searchable.search(term); | ||
}); | ||
}, | ||
reset: function() { | ||
get(this, 'form').clear({ Datacenter: get(this, 'dc') }); | ||
}, | ||
open: function() { | ||
if (!get(this, 'allOptions.closed')) { | ||
set(this, 'allOptions', get(this, 'repo').findAllByDatacenter(get(this, 'dc'))); | ||
} | ||
}, | ||
save: function(item, items, success = function() {}) { | ||
// Specifically this saves an 'new' option/child | ||
// and then adds it to the selectedOptions, not options | ||
const repo = get(this, 'repo'); | ||
set(item, 'CreateTime', new Date().getTime()); | ||
// TODO: temporary async | ||
// this should be `set(this, 'item', repo.persist(item));` | ||
// need to be sure that its saved before adding/closing the modal for now | ||
// and we don't open the modal on prop change yet | ||
item = repo.persist(item); | ||
this.listen(item, 'message', e => { | ||
this.actions.change.bind(this)( | ||
{ | ||
target: { | ||
name: 'items[]', | ||
value: items, | ||
}, | ||
}, | ||
items, | ||
e.data | ||
); | ||
success(); | ||
}); | ||
this.listen(item, 'error', this.error.bind(this)); | ||
}, | ||
remove: function(item, items) { | ||
const prop = get(this, 'repo').getSlugKey(); | ||
const value = get(item, prop); | ||
const pos = items.findIndex(function(item) { | ||
return get(item, prop) === value; | ||
}); | ||
if (pos !== -1) { | ||
return items.removeAt(pos, 1); | ||
} | ||
this.onchange({ target: this }); | ||
}, | ||
change: function(e, value, item) { | ||
const event = get(this, 'dom').normalizeEvent(...arguments); | ||
const items = value; | ||
switch (event.target.name) { | ||
case 'items[]': | ||
set(item, 'CreateTime', new Date().getTime()); | ||
// this always happens synchronously | ||
items.pushObject(item); | ||
// TODO: Fire a proper event | ||
this.onchange({ target: this }); | ||
break; | ||
default: | ||
} | ||
}, | ||
}, | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import Component from '@ember/component'; | ||
import SlotsMixin from 'block-slots'; | ||
import { inject as service } from '@ember/service'; | ||
import { get } from '@ember/object'; | ||
import { alias } from '@ember/object/computed'; | ||
import WithListeners from 'consul-ui/mixins/with-listeners'; | ||
// match anything that isn't a [ or ] into multiple groups | ||
const propRe = /([^[\]])+/g; | ||
export default Component.extend(WithListeners, SlotsMixin, { | ||
onreset: function() {}, | ||
onchange: function() {}, | ||
onerror: function() {}, | ||
onsuccess: function() {}, | ||
|
||
data: alias('form.data'), | ||
item: alias('form.data'), | ||
// TODO: Could probably alias item | ||
// or just use data/value instead | ||
|
||
dom: service('dom'), | ||
container: service('form'), | ||
|
||
actions: { | ||
change: function(e, value, item) { | ||
let event = get(this, 'dom').normalizeEvent(e, value); | ||
const matches = [...event.target.name.matchAll(propRe)]; | ||
const prop = matches[matches.length - 1][0]; | ||
event = get(this, 'dom').normalizeEvent( | ||
`${get(this, 'type')}[${prop}]`, | ||
event.target.value, | ||
event.target | ||
); | ||
const form = get(this, 'form'); | ||
try { | ||
form.handleEvent(event); | ||
this.onchange({ target: this }); | ||
} catch (err) { | ||
throw err; | ||
} | ||
}, | ||
}, | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.