open-vault/ui/app/decorators/model-expanded-attributes.js
2023-03-27 13:58:21 -05:00

86 lines
3.2 KiB
JavaScript

/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: MPL-2.0
*/
import { expandAttributeMeta } from 'vault/utils/field-to-attrs';
import Model from '@ember-data/model';
import { assert } from '@ember/debug';
/**
* sets allByKey properties on model class. These are all the attributes on the model
* and any belongsTo models, expanded with attribute metadata. The value returned is an
* object where the key is the attribute name, and the value is the expanded attribute
* metadata.
* This decorator also exposes a helper function `_expandGroups` which, when given groups
* as expected in field-to-attrs util, will return a similar object with the expanded
* attributes in place of the strings in the array.
*/
export function withExpandedAttributes() {
return function decorator(SuperClass) {
if (!Object.prototype.isPrototypeOf.call(Model, SuperClass)) {
// eslint-disable-next-line
console.error(
'withExpandedAttributes decorator must be used on instance of ember-data Model class. Decorator not applied to returned class'
);
return SuperClass;
}
return class ModelExpandedAttrs extends SuperClass {
// Helper method for expanding dynamic groups on model
_expandGroups(groups) {
if (!Array.isArray(groups)) {
throw new Error('_expandGroups expects an array of objects');
}
/* Expects group shape to be something like:
[
{ default: ['ttl', 'maxTtl'] },
{ "Method Options": ['other', 'fieldNames'] },
]*/
return groups.map((obj) => {
const [key, stringArray] = Object.entries(obj)[0];
const expanded = stringArray.map((fieldName) => this.allByKey[fieldName]).filter((f) => !!f);
assert(`all fields found in allByKey for group ${key}`, expanded.length === stringArray.length);
return { [key]: expanded };
});
}
_allByKey = null;
get allByKey() {
// Caching like this ensures allByKey only gets calculated once
if (!this._allByKey) {
const byKey = {};
// First, get attr names which are on the model directly
// By this time, OpenAPI should have populated non-explicit attrs
const mainFields = [];
this.eachAttribute(function (key) {
mainFields.push(key);
});
const expanded = expandAttributeMeta(this, mainFields);
expanded.forEach((attr) => {
// Add expanded attributes from the model
byKey[attr.name] = attr;
});
// Next, fetch and expand attrs for related models
this.eachRelationship(function (name, descriptor) {
// We don't worry about getting hasMany relationships
if (descriptor.kind !== 'belongsTo') return;
const rModel = this[name];
const rAttrNames = [];
rModel.eachAttribute(function (key) {
rAttrNames.push(key);
});
const expanded = expandAttributeMeta(rModel, rAttrNames);
expanded.forEach((attr) => {
byKey[`${name}.${attr.name}`] = attr;
});
}, this);
this._allByKey = byKey;
}
return this._allByKey;
}
};
};
}