Skip to content
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

Merged
merged 38 commits into from
Jun 21, 2019
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
f032067
add list endpoint tab links to auth section page
Apr 16, 2019
82d072e
add generic auth method list route and controller, create model in pa…
Apr 18, 2019
ae8bb90
successfully make generic list call and render template if items don'…
Apr 19, 2019
34e2c1d
add tabs menu to list page
Apr 19, 2019
4f68001
add show route and show properties for item
Apr 22, 2019
5e4a4af
restructure generic routes to include a parent item route that constr…
Apr 30, 2019
8603ab3
add create page (WIP) to generic pages
Apr 30, 2019
83e905d
add beginning of edit view
May 1, 2019
4bbf98a
refactor adapter to pull urls from path info, adapt pages to work wit…
May 1, 2019
97b0112
box off ldap, make sure path links are generated properly for configu…
May 2, 2019
36b7ff4
fix routing for models with factories
May 2, 2019
190640c
add comments, try to debug pki role weirdness
May 2, 2019
73f7865
Merge branch 'master' of github.com:hashicorp/vault into openapi-crud
May 2, 2019
b684338
commentssssss
May 2, 2019
8172468
add delete functionality for generated items
May 8, 2019
61589f6
Merge branch 'master' into openapi-crud
madalynrose May 8, 2019
79ecf47
adjust linting errors
May 8, 2019
676ee89
Merge branch 'openapi-crud' of github.com:hashicorp/vault into openap…
May 8, 2019
0682a89
Merge branch 'master' into openapi-crud
madalynrose May 9, 2019
51c1489
merge master
May 10, 2019
9cc9c1e
update pages to use new toolbar components'
May 10, 2019
5e50388
Merge branch 'openapi-crud' of github.com:hashicorp/vault into openap…
May 10, 2019
ce6ecdf
update Model fieldGroups to be build from openAPIif they aren't suppl…
May 22, 2019
9ee259f
use .compact instead of .filter, return paths promise
Jun 10, 2019
325559b
small fixes from PR comments
Jun 11, 2019
469dfde
use new displayAttributes when parsing openAPI document
Jun 11, 2019
5d9eb50
correctly parse incoming displayAtttrs
Jun 12, 2019
081dda0
use displayAttrs.navigation for nav links
Jun 17, 2019
7e28fa9
merge in master
Jun 17, 2019
417e4b7
fix icon stuff
Jun 18, 2019
177109a
update openapi util tests
Jun 18, 2019
f523cde
revert github path config to master so we can update on another branch
Jun 19, 2019
caf9e05
use fieldGroupShow component
Jun 19, 2019
c3f1aaa
remove old definition of deprecated
Jun 20, 2019
5fcb5bd
update fieldGroups for github to put things in correct order
Jun 20, 2019
b40623c
add debug calls back in (they disappeared somewhere)
Jun 20, 2019
56f0704
link ldap list item to the new access.method route
meirish Jun 20, 2019
2d7795f
remove duplicate delete button on item edit page
Jun 21, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions ui/app/adapters/generated-item-list.js
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);
},
});
52 changes: 52 additions & 0 deletions ui/app/components/generated-item.js
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.`);
});
},
},
});
3 changes: 1 addition & 2 deletions ui/app/components/section-tabs.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,12 @@ import Component from '@ember/component';

const SectionTabs = Component.extend({
tagName: '',

model: null,
tabType: 'authSettings',
});

SectionTabs.reopenClass({
positionalParams: ['model', 'tabType'],
positionalParams: ['model', 'tabType', 'paths'],
Copy link
Contributor

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.

});

export default SectionTabs;
4 changes: 4 additions & 0 deletions ui/app/controllers/vault/cluster/access/method/item/list.js
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, {});
19 changes: 15 additions & 4 deletions ui/app/helpers/tabs-for-auth-section.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { helper as buildHelper } from '@ember/component/helper';
import { pluralize } from 'ember-inflector';
import { capitalize } from '@ember/string';

const TABS_FOR_SETTINGS = {
aws: [
Expand Down Expand Up @@ -73,19 +75,28 @@ const TABS_FOR_SETTINGS = {

const TABS_FOR_SHOW = {};

export function tabsForAuthSection([methodType, sectionType = 'authSettings']) {
export function tabsForAuthSection([model, sectionType = 'authSettings', paths]) {
let tabs;

if (sectionType === 'authSettings') {
tabs = (TABS_FOR_SETTINGS[methodType] || []).slice();
tabs = (TABS_FOR_SETTINGS[model.type] || []).slice();
tabs.push({
label: 'Method Options',
routeParams: ['vault.cluster.settings.auth.configure.section', 'options'],
});
return tabs;
}

tabs = (TABS_FOR_SHOW[methodType] || []).slice();
if (paths) {
tabs = paths.map(path => {
let itemName = path.slice(1); //get rid of leading slash
return {
label: capitalize(pluralize(itemName)),
routeParams: ['vault.cluster.access.method.item.list', itemName],
};
});
} else {
tabs = (TABS_FOR_SHOW[model.type] || []).slice();
}
tabs.push({
label: 'Configuration',
routeParams: ['vault.cluster.access.method.section', 'configuration'],
Expand Down
6 changes: 6 additions & 0 deletions ui/app/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@ Router.map(function() {
this.route('methods', { path: '/' });
this.route('method', { path: '/:path' }, function() {
this.route('index', { path: '/' });
this.route('item', { path: '/item/:item_type' }, function() {
madalynrose marked this conversation as resolved.
Show resolved Hide resolved
this.route('list', { path: '/' });
this.route('create');
this.route('edit', { path: '/edit/:item_id' });
this.route('show', { path: '/:item_id' });
madalynrose marked this conversation as resolved.
Show resolved Hide resolved
});
this.route('section', { path: '/:section_name' });
});
this.route('leases', function() {
Expand Down
9 changes: 9 additions & 0 deletions ui/app/routes/vault/cluster/access/method.js
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
Copy link
Contributor

Choose a reason for hiding this comment

The 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);
Expand All @@ -12,6 +15,12 @@ export default Route.extend({
set(error, 'httpStatus', 404);
throw error;
}
if (model.type === 'ldap') {
return this.pathHelp.getPaths(model.apiPath, path).then(paths => {
model.set('paths', paths);
return model;
});
}
return model;
});
},
Expand Down
30 changes: 30 additions & 0 deletions ui/app/routes/vault/cluster/access/method/item.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { inject as service } from '@ember/service';
import Route from '@ember/routing/route';
import { getOwner } from '@ember/application';
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, getOwner(this), method, apiPath, itemType);
},

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');
Copy link
Contributor

Choose a reason for hiding this comment

The 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));
});
},
});
28 changes: 28 additions & 0 deletions ui/app/routes/vault/cluster/access/method/item/create.js
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);
},
});
25 changes: 25 additions & 0 deletions ui/app/routes/vault/cluster/access/method/item/edit.js
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);
},
});
45 changes: 45 additions & 0 deletions ui/app/routes/vault/cluster/access/method/item/list.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { inject as service } from '@ember/service';
import Route from '@ember/routing/route';
import { singularize } from 'ember-inflector';
import ListRoute from 'vault/mixins/list-route';

export default Route.extend(ListRoute, {
wizard: service(),
pathHelp: service('path-help'),
model() {
const { item_type: itemType } = this.paramsFor('vault.cluster.access.method.item');
const { page, pageFilter } = this.paramsFor(this.routeName);
const methodModel = this.modelFor('vault.cluster.access.method');
const { type } = methodModel;
let modelType = `generated-${singularize(itemType)}-${type}`;
const { path: method } = this.paramsFor('vault.cluster.access.method');

return this.store
.lazyPaginatedQuery(modelType, {
responsePath: 'data.keys',
page: page,
pageFilter: pageFilter,
type: itemType,
method: method,
})
.catch(err => {
if (err.httpStatus === 404) {
return [];
} else {
throw err;
}
});
},

setupController(controller) {
this._super(...arguments);
const { item_type: itemType } = this.paramsFor('vault.cluster.access.method.item');
let { path } = this.paramsFor('vault.cluster.access.method');
controller.set('itemType', singularize(itemType));
controller.set('method', path);
const { apiPath } = this.modelFor('vault.cluster.access.method');
this.pathHelp.getPaths(apiPath, path, itemType).then(paths => {
madalynrose marked this conversation as resolved.
Show resolved Hide resolved
controller.set('paths', Array.from(paths.list, pathInfo => pathInfo.path));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's the type of paths.list here?

});
},
});
24 changes: 24 additions & 0 deletions ui/app/routes/vault/cluster/access/method/item/show.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { singularize } from 'ember-inflector';
import { inject as service } from '@ember/service';
import Route from '@ember/routing/route';

export default Route.extend({
pathHelp: service('path-help'),
model() {
const { item_id: itemName } = this.paramsFor(this.routeName);
const { item_type: itemType } = this.paramsFor('vault.cluster.access.method.item');
const { path: method } = this.paramsFor('vault.cluster.access.method');
const methodModel = this.modelFor('vault.cluster.access.method');
const { type } = methodModel;
const modelType = `generated-${singularize(itemType)}-${type}`;
return this.store.findRecord(modelType, itemName, {
adapterOptions: { path: `${method}/${itemType}` },
});
},

setupController(controller) {
this._super(...arguments);
const { item_type: itemType } = this.paramsFor('vault.cluster.access.method.item');
controller.set('itemType', singularize(itemType));
},
});
5 changes: 5 additions & 0 deletions ui/app/routes/vault/cluster/access/method/section.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,10 @@ export default Route.extend({
const { section_name: section } = this.paramsFor(this.routeName);
this._super(...arguments);
controller.set('section', section);
let method = this.modelFor('vault.cluster.access.method');
if (method.type === 'ldap') {
let paths = method.paths.list.map(pathInfo => pathInfo.path);
controller.set('paths', paths);
}
},
});
4 changes: 2 additions & 2 deletions ui/app/routes/vault/cluster/settings/auth/configure/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import { tabsForAuthSection } from 'vault/helpers/tabs-for-auth-section';

export default Route.extend({
beforeModel() {
const type = this.modelFor('vault.cluster.settings.auth.configure').get('type');
const section = get(tabsForAuthSection([type]), 'firstObject.routeParams.lastObject');
const model = this.modelFor('vault.cluster.settings.auth.configure');
const section = get(tabsForAuthSection([model]), 'firstObject.routeParams.lastObject');
return this.transitionTo('vault.cluster.settings.auth.configure.section', section);
},
});
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export default Route.extend(UnloadModelRoute, {
const backend = this.modelFor('vault.cluster.settings.auth.configure');
const modelType = this.modelType(backend.type, section_name);
let owner = getOwner(this);
return this.pathHelp.getNewModel(modelType, owner, method);
return this.pathHelp.getNewModel(modelType, owner, method, backend.apiPath);
},

model(params) {
Expand Down
Loading