-
Notifications
You must be signed in to change notification settings - Fork 4.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
OpenAPI CRUD views #6702
OpenAPI CRUD views #6702
Changes from 37 commits
f032067
82d072e
ae8bb90
34e2c1d
4f68001
5e4a4af
8603ab3
83e905d
4bbf98a
97b0112
36b7ff4
190640c
73f7865
b684338
8172468
61589f6
79ecf47
676ee89
0682a89
51c1489
9cc9c1e
5e50388
ce6ecdf
9ee259f
325559b
469dfde
5d9eb50
081dda0
7e28fa9
417e4b7
177109a
f523cde
caf9e05
c3f1aaa
5fcb5bd
b40623c
56f0704
2d7795f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
import { assign } from '@ember/polyfills'; | ||
import ApplicationAdapter from './application'; | ||
|
||
export default ApplicationAdapter.extend({ | ||
namespace: 'v1', | ||
urlForItem() {}, | ||
optionsForQuery(id) { | ||
let data = {}; | ||
if (!id) { | ||
data['list'] = true; | ||
} | ||
return { data }; | ||
}, | ||
|
||
fetchByQuery(store, query) { | ||
const { id, method, type } = query; | ||
return this.ajax(this.urlForItem(method, id, type), 'GET', this.optionsForQuery(id)).then(resp => { | ||
const data = { | ||
id, | ||
name: id, | ||
method, | ||
}; | ||
|
||
return assign({}, resp, data); | ||
}); | ||
}, | ||
|
||
query(store, type, query) { | ||
return this.fetchByQuery(store, query); | ||
}, | ||
|
||
queryRecord(store, type, query) { | ||
return this.fetchByQuery(store, query); | ||
}, | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
/** | ||
* @module FieldGroupShow | ||
* FieldGroupShow components loop through a Model's fieldGroups | ||
* to display their attributes | ||
* | ||
* @example | ||
* ```js | ||
* <FieldGroupShow @model={{model}} @showAllFields=true /> | ||
* ``` | ||
* | ||
* @param model {Object} - the model | ||
* @param [showAllFields=false] {boolean} - whether to show fields with empty values | ||
*/ | ||
import Component from '@ember/component'; | ||
import layout from '../templates/components/field-group-show'; | ||
|
||
export default Component.extend({ | ||
layout, | ||
model: null, | ||
showAllFields: false, | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
import { inject as service } from '@ember/service'; | ||
import Component from '@ember/component'; | ||
import { computed } from '@ember/object'; | ||
import { task } from 'ember-concurrency'; | ||
import DS from 'ember-data'; | ||
|
||
/** | ||
* @module GeneratedItem | ||
* The `GeneratedItem` is the form to configure generated items related to mounts (e.g. groups, roles, users) | ||
* | ||
* @example | ||
* ```js | ||
* <GeneratedItem @model={{model}} @mode={{mode}} @itemType={{itemType/> | ||
* ``` | ||
* | ||
* @property model=null {DS.Model} - The corresponding item model that is being configured. | ||
* @property mode {String} - which config mode to use. either `show`, `edit`, or `create` | ||
* @property itemType {String} - the type of item displayed | ||
* | ||
*/ | ||
|
||
export default Component.extend({ | ||
model: null, | ||
itemType: null, | ||
flashMessages: service(), | ||
router: service(), | ||
props: computed(function() { | ||
return this.model.serialize(); | ||
}), | ||
saveModel: task(function*() { | ||
try { | ||
yield this.model.save(); | ||
} catch (err) { | ||
// AdapterErrors are handled by the error-message component | ||
// in the form | ||
if (err instanceof DS.AdapterError === false) { | ||
throw err; | ||
} | ||
return; | ||
} | ||
this.router.transitionTo('vault.cluster.access.method.item.list').followRedirects(); | ||
this.flashMessages.success(`The ${this.itemType} configuration was saved successfully.`); | ||
}).withTestWaiter(), | ||
actions: { | ||
deleteItem() { | ||
this.model.destroyRecord().then(() => { | ||
this.router.transitionTo('vault.cluster.access.method.item.list').followRedirects(); | ||
this.flashMessages.success(`${this.model.id} ${this.itemType} was deleted successfully.`); | ||
}); | ||
}, | ||
}, | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
import Controller from '@ember/controller'; | ||
import ListController from 'vault/mixins/list-controller'; | ||
|
||
export default Controller.extend(ListController, {}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,50 +1,11 @@ | ||
import { computed } from '@ember/object'; | ||
import DS from 'ember-data'; | ||
|
||
import AuthConfig from '../auth-config'; | ||
import fieldToAttrs from 'vault/utils/field-to-attrs'; | ||
import { combineFieldGroups } from 'vault/utils/openapi-to-attrs'; | ||
|
||
const { attr } = DS; | ||
|
||
export default AuthConfig.extend({ | ||
useOpenAPI: true, | ||
binddn: attr('string', { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. notice here that when we set |
||
helpText: 'Used when performing user search. Example: cn=vault,ou=Users,dc=example,dc=com', | ||
}), | ||
bindpass: attr('string', { | ||
helpText: 'Used along with binddn when performing user search', | ||
sensitive: true, | ||
}), | ||
userdn: attr('string', { | ||
helpText: 'Base DN under which to perform user search. Example: ou=Users,dc=example,dc=com', | ||
}), | ||
userattr: attr('string', { | ||
helpText: | ||
'Attribute on user attribute object matching the username passed when authenticating. Examples: sAMAccountName, cn, uid', | ||
}), | ||
upndomain: attr('string', { | ||
helpText: | ||
'The userPrincipalDomain used to construct the UPN string for the authenticating user. The constructed UPN will appear as [username]@UPNDomain. Example: example.com, which will cause vault to bind as [email protected].', | ||
}), | ||
|
||
groupfilter: attr('string', { | ||
helpText: | ||
'Go template used when constructing the group membership query. The template can access the following context variables: [UserDN, Username]. The default is (|(memberUid={{.Username}})(member={{.UserDN}})(uniqueMember={{.UserDN}})), which is compatible with several common directory schemas. To support nested group resolution for Active Directory, instead use the following query: (&(objectClass=group)(member:1.2.840.113556.1.4.1941:={{.UserDN}}))', | ||
}), | ||
groupdn: attr('string', { | ||
helpText: | ||
'LDAP search base for group membership search. This can be the root containing either groups or users. Example: ou=Groups,dc=example,dc=com', | ||
}), | ||
groupattr: attr('string', { | ||
helpText: | ||
'LDAP attribute to follow on objects returned by groupfilter in order to enumerate user group membership. Examples: for groupfilter queries returning group objects, use: cn. For queries returning user objects, use: memberOf. The default is cn.', | ||
}), | ||
useTokenGroups: attr('boolean', { | ||
helpText: | ||
'Use the Active Directory tokenGroups constructed attribute to find the group memberships. This returns all security groups for the user, including nested groups. In an Active Directory environment with a large number of groups this method offers increased performance. Selecting this will cause Group DN, Attribute, and Filter to be ignored.', | ||
}), | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. uau 👏 |
||
fieldGroups: computed(function() { | ||
let groups = [ | ||
{ | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,12 @@ | ||
import { set } from '@ember/object'; | ||
import Route from '@ember/routing/route'; | ||
import DS from 'ember-data'; | ||
import { inject as service } from '@ember/service'; | ||
|
||
export default Route.extend({ | ||
pathHelp: service('path-help'), | ||
model(params) { | ||
// eslint-disable-line rule | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this can be removed. |
||
const { path } = params; | ||
return this.store.findAll('auth-method').then(modelArray => { | ||
const model = modelArray.findBy('id', path); | ||
|
@@ -12,7 +15,10 @@ export default Route.extend({ | |
set(error, 'httpStatus', 404); | ||
throw error; | ||
} | ||
return model; | ||
return this.pathHelp.getPaths(model.apiPath, path).then(paths => { | ||
model.set('paths', paths); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (for later) we should document what attributes OpenAPI models have on them - |
||
return model; | ||
}); | ||
}); | ||
}, | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import { inject as service } from '@ember/service'; | ||
import Route from '@ember/routing/route'; | ||
import { singularize } from 'ember-inflector'; | ||
|
||
export default Route.extend({ | ||
wizard: service(), | ||
pathHelp: service('path-help'), | ||
|
||
beforeModel() { | ||
const { item_type: itemType } = this.paramsFor(this.routeName); | ||
const { path: method } = this.paramsFor('vault.cluster.access.method'); | ||
let methodModel = this.modelFor('vault.cluster.access.method'); | ||
let { apiPath, type } = methodModel; | ||
let modelType = `generated-${singularize(itemType)}-${type}`; | ||
madalynrose marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return this.pathHelp.getNewModel(modelType, method, apiPath, itemType); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is an example of how we call OpenAPI and generate a model on the auth method items pages. |
||
}, | ||
|
||
setupController(controller) { | ||
this._super(...arguments); | ||
const { item_type: itemType } = this.paramsFor(this.routeName); | ||
const { path } = this.paramsFor('vault.cluster.access.method'); | ||
const { apiPath } = this.modelFor('vault.cluster.access.method'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We could wrap these into a helper method to use in both places. |
||
controller.set('itemType', itemType); | ||
controller.set('method', path); | ||
this.pathHelp.getPaths(apiPath, path, itemType).then(paths => { | ||
controller.set('paths', Array.from(paths.list, pathInfo => pathInfo.path)); | ||
}); | ||
}, | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import Route from '@ember/routing/route'; | ||
import UnloadModelRoute from 'vault/mixins/unload-model-route'; | ||
import UnsavedModelRoute from 'vault/mixins/unsaved-model-route'; | ||
import { singularize } from 'ember-inflector'; | ||
|
||
export default Route.extend(UnloadModelRoute, UnsavedModelRoute, { | ||
model() { | ||
const { item_type: itemType } = this.paramsFor('vault.cluster.access.method.item'); | ||
const methodModel = this.modelFor('vault.cluster.access.method'); | ||
const { type } = methodModel; | ||
const { path: method } = this.paramsFor('vault.cluster.access.method'); | ||
const modelType = `generated-${singularize(itemType)}-${type}`; | ||
return this.store.createRecord(modelType, { | ||
itemType, | ||
method, | ||
adapterOptions: { path: `${method}/${itemType}` }, | ||
}); | ||
}, | ||
|
||
setupController(controller) { | ||
this._super(...arguments); | ||
const { item_type: itemType } = this.paramsFor('vault.cluster.access.method.item'); | ||
const { path: method } = this.paramsFor('vault.cluster.access.method'); | ||
controller.set('itemType', singularize(itemType)); | ||
controller.set('mode', 'create'); | ||
controller.set('method', method); | ||
}, | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import Route from '@ember/routing/route'; | ||
import UnloadModelRoute from 'vault/mixins/unload-model-route'; | ||
import UnsavedModelRoute from 'vault/mixins/unsaved-model-route'; | ||
import { singularize } from 'ember-inflector'; | ||
|
||
export default Route.extend(UnloadModelRoute, UnsavedModelRoute, { | ||
model(params) { | ||
const methodModel = this.modelFor('vault.cluster.access.method'); | ||
const { type } = methodModel; | ||
const { item_type: itemType } = this.paramsFor('vault.cluster.access.method.item'); | ||
let modelType = `generated-${singularize(itemType)}-${type}`; | ||
return this.store.findRecord(modelType, params.item_id); | ||
}, | ||
|
||
setupController(controller) { | ||
this._super(...arguments); | ||
const { item_type: itemType } = this.paramsFor('vault.cluster.access.method.item'); | ||
const { path: method } = this.paramsFor('vault.cluster.access.method'); | ||
const { item_id: itemName } = this.paramsFor(this.routeName); | ||
controller.set('itemType', singularize(itemType)); | ||
controller.set('mode', 'edit'); | ||
controller.set('method', method); | ||
controller.set('itemName', itemName); | ||
}, | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a note here that positionParams are going away in Glimmer components, so we'll eventually want to move away from this.