UI: Glimmerize path-help and generated-item-adapter (#25767)

* glimmerize path help

* update path-help

* Glimmerize generated-item-adapter and usage to fix tests

* Address PR comments
This commit is contained in:
Chelsea Shaw
2024-03-08 11:06:31 -06:00
committed by GitHub
parent 86be20e5d9
commit dbca3a00fb
2 changed files with 46 additions and 47 deletions

View File

@@ -6,21 +6,22 @@
import ApplicationAdapter from './application';
import { task } from 'ember-concurrency';
import { service } from '@ember/service';
import { tracked } from '@glimmer/tracking';
export default ApplicationAdapter.extend({
store: service(),
namespace: 'v1',
urlForItem() {},
dynamicApiPath: '',
export default class GeneratedItemAdapter extends ApplicationAdapter {
@service store;
namespace = 'v1';
@tracked dynamicApiPath = '';
getDynamicApiPath: task(function* (id) {
// TODO: remove yield at some point.
@task
*getDynamicApiPath(id) {
const result = yield this.store.peekRecord('auth-method', id);
this.dynamicApiPath = result.apiPath;
return;
}),
}
fetchByQuery: task(function* (store, query, isList) {
@task
*fetchByQuery(store, query, isList) {
const { id } = query;
const data = {};
if (isList) {
@@ -35,13 +36,13 @@ export default ApplicationAdapter.extend({
};
return { ...resp, ...data };
});
}),
}
query(store, type, query) {
return this.fetchByQuery.perform(store, query, true);
},
}
queryRecord(store, type, query) {
return this.fetchByQuery.perform(store, query);
},
});
}
}

View File

@@ -9,18 +9,18 @@
has less (or no) information about.
*/
import Model from '@ember-data/model';
import Service from '@ember/service';
import Service, { service } from '@ember/service';
import { encodePath } from 'vault/utils/path-encoding-helpers';
import { getOwner } from '@ember/application';
import { expandOpenApiProps, combineAttributes } from 'vault/utils/openapi-to-attrs';
import fieldToAttrs from 'vault/utils/field-to-attrs';
import { resolve, reject } from 'rsvp';
import { debug } from '@ember/debug';
import { assert, debug } from '@ember/debug';
import { capitalize } from '@ember/string';
import { computed } from '@ember/object'; // eslint-disable-line
import { withModelValidations } from 'vault/decorators/model-validations';
import generatedItemAdapter from 'vault/adapters/generated-item-list';
import GeneratedItemAdapter from 'vault/adapters/generated-item-list';
import { sanitizePath } from 'core/utils/sanitize-path';
import {
filterPathsByItemType,
@@ -28,16 +28,16 @@ import {
reducePathsByPathName,
} from 'vault/utils/openapi-helpers';
export default Service.extend({
attrs: null,
dynamicApiPath: '',
export default class PathHelpService extends Service {
@service store;
ajax(url, options = {}) {
const appAdapter = getOwner(this).lookup(`adapter:application`);
const { data } = options;
return appAdapter.ajax(url, 'GET', {
data,
});
},
}
/**
* getNewModel instantiates models which use OpenAPI fully or partially
@@ -49,9 +49,7 @@ export default Service.extend({
*/
getNewModel(modelType, backend, apiPath, itemType) {
const owner = getOwner(this);
const modelName = `model:${modelType}`;
const modelFactory = owner.factoryFor(modelName);
const modelFactory = owner.factoryFor(`model:${modelType}`);
let newModel, helpUrl;
// if we have a factory, we need to take the existing model into account
if (modelFactory) {
@@ -63,7 +61,7 @@ export default Service.extend({
}
helpUrl = modelProto.getHelpUrl(backend);
return this.registerNewModelWithProps(helpUrl, backend, newModel, modelName);
return this.registerNewModelWithProps(helpUrl, backend, newModel, modelType);
} else {
debug(`Creating new Model for ${modelType}`);
newModel = Model.extend({});
@@ -73,7 +71,7 @@ export default Service.extend({
// and we don't need paths for them yet
if (!apiPath) {
helpUrl = newModel.proto().getHelpUrl(backend);
return this.registerNewModelWithProps(helpUrl, backend, newModel, modelName);
return this.registerNewModelWithProps(helpUrl, backend, newModel, modelType);
}
// use paths to dynamically create our openapi help url
@@ -100,13 +98,13 @@ export default Service.extend({
helpUrl = `/v1/${apiPath}${path.slice(1)}?help=true` || newModel.proto().getHelpUrl(backend);
pathInfo.paths = paths;
newModel = newModel.extend({ paths: pathInfo });
return this.registerNewModelWithProps(helpUrl, backend, newModel, modelName);
return this.registerNewModelWithProps(helpUrl, backend, newModel, modelType);
})
.catch((err) => {
// TODO: we should handle the error better here
console.error(err); // eslint-disable-line
});
},
}
/**
* getPaths is used to fetch all the openAPI paths available for an auth method,
@@ -135,7 +133,7 @@ export default Service.extend({
itemID,
});
});
},
}
// Makes a call to grab the OpenAPI document.
// Returns relevant information from OpenAPI
@@ -185,7 +183,7 @@ export default Service.extend({
const newProps = { ...paramProp, ...props };
return expandOpenApiProps(newProps);
});
},
}
getNewAdapter(pathInfo, itemType) {
// we need list and create paths to set the correct urls for actions
@@ -199,7 +197,7 @@ export default Service.extend({
const createPath = paths.find((path) => path.action === 'Create' || path.operations.includes('post'));
const deletePath = paths.find((path) => path.operations.includes('delete'));
return generatedItemAdapter.extend({
return class NewAdapter extends GeneratedItemAdapter {
urlForItem(id, isList, dynamicApiPath) {
const itemType = sanitizePath(getPath.path);
let url;
@@ -221,30 +219,30 @@ export default Service.extend({
}
return url;
},
}
urlForQueryRecord(id, modelName) {
return this.urlForItem(id, modelName);
},
}
urlForUpdateRecord(id) {
const itemType = createPath.path.slice(1, createPath.path.indexOf('{') - 1);
return `${this.buildURL()}/${apiPath}${itemType}/${id}`;
},
}
urlForCreateRecord(modelType, snapshot) {
const id = snapshot.record.mutableId; // computed property that returns either id or private settable _id value
const path = createPath.path.slice(1, createPath.path.indexOf('{') - 1);
return `${this.buildURL()}/${apiPath}${path}/${id}`;
},
}
urlForDeleteRecord(id) {
const path = deletePath.path.slice(1, deletePath.path.indexOf('{') - 1);
return `${this.buildURL()}/${apiPath}${path}/${id}`;
},
}
createRecord(store, type, snapshot) {
return this._super(...arguments).then((response) => {
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;
@@ -252,11 +250,12 @@ export default Service.extend({
}
return response;
});
},
});
},
}
};
}
registerNewModelWithProps(helpUrl, backend, newModel, modelName) {
assert('modelName should not include the type prefix', modelName.includes(':') === false);
return this.getProps(helpUrl, backend).then((props) => {
const { attrs, newFields } = combineAttributes(newModel.attributes, props);
const owner = getOwner(this);
@@ -282,9 +281,8 @@ export default Service.extend({
}
return obj;
}, {});
@withModelValidations(validations)
class GeneratedItemModel extends newModel {}
newModel = GeneratedItemModel;
newModel = withModelValidations(validations)(class GeneratedItemModel extends newModel {});
}
}
} catch (err) {
@@ -303,10 +301,10 @@ export default Service.extend({
}),
});
newModel.reopenClass({ merged: true });
owner.unregister(modelName);
owner.register(modelName, newModel);
owner.unregister(`model:${modelName}`);
owner.register(`model:${modelName}`, newModel);
});
},
}
getFieldGroups(newModel) {
const groups = {
default: [],
@@ -330,5 +328,5 @@ export default Service.extend({
fieldGroups.push({ [group]: groups[group] });
}
return fieldToAttrs(newModel, fieldGroups);
},
});
}
}