UNPKG

@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
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(' '); } }