@adaptabletools/adaptable
Version:
Powerful data-agnostic HTML5 AG Grid extension which provides advanced, cutting-edge functionality to meet all DataGrid requirements
169 lines (168 loc) • 8.67 kB
JavaScript
import { ADAPTABLE_METAMODEL } from '../../metamodel/adaptable.metamodel';
import { AdaptableOptionsDocsLink } from '../Constants/DocumentationLinkConstants';
import StringExtensions from '../Extensions/StringExtensions';
import { AdaptableLogger } from '../../agGrid/AdaptableLogger';
import { getDefaultAdaptableOptions } from '../../AdaptableOptions/DefaultAdaptableOptions';
const supportedMetamodelTypes = ['s', 'n', 'b', 'a', 'f', 'R'];
export class MetamodelService {
constructor(getAdaptableOptions, validateOptions) {
this.gridInfoOptions = new Map();
this.getAdaptableOptions = () => null;
this.getAdaptableOptions = getAdaptableOptions;
this.gridInfoOptions = this.buildGridInfoOptions();
if (validateOptions) {
this.validateAdaptableOptionsValues();
}
}
getGridInfoOptions() {
return this.gridInfoOptions;
}
getGridInfoNoCodeOptions() {
return this.buildGridInfoOptions({
filterItemProperty: (itemProperty) => {
return itemProperty.noCode === 'item';
},
filterEmptyAdaptableOptions: false,
});
}
validateAdaptableOptionsValues() {
const adaptableOptionsValues = this.getAdaptableOptions();
const adaptableOptionsMetamodel = this.getAdaptableOptionsMetamodel();
const adaptableOptionsDefaultValues = this.getAdaptableOptionsDefaultValues();
if (adaptableOptionsValues.adaptableId?.includes('.')) {
AdaptableLogger.consoleWarnBase("The 'AdaptableOptions.adaptableId' property should NOT include a '.' character. We strongly recommend to remove it to avoid unexpected AdapTable behavior.");
}
const validationErrors = [];
this.validateOptionsObject(validationErrors, 'AdaptableOptions', adaptableOptionsValues, adaptableOptionsMetamodel, adaptableOptionsDefaultValues);
if (validationErrors.length) {
AdaptableLogger.consoleWarnBase([
'AdaptableOptions validation errors:',
'\n',
...validationErrors,
'\n',
`See AdaptableOptions documentation at ${AdaptableOptionsDocsLink} for details`,
].join('\n'));
}
}
validateOptionsObject(validationErrors, optionsObjectName, optionsObject, optionsObjectMetamodel, optionsObjectDefaultValues) {
Object.entries(optionsObject).forEach(([optionKey, optionValue]) => {
// ignore fdc3options
if (optionKey === 'fdc3Options') {
return;
}
const optionMetamodel = optionsObjectMetamodel?.props?.find((metamodelProperty) => metamodelProperty.name === optionKey);
// Return if property not found - log if not 'Revision' or 'Uuid' or 'AdaptableVersion'
if (!optionMetamodel) {
if (optionKey === 'Revision' || optionKey === 'Uuid' || optionKey === 'AdaptableVersion') {
return;
}
else {
validationErrors.push(`${optionsObjectName}.${optionKey} (value=${optionValue}) :: unknown/unsupported property, will be ignored`);
return;
}
}
// let's try to validate the type of the provided value
const expectedOptionsValueType = this.getExpectedOptionsValueType(optionMetamodel);
const providedOptionsValueType = Array.isArray(optionValue) ? 'a' : typeof optionValue;
if (!expectedOptionsValueType) {
return;
}
// if it's a REFERENCE, we try to go (recursively) deeper
if (expectedOptionsValueType === 'R') {
const referenceObjectName = optionMetamodel.ref;
const referenceObject = optionsObject[optionKey];
const referenceObjectMetamodel = this.getAdaptableMetamodel()[referenceObjectName];
if (referenceObject && referenceObjectMetamodel?.kind === 'I') {
this.validateOptionsObject(validationErrors, referenceObjectName, referenceObject, referenceObjectMetamodel, optionsObjectDefaultValues?.[optionKey]);
}
}
else if (supportedMetamodelTypes.includes(providedOptionsValueType) &&
providedOptionsValueType !== expectedOptionsValueType) {
validationErrors.push(`${optionsObjectName}.${optionKey} (value=${optionValue}) :: wrong type (${providedOptionsValueType}), expected ${expectedOptionsValueType}`);
}
});
}
getExpectedOptionsValueType(metamodelProperty) {
const metamodelPropertyKind = metamodelProperty.kind;
if (supportedMetamodelTypes.includes(metamodelPropertyKind)) {
return metamodelPropertyKind;
}
}
buildGridInfoOptions(options) {
const { filterItemProperty, filterEmptyAdaptableOptions = true } = options || {};
const gridInfoOptions = new Map();
const adaptableMetamodel = this.getAdaptableMetamodel();
const adaptableOptionsMetamodel = this.getAdaptableOptionsMetamodel();
const adaptableOptionsValues = this.getAdaptableOptions();
const adaptableOptionsDefaultValues = this.getAdaptableOptionsDefaultValues();
if (!adaptableOptionsMetamodel) {
// should never happen
return gridInfoOptions;
}
// root-level properties are grouped in a synthetic 'Base Options' container
const baseOptionsItems = this.mapGridInfoContainerItems(adaptableOptionsMetamodel, adaptableOptionsValues, adaptableOptionsDefaultValues, filterItemProperty);
gridInfoOptions.set('baseOptions', {
containerLabel: 'Base Options',
items: baseOptionsItems,
});
// map containers
adaptableOptionsMetamodel.props
.filter((optionItem) => optionItem.gridInfo === 'container')
.forEach((containerOptionItem) => {
const containerMetamodelName = containerOptionItem.ref;
const adaptableOptionsName = containerOptionItem.name;
const containerOptionsMetamodel = adaptableMetamodel[containerMetamodelName];
// @ts-ignore
const containerOptionsValues = adaptableOptionsValues[adaptableOptionsName];
// @ts-ignore
const containerOptionsDefaultValues = adaptableOptionsDefaultValues[adaptableOptionsName];
if (!containerOptionsMetamodel ||
(!containerOptionsValues && filterEmptyAdaptableOptions) ||
!containerOptionsDefaultValues) {
// should never happen
return;
}
const optionItems = this.mapGridInfoContainerItems(containerOptionsMetamodel, containerOptionsValues, containerOptionsDefaultValues, filterItemProperty);
gridInfoOptions.set(containerOptionItem.name, {
containerLabel: this.extractUiLabel(containerOptionItem),
items: optionItems,
});
});
return gridInfoOptions;
}
mapGridInfoContainerItems(optionItemContainer, adaptableOptionsValues, defaultAdaptableOptionsValues, filter = (itemProperty) => itemProperty.gridInfo === 'item') {
return optionItemContainer.props.filter(filter).map((itemProperty) => {
return {
name: itemProperty.name,
value: adaptableOptionsValues?.[itemProperty.name],
defaultValue: defaultAdaptableOptionsValues[itemProperty.name],
kind: itemProperty.kind,
description: StringExtensions.UnescapeHtmlEntities(itemProperty.desc),
uiLabel: this.extractUiLabel(itemProperty),
};
});
}
getAdaptableOptionsDefaultValues() {
return getDefaultAdaptableOptions();
}
getAdaptableMetamodel() {
return ADAPTABLE_METAMODEL;
}
getAdaptableOptionsMetamodel() {
return this.getAdaptableMetamodel()['AdaptableOptions'];
}
extractUiLabel(item) {
return item.uiLabel || this.formatCamelCaseToHumanText(item.name);
}
formatCamelCaseToHumanText(camelCase) {
if (!camelCase || camelCase == null) {
return null;
}
const rex = /([A-Z])([A-Z])([a-z])|([a-z])([A-Z])/g;
const words = camelCase.replace(rex, '$1$4 $2$3$5').replace('.', ' ').split(' ');
return words
.map((word) => word.substring(0, 1).toUpperCase() +
(word.length > 1 ? word.substring(1, word.length) : ''))
.join(' ');
}
}