UNPKG

cspace-ui

Version:
755 lines (726 loc) 27.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.validateLocation = exports.mergeStrategy = exports.mergeKey = exports.mergeConfig = exports.isUtility = exports.isFieldViewReadOnly = exports.isFieldRequired = exports.isFieldRepeating = exports.isFieldCloneable = exports.isAutocompleteField = exports.isAuthority = exports.initializeRecordTypes = exports.initializeExtensions = exports.initializeExtensionFieldParents = exports.initConfig = exports.getVocabularyConfigByShortID = exports.getVocabularyConfigByServicePath = exports.getStickyFields = exports.getRequiredMessage = exports.getRecordTypeNameByUri = exports.getRecordTypeNameByServiceObjectName = exports.getRecordTypeConfigByUri = exports.getRecordTypeConfigByServicePath = exports.getRecordTypeConfigByServiceObjectName = exports.getRecordTypeConfigByServiceDocumentName = exports.getRecordGroupOptionListName = exports.getRecordFieldOptionListName = exports.getFirstColumnName = exports.getFieldDataType = exports.getFieldCustomValidator = exports.getFieldComputer = exports.getDefaults = exports.getDefaultValue = exports.getDefaultSearchVocabulary = exports.getDefaultSearchRecordType = exports.findVocabularyUses = exports.findFieldConfigInPart = exports.findField = exports.finalizeRecordTypes = exports.evaluatePlugin = exports.dataPathToFieldDescriptorPath = exports.configKey = exports.applyPlugins = exports.applyPlugin = void 0; var _react = _interopRequireDefault(require("react")); var _immutable = _interopRequireDefault(require("immutable")); var _mergeWith = _interopRequireDefault(require("lodash/mergeWith")); var _flatMap = _interopRequireDefault(require("lodash/flatMap")); var _get = _interopRequireDefault(require("lodash/get")); var _set = _interopRequireDefault(require("lodash/set")); var _warning = _interopRequireDefault(require("warning")); var _errorCodes = require("../constants/errorCodes"); var _dataTypes = require("../constants/dataTypes"); var _xmlNames = require("../constants/xmlNames"); var _csidHelpers = require("./csidHelpers"); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } const onlyDigitsPattern = /^\d+$/; const isNotNumeric = string => !onlyDigitsPattern.test(string); const configKey = exports.configKey = '[config]'; const mergeKey = exports.mergeKey = '[merge]'; const dataPathToFieldDescriptorPath = dataPath => dataPath.filter(isNotNumeric); exports.dataPathToFieldDescriptorPath = dataPathToFieldDescriptorPath; const initializeExtensionFieldParents = fieldDescriptor => { if (fieldDescriptor) { Object.keys(fieldDescriptor).filter(key => key !== configKey).forEach(key => { const childFieldDescriptor = fieldDescriptor[key]; const isExtensionField = (0, _get.default)(childFieldDescriptor, [configKey, 'extensionName']); if (isExtensionField) { // Make a copy of this field descriptor and its configuration, so that different // configuration can be applied to each use of the extension (really wishing I'd made the // config an Immutable, so explicit copy wouldn't be necessary). // Set the extension parent config in the extension field's config. const childFieldDescriptorCopy = { ...childFieldDescriptor, [configKey]: { ...childFieldDescriptor[configKey], extensionParentConfig: fieldDescriptor[configKey] } }; // eslint-disable-next-line no-param-reassign fieldDescriptor[key] = childFieldDescriptorCopy; } else { initializeExtensionFieldParents(childFieldDescriptor); } }); } }; /* * Initialize the extension configurations in a configuration object. This function mutates the * argument configuration. * * - Set the extensionName property of each top level field in the extension to the extension name */ exports.initializeExtensionFieldParents = initializeExtensionFieldParents; const initializeExtensions = config => { const { extensions } = config; if (extensions) { Object.keys(extensions).forEach(extensionName => { const extension = extensions[extensionName]; const { fields } = extension; if (fields) { Object.values(fields).forEach(fieldDescriptor => { (0, _set.default)(fieldDescriptor, [configKey, 'extensionName'], extensionName); initializeExtensionFieldParents(fieldDescriptor); }); } }); } return config; }; /* * Initialize the record type configurations in a configuration object. This function mutates the * argument configuration. * * - Set the name property of each recordTypes entry to its key * - Set the name property of each vocabularies entry to its key * - Set the parent property of any extension fields */ exports.initializeExtensions = initializeExtensions; const initializeRecordTypes = config => { const keys = ['recordTypes', ['invocables', 'report'], ['invocables', 'batch']]; keys.forEach(key => { const recordTypesConfig = (0, _get.default)(config, key); if (recordTypesConfig) { Object.keys(recordTypesConfig).forEach(recordTypeName => { const recordType = recordTypesConfig[recordTypeName]; recordType.name = recordTypeName; const { fields, vocabularies } = recordType; if (fields) { Object.values(fields).forEach(fieldDescriptor => { initializeExtensionFieldParents(fieldDescriptor); }); } if (vocabularies) { Object.keys(vocabularies).forEach(vocabularyName => { vocabularies[vocabularyName].name = vocabularyName; }); } }); } }); return config; }; /* * Finalize the record type configurations in a configuration object. This function mutates the * argument configuration. * * - Delete any record type or vocabulary that is disabled * - Set the disableAltTerms property of each vocabulary to the top-level disableAltTerms property * if it is undefined */ exports.initializeRecordTypes = initializeRecordTypes; const finalizeRecordTypes = config => { const { recordTypes } = config; if (recordTypes) { Object.keys(recordTypes).forEach(recordTypeName => { const recordType = recordTypes[recordTypeName]; if (recordType.disabled) { delete recordTypes[recordTypeName]; } else { const { vocabularies } = recordType; if (vocabularies) { Object.keys(vocabularies).forEach(vocabularyName => { const vocabulary = vocabularies[vocabularyName]; if (vocabulary.disabled) { delete vocabularies[vocabularyName]; } else if (typeof vocabulary.disableAltTerms === 'undefined') { vocabulary.disableAltTerms = config.disableAltTerms; } }); } } }); } return config; }; exports.finalizeRecordTypes = finalizeRecordTypes; const evaluatePlugin = (plugin, configContext) => { const pluginType = typeof plugin; const isValidType = plugin && (pluginType === 'function' || pluginType === 'object' && !Array.isArray(plugin)); process.env.NODE_ENV !== "production" ? (0, _warning.default)(isValidType, 'A plugin must be an object or a function.') : void 0; if (!isValidType) { return {}; } const config = pluginType === 'object' ? plugin : plugin(configContext); initializeExtensions(config); initializeRecordTypes(config); return config; }; exports.evaluatePlugin = evaluatePlugin; const applyPlugin = (targetConfig, plugin, configContext = {}) => { const pluginConfigContribution = evaluatePlugin(plugin, configContext); /* Gotta do this mutual recursion */ /* eslint-disable no-use-before-define */ return mergeConfig(targetConfig, pluginConfigContribution, configContext); /* eslint-enable no-use-before-define */ }; exports.applyPlugin = applyPlugin; const applyPlugins = (targetConfig, plugins, configContext = {}) => { const isArray = Array.isArray(plugins); process.env.NODE_ENV !== "production" ? (0, _warning.default)(isArray, 'Plugins must be an array.') : void 0; if (!isArray) { return targetConfig; } return plugins.reduce((updatedConfig, plugin) => { // eslint-disable-next-line no-param-reassign configContext.config = updatedConfig; return applyPlugin(updatedConfig, plugin, configContext); }, targetConfig); }; exports.applyPlugins = applyPlugins; const mergeStrategy = exports.mergeStrategy = { override: srcValue => ({ ...srcValue, [mergeKey]: 'override' }) }; const configMerger = (objValue, srcValue, key) => { if (Array.isArray(objValue)) { // Don't merge arrays. Just override with the source value. return srcValue; } if (key === 'advancedSearch') { // Don't merge advanced search config. Just override with the source value. return srcValue; } if ( /*#__PURE__*/_react.default.isValidElement(objValue)) { // Don't merge React elements, e.g. in form templates. Just override with the source value. return srcValue; } if (srcValue && typeof srcValue === 'object' && srcValue[mergeKey] === 'override') { const srcValueCopy = { ...srcValue }; delete srcValueCopy[mergeKey]; return srcValueCopy; } return undefined; }; const mergeConfig = (targetConfig, sourceConfig, configContext = {}) => { // eslint-disable-next-line no-param-reassign configContext.config = targetConfig; const pluginsAppliedConfig = sourceConfig && 'plugins' in sourceConfig ? applyPlugins(targetConfig, sourceConfig.plugins, configContext) : targetConfig; const mergedConfig = (0, _mergeWith.default)({}, pluginsAppliedConfig, sourceConfig, configMerger); delete mergedConfig.plugins; return mergedConfig; }; exports.mergeConfig = mergeConfig; const initConfig = (config, configContext) => mergeConfig({}, config, configContext); exports.initConfig = initConfig; const getRecordTypeConfigByServiceDocumentName = (config, documentName) => { if (!documentName) { return undefined; } if (!config.recordTypesByServiceDocumentName) { const recordTypesByServiceDocumentName = {}; const { recordTypes } = config; Object.keys(recordTypes).forEach(recordType => { const recordTypeConfig = recordTypes[recordType]; const { serviceConfig } = recordTypeConfig; recordTypesByServiceDocumentName[serviceConfig.documentName] = recordTypeConfig; }); /* eslint-disable no-param-reassign */ config.recordTypesByServiceDocumentName = recordTypesByServiceDocumentName; /* eslint-enable no-param-reassign */ } return config.recordTypesByServiceDocumentName[documentName]; }; exports.getRecordTypeConfigByServiceDocumentName = getRecordTypeConfigByServiceDocumentName; const getRecordTypeConfigByServiceObjectName = (config, objectName) => { if (!objectName) { return undefined; } if (!config.recordTypesByServiceObjectName) { const recordTypesByServiceObjectName = {}; const { recordTypes } = config; Object.keys(recordTypes).forEach(recordType => { const recordTypeConfig = recordTypes[recordType]; recordTypesByServiceObjectName[recordTypeConfig.serviceConfig.objectName] = recordTypeConfig; }); /* eslint-disable no-param-reassign */ config.recordTypesByServiceObjectName = recordTypesByServiceObjectName; /* eslint-enable no-param-reassign */ } return config.recordTypesByServiceObjectName[objectName]; }; exports.getRecordTypeConfigByServiceObjectName = getRecordTypeConfigByServiceObjectName; const getRecordTypeNameByServiceObjectName = (config, objectName) => { const recordTypeConfig = getRecordTypeConfigByServiceObjectName(config, objectName); return recordTypeConfig ? recordTypeConfig.name : undefined; }; exports.getRecordTypeNameByServiceObjectName = getRecordTypeNameByServiceObjectName; const getRecordTypeConfigByServicePath = (config, servicePath) => { if (!servicePath) { return undefined; } if (!config.recordTypesByServicePath) { const recordTypesByServicePath = {}; const { recordTypes } = config; Object.keys(recordTypes).forEach(recordType => { const recordTypeConfig = recordTypes[recordType]; recordTypesByServicePath[recordTypeConfig.serviceConfig.servicePath] = recordTypeConfig; }); /* eslint-disable no-param-reassign */ config.recordTypesByServicePath = recordTypesByServicePath; /* eslint-enable no-param-reassign */ } return config.recordTypesByServicePath[servicePath]; }; exports.getRecordTypeConfigByServicePath = getRecordTypeConfigByServicePath; const getRecordTypeConfigByUri = (config, uri) => { if (!uri) { return undefined; } const servicePath = uri.split('/', 2)[1]; return getRecordTypeConfigByServicePath(config, servicePath); }; exports.getRecordTypeConfigByUri = getRecordTypeConfigByUri; const getRecordTypeNameByUri = (config, uri) => { const recordTypeConfig = getRecordTypeConfigByUri(config, uri); return recordTypeConfig ? recordTypeConfig.name : undefined; }; exports.getRecordTypeNameByUri = getRecordTypeNameByUri; const getVocabularyConfigByShortID = (recordTypeConfig, shortID) => { if (!shortID) { return undefined; } if (!recordTypeConfig.vocabulariesByShortID) { const vocabulariesByShortID = {}; const { vocabularies } = recordTypeConfig; if (vocabularies) { Object.keys(vocabularies).forEach(vocabulary => { const vocabularyConfig = vocabularies[vocabulary]; const { servicePath } = vocabularyConfig.serviceConfig; if (servicePath && servicePath.indexOf('urn:cspace:name(') === 0 && servicePath.lastIndexOf(')') === servicePath.length - 1) { const vocabularyShortID = servicePath.substring(16, servicePath.length - 1); vocabulariesByShortID[vocabularyShortID] = vocabularyConfig; } }); /* eslint-disable no-param-reassign */ recordTypeConfig.vocabulariesByShortID = vocabulariesByShortID; /* eslint-enable no-param-reassign */ } } return recordTypeConfig.vocabulariesByShortID[shortID]; }; exports.getVocabularyConfigByShortID = getVocabularyConfigByShortID; const vocabularyServicePathPattern = /^urn:cspace:name\((.*?)\)$/; const getVocabularyConfigByServicePath = (recordTypeConfig, servicePath) => { const match = vocabularyServicePathPattern.exec(servicePath); if (!match) { return undefined; } const shortID = match[1]; return getVocabularyConfigByShortID(recordTypeConfig, shortID); }; exports.getVocabularyConfigByServicePath = getVocabularyConfigByServicePath; const getDefaultValue = fieldDescriptor => { const config = fieldDescriptor[configKey]; if (config) { const { dataType, defaultValue } = config; if (typeof defaultValue === 'object' && !_immutable.default.Map.isMap(defaultValue)) { // If an object is supplied as a default value, convert it to an immutable map. return _immutable.default.fromJS(defaultValue); } if (typeof defaultValue === 'undefined' && dataType === _dataTypes.DATA_TYPE_BOOL) { // If no default value is configured for a boolean field, consider it to be false. return false; } return defaultValue; } return undefined; }; exports.getDefaultValue = getDefaultValue; const getDefaults = (fieldDescriptor, currentPath = []) => { let results = []; const defaultValue = getDefaultValue(fieldDescriptor); if (typeof defaultValue !== 'undefined') { results = results.concat({ path: currentPath, value: defaultValue }); } const childKeys = Object.keys(fieldDescriptor).filter(key => key !== configKey); childKeys.forEach(childKey => { const childPath = currentPath.concat(childKey); const childfieldDescriptor = fieldDescriptor[childKey]; const childResults = getDefaults(childfieldDescriptor, childPath); results = results.concat(childResults); }); return results; }; /** * Returns an array of paths to sticky fields that exist under a given field. */ exports.getDefaults = getDefaults; const getStickyFields = (fieldDescriptor, currentPath = []) => { if (!fieldDescriptor) { return []; } const isSticky = (0, _get.default)(fieldDescriptor, [configKey, 'sticky']); if (isSticky) { return [currentPath]; } return Object.keys(fieldDescriptor).filter(key => key !== configKey).reduce((results, childKey) => { const childPath = currentPath.concat(childKey); const childfieldDescriptor = fieldDescriptor[childKey]; const childResults = getStickyFields(childfieldDescriptor, childPath); return results.concat(childResults); }, []); }; exports.getStickyFields = getStickyFields; const isAutocompleteField = fieldDescriptor => { const viewType = (0, _get.default)(fieldDescriptor, [configKey, 'view', 'type']); return JSON.stringify(viewType) === '"AutocompleteInput"'; }; exports.isAutocompleteField = isAutocompleteField; const isFieldCloneable = fieldDescriptor => { const config = fieldDescriptor[configKey]; if (config && 'cloneable' in config) { return config.cloneable; } return true; }; exports.isFieldCloneable = isFieldCloneable; const isFieldViewReadOnly = computeContext => { const { fieldDescriptor, isSearch } = computeContext; let readOnly; const fieldConfig = (0, _get.default)(fieldDescriptor, configKey); if (fieldConfig) { const viewConfig = isSearch ? fieldConfig.searchView || fieldConfig.view : fieldConfig.view; readOnly = (0, _get.default)(viewConfig, ['props', 'readOnly']); if (typeof readOnly === 'function') { const callComputeContext = { ...computeContext }; // Don't include the data property of the compute context when calling the function, since it // doesn't really make sense to have the read only state of a field depend on the value in the // field. delete callComputeContext.data; readOnly = readOnly(callComputeContext); } } return !!readOnly; }; exports.isFieldViewReadOnly = isFieldViewReadOnly; const isFieldRepeating = fieldDescriptor => { const config = fieldDescriptor[configKey]; if (config && 'repeating' in config) { return config.repeating; } return false; }; exports.isFieldRepeating = isFieldRepeating; const isFieldRequired = computeContext => { const { fieldDescriptor } = computeContext; let required = (0, _get.default)(fieldDescriptor, [configKey, 'required']); if (typeof required === 'function') { const callComputeContext = { ...computeContext }; // Don't include the data property of the compute context when calling the function, since it // doesn't really make sense to have the required state of a field depend on the value in the // field. delete callComputeContext.data; required = required(callComputeContext); } return !!required; }; exports.isFieldRequired = isFieldRequired; const getFieldDataType = fieldDescriptor => { let type; const fieldConfig = fieldDescriptor[configKey]; if (fieldConfig) { type = fieldConfig.dataType; } if (!type) { // Check if there are child field descriptors. If so, default to map. const keys = Object.keys(fieldDescriptor); for (let i = 0; i < keys.length; i += 1) { if (keys[i] !== configKey) { type = _dataTypes.DATA_TYPE_MAP; break; } } } if (!type) { // Default to string. type = _dataTypes.DATA_TYPE_STRING; } return type; }; exports.getFieldDataType = getFieldDataType; const getFieldCustomValidator = fieldDescriptor => { let validator; const fieldConfig = fieldDescriptor[configKey]; if (fieldConfig) { validator = fieldConfig.validate; } return validator; }; exports.getFieldCustomValidator = getFieldCustomValidator; const getFieldComputer = fieldDescriptor => { let computer; const fieldConfig = fieldDescriptor[configKey]; if (fieldConfig) { computer = fieldConfig.compute; } return computer; }; exports.getFieldComputer = getFieldComputer; const getRequiredMessage = fieldDescriptor => (0, _get.default)(fieldDescriptor, [configKey, 'messages', 'required']); exports.getRequiredMessage = getRequiredMessage; const isAuthority = recordTypeConfig => (0, _get.default)(recordTypeConfig, ['serviceConfig', 'serviceType']) === 'authority'; exports.isAuthority = isAuthority; const isUtility = recordTypeConfig => (0, _get.default)(recordTypeConfig, ['serviceConfig', 'serviceType']) === 'utility'; exports.isUtility = isUtility; const validateLocation = (config, location) => { const { recordType, vocabulary, csid, subresource, relatedRecordType, relatedCsid } = location; const recordTypeConfig = (0, _get.default)(config, ['recordTypes', recordType]); if (!recordTypeConfig || recordTypeConfig.disabled) { return { error: { recordType, code: _errorCodes.ERR_UNKNOWN_RECORD_TYPE } }; } if (isAuthority(recordTypeConfig)) { if (!vocabulary) { return { error: { recordType, code: _errorCodes.ERR_MISSING_VOCABULARY } }; } const vocabularyConfig = (0, _get.default)(recordTypeConfig, ['vocabularies', vocabulary]); if (!vocabularyConfig || vocabularyConfig.disabled) { return { error: { recordType, vocabulary, code: _errorCodes.ERR_UNKNOWN_VOCABULARY } }; } } else if (vocabulary) { return { error: { recordType, vocabulary, code: _errorCodes.ERR_UNKNOWN_VOCABULARY } }; } if (csid && !(0, _csidHelpers.isCsid)(csid) && !(0, _csidHelpers.isUrnCsid)(csid)) { return { error: { csid, code: _errorCodes.ERR_INVALID_CSID } }; } if (subresource) { const subresourceConfig = (0, _get.default)(config, ['subresources', subresource]); if (!subresourceConfig) { return { error: { subresource, code: _errorCodes.ERR_UNKNOWN_SUBRESOURCE } }; } } if (relatedRecordType) { const serviceType = (0, _get.default)(recordTypeConfig, ['serviceConfig', 'serviceType']); const relatedServiceType = (0, _get.default)(config, ['recordTypes', relatedRecordType, 'serviceConfig', 'serviceType']); if (serviceType !== 'procedure' && serviceType !== 'object' || relatedServiceType !== 'procedure' && relatedServiceType !== 'object') { return { error: { recordType, relatedRecordType, code: _errorCodes.ERR_INVALID_RELATED_TYPE } }; } } if (relatedCsid && !(0, _csidHelpers.isCsid)(relatedCsid)) { return { error: { csid: relatedCsid, code: _errorCodes.ERR_INVALID_CSID } }; } return {}; }; exports.validateLocation = validateLocation; const getDefaultSearchRecordType = config => { const { recordTypes } = config; const names = Object.keys(recordTypes); let defaultSearchRecordType; for (let i = 0; i < names.length; i += 1) { const name = names[i]; if (recordTypes[name].defaultForSearch) { defaultSearchRecordType = name; break; } } return defaultSearchRecordType || names[0]; }; exports.getDefaultSearchRecordType = getDefaultSearchRecordType; const getDefaultSearchVocabulary = recordTypeConfig => { const { vocabularies } = recordTypeConfig; const names = Object.keys(vocabularies); let defaultSearchVocabulary; for (let i = 0; i < names.length; i += 1) { const name = names[i]; if (vocabularies[name].defaultForSearch) { defaultSearchVocabulary = name; break; } } return defaultSearchVocabulary || names[0]; }; exports.getDefaultSearchVocabulary = getDefaultSearchVocabulary; const findField = (parentFieldDescriptor, fieldName) => { const keys = Object.keys(parentFieldDescriptor); for (let i = 0; i < keys.length; i += 1) { const key = keys[i]; const value = parentFieldDescriptor[key]; if (key === fieldName) { return value; } if (key !== configKey) { const fieldDescriptor = findField(value, fieldName); if (fieldDescriptor) { return fieldDescriptor; } } } return null; }; exports.findField = findField; const findFieldConfigInPart = (recordTypeConfig, partName, fieldName) => { const partDescriptor = (0, _get.default)(recordTypeConfig, ['fields', 'document', `${_xmlNames.NS_PREFIX}:${partName}`]); if (!partDescriptor) { return null; } // 1. Look for the field as a direct child of the part. let fieldDescriptor = partDescriptor[fieldName]; // 2. Check for a memoized result. if (!fieldDescriptor) { fieldDescriptor = (0, _get.default)(recordTypeConfig, ['fieldsByPart', partName, fieldName]); } // 3. Search for a nested field. if (typeof fieldDescriptor === 'undefined') { fieldDescriptor = findField(partDescriptor, fieldName); // Memoize the result. (0, _set.default)(recordTypeConfig, ['fieldsByPart', partName, fieldName], fieldDescriptor); } return fieldDescriptor ? fieldDescriptor[configKey] : null; }; exports.findFieldConfigInPart = findFieldConfigInPart; const findFieldsWithSource = (fieldDescriptor, shortId) => { const fieldsWithSource = (0, _flatMap.default)(Object.keys(fieldDescriptor).filter(key => key !== configKey), childFieldName => findFieldsWithSource(fieldDescriptor[childFieldName], shortId)); const fieldConfig = fieldDescriptor[configKey]; const source = (0, _get.default)(fieldConfig, ['view', 'props', 'source']); if (source === shortId) { fieldsWithSource.push(fieldConfig); } return fieldsWithSource; }; const findVocabularyUses = (config, shortId) => { if (!shortId) { return null; } const uses = {}; Object.values(config.recordTypes).forEach(recordTypeConfig => { const fieldDescriptor = recordTypeConfig.fields; if (fieldDescriptor) { const fields = findFieldsWithSource(recordTypeConfig.fields, shortId); if (fields.length > 0) { uses[recordTypeConfig.name] = fields; } } }); return uses; }; exports.findVocabularyUses = findVocabularyUses; const getFirstColumnName = (config, recordType, columnSetName = 'default') => { const columnConfig = (0, _get.default)(config, ['recordTypes', recordType, 'columns', columnSetName]); if (!columnConfig) { return undefined; } const orderedColumnNames = Object.keys(columnConfig).filter(name => { const { disabled, width } = columnConfig[name]; return !disabled // FIXME: This is a hack to filter out thumbnail/icon columns, so that the first text column // is returned. && (typeof width === 'undefined' || width >= 100); }).sort((nameA, nameB) => { const orderA = columnConfig[nameA].order; const orderB = columnConfig[nameB].order; return orderA - orderB; }); return orderedColumnNames[0]; }; exports.getFirstColumnName = getFirstColumnName; const getRecordFieldOptionListName = (recordType, rootPath) => { const atPath = rootPath ? `@${rootPath}` : ''; return `_field_${recordType}${atPath}`; }; exports.getRecordFieldOptionListName = getRecordFieldOptionListName; const getRecordGroupOptionListName = (recordType, rootPath) => { const atPath = rootPath ? `@${rootPath}` : ''; return `_fieldgroup_${recordType}${atPath}`; }; exports.getRecordGroupOptionListName = getRecordGroupOptionListName;