Skip to content

Commit

Permalink
UI: Refactor path-help service (#28444)
Browse files Browse the repository at this point in the history
* Add helper combineOpenApiAttrs + test

* hydrateModel working with upgradeModelSchema

* new registerNewModelWithAttrs method for generated models

* Add newFields to generated models

* copyright

* Glimmerize path-help service

* update generated-item-list adapter and path-help usage of it

* remove unused methods combineAttributes and combineFields

* move expandOpenApiProps to ts helper file

* fix auth test

* fix bug where adding user to second userpass mount saves to first mount

* Add mutableId

* fix ent test

* remove addressed deprecation

* Address PR comments

* [VAULT-31208] remove deprecation early-static from decorator tests
  • Loading branch information
hashishaw authored Sep 25, 2024
1 parent 2b031ad commit 8d6d26e
Show file tree
Hide file tree
Showing 14 changed files with 887 additions and 653 deletions.
126 changes: 97 additions & 29 deletions ui/app/adapters/generated-item-list.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,44 +4,112 @@
*/

import ApplicationAdapter from './application';
import { task } from 'ember-concurrency';
import { service } from '@ember/service';
import { sanitizePath } from 'core/utils/sanitize-path';
import { encodePath } from 'vault/utils/path-encoding-helpers';
import { tracked } from '@glimmer/tracking';

export default ApplicationAdapter.extend({
store: service(),
namespace: 'v1',
urlForItem() {},
dynamicApiPath: '',
export default class GeneratedItemListAdapter extends ApplicationAdapter {
@service store;
namespace = 'v1';

getDynamicApiPath: task(function* (id) {
// TODO: remove yield at some point.
const result = yield this.store.peekRecord('auth-method', id);
this.dynamicApiPath = result.apiPath;
return;
}),
// these items are set by calling getNewAdapter in the path-help service.
@tracked apiPath = '';
paths = {};
fetchByQuery: task(function* (store, query, isList) {
// These are the paths used for the adapter actions
get getPath() {
return this.paths.getPath || '';
}
get createPath() {
return this.paths.createPath || '';
}
get deletePath() {
return this.paths.deletePath || '';
}
getDynamicApiPath(id) {
const result = this.store.peekRecord('auth-method', id);
this.apiPath = result.apiPath;
return result.apiPath;
}
async fetchByQuery(store, query, isList) {
const { id } = query;
const data = {};
const payload = {};
if (isList) {
data.list = true;
yield this.getDynamicApiPath.perform(id);
payload.list = true;
}
const path = isList ? this.getDynamicApiPath(id) : '';

return this.ajax(this.urlForItem(id, isList, this.dynamicApiPath), 'GET', { data }).then((resp) => {
const data = {
id,
method: id,
};
return { ...resp, ...data };
});
}),
const resp = await this.ajax(this.urlForItem(id, isList, path), 'GET', { data: payload });
const data = {
id,
method: id,
};
return { ...resp, ...data };
}

query(store, type, query) {
return this.fetchByQuery.perform(store, query, true);
},
return this.fetchByQuery(store, query, true);
}

queryRecord(store, type, query) {
return this.fetchByQuery.perform(store, query);
},
});
return this.fetchByQuery(store, query);
}

urlForItem(id, isList, dynamicApiPath) {
const itemType = sanitizePath(this.getPath);
let url;
id = encodePath(id);
// the apiPath changes when you switch between routes but the apiPath variable does not unless the model is reloaded
// overwrite apiPath if dynamicApiPath exist.
// dynamicApiPath comes from the model->adapter
let apiPath = this.apiPath;
if (dynamicApiPath) {
apiPath = dynamicApiPath;
}
// isList indicates whether we are viewing the list page
// of a top-level item such as userpass
if (isList) {
url = `${this.buildURL()}/${apiPath}${itemType}/`;
} else {
// build the URL for the show page of a nested item
// such as a userpass group
url = `${this.buildURL()}/${apiPath}${itemType}/${id}`;
}

return url;
}

urlForQueryRecord(id, modelName) {
return this.urlForItem(id, modelName);
}

urlForUpdateRecord(id) {
const itemType = this.createPath.slice(1, this.createPath.indexOf('{') - 1);
return `${this.buildURL()}/${this.apiPath}${itemType}/${id}`;
}

urlForCreateRecord(modelType, snapshot) {
const id = snapshot.record.mutableId; // computed property that returns either id or private settable _id value
const path = this.createPath.slice(1, this.createPath.indexOf('{') - 1);
return `${this.buildURL()}/${this.apiPath}${path}/${id}`;
}

urlForDeleteRecord(id) {
const path = this.deletePath.slice(1, this.deletePath.indexOf('{') - 1);
return `${this.buildURL()}/${this.apiPath}${path}/${id}`;
}

createRecord(store, type, snapshot) {
return super.createRecord(...arguments).then((response) => {
// if the server does not return an id and one has not been set on the model we need to set it manually from the mutableId value
if (!response?.id && !snapshot.record.id) {
snapshot.record.id = snapshot.record.mutableId;
snapshot.id = snapshot.record.id;
}
return response;
});
}
}
44 changes: 44 additions & 0 deletions ui/app/models/generated-item.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/

import Model from '@ember-data/model';
import { tracked } from '@glimmer/tracking';

// This model is used for OpenApi-generated models in path-help service's getNewModel method
export default class GeneratedItemModel extends Model {
allFields = [];

@tracked _id;
get mutableId() {
return this._id || this.id;
}
set mutableId(value) {
this._id = value;
}

get fieldGroups() {
const groups = {
default: [],
};
const fieldGroups = [];
this.constructor.eachAttribute((name, attr) => {
// if the attr comes in with a fieldGroup from OpenAPI,
if (attr.options.fieldGroup) {
if (groups[attr.options.fieldGroup]) {
groups[attr.options.fieldGroup].push(attr);
} else {
groups[attr.options.fieldGroup] = [attr];
}
} else {
// otherwise just add that attr to the default group
groups.default.push(attr);
}
});
for (const group in groups) {
fieldGroups.push({ [group]: groups[group] });
}
return fieldGroups;
}
}
Loading

0 comments on commit 8d6d26e

Please sign in to comment.