@overture-stack/lyric
Version:
Data Submission system
147 lines (146 loc) • 8 kB
JavaScript
import submittedRepository from '../../repository/submittedRepository.js';
import { generateHierarchy } from '../../utils/dictionarySchemaRelations.js';
import { InternalServerError } from '../../utils/errors.js';
import { pluralizeSchemaName } from '../../utils/submissionUtils.js';
import { groupByEntityName } from '../../utils/submittedDataUtils.js';
import { ORDER_TYPE } from '../../utils/types.js';
const viewMode = (dependencies) => {
const LOG_MODULE = 'VIEW_MODE_SERVICE';
const submittedDataRepo = submittedRepository(dependencies);
const recordHierarchy = dependencies.features?.recordHierarchy;
const { logger } = dependencies;
const convertRecordsToCompoundDocuments = async ({ dictionary, records, defaultCentricEntity, }) => {
// get dictionary hierarchy structure
const hierarchyStructureDesc = generateHierarchy(dictionary, ORDER_TYPE.Values.desc);
const hierarchyStructureAsc = generateHierarchy(dictionary, ORDER_TYPE.Values.asc);
return await Promise.all(records.map(async (record) => {
try {
const childNodes = await traverseChildNodes({
data: record.data,
entityName: record.entityName,
organization: record.organization,
schemaCentric: defaultCentricEntity || record.entityName,
treeNode: hierarchyStructureDesc,
});
const parentNodes = await traverseParentNodes({
data: record.data,
entityName: record.entityName,
organization: record.organization,
schemaCentric: defaultCentricEntity || record.entityName,
treeNode: hierarchyStructureAsc,
});
record.data = { ...record.data, ...childNodes, ...parentNodes };
}
catch (error) {
logger.error(LOG_MODULE, `Error converting record ${record.systemId} into compound document`, error);
throw new InternalServerError(`An error occurred while converting records into compound view`);
}
return record;
}));
};
/**
* Recursively traverses parent nodes of a schema tree and queries for related SubmittedData.
*
* This function takes in the current data record, entity name, organization, schema-centric information,
* and tree node structure, then filters and queries dependent records recursively, constructing a nested
* structure of related data.
*
* @param data - The current data record to traverse.
* @param entityName - The name of the entity (schema) associated with the current data.
* @param organization - The organization to which the data belongs.
* @param schemaCentric - The schema-centric identifier for filtering parent nodes.
* @param treeNode - The hierarchical structure representing schema relationships.
*
* @returns A promise that resolves to a nested `DataRecordNested` object, containing the traversed and filtered dependent data.
* If no parent nodes or dependencies exist, it returns an empty object.
*
*/
const traverseParentNodes = async ({ data, entityName, organization, schemaCentric, treeNode, }) => {
const { getSubmittedDataFiltered } = submittedDataRepo;
const parentNode = treeNode.find((node) => node.schemaName === schemaCentric)?.parent;
if (!parentNode || !parentNode.parentFieldName || !parentNode.schemaName) {
// return empty array when no dependents for this record
return {};
}
const filterData = {
entityName: parentNode.schemaName,
dataField: parentNode.fieldName || '',
dataValue: data[parentNode.parentFieldName]?.toString() || '',
};
logger.debug(LOG_MODULE, `Entity '${entityName}' has following dependencies filter '${JSON.stringify(filterData)}'`);
const directDependants = await getSubmittedDataFiltered(organization, [filterData]);
const groupedDependants = groupByEntityName(directDependants);
const result = {};
for (const [entityName, records] of Object.entries(groupedDependants)) {
const additionalRecordsForEntity = await Promise.all(records.map(async (record) => {
const additional = await traverseParentNodes({
data: record.data,
entityName: record.entityName,
organization: record.organization,
schemaCentric: record.entityName,
treeNode,
});
return { ...record.data, ...additional };
}));
// Getting the first record as record can have only 1 parent
result[entityName] = additionalRecordsForEntity[0];
}
return result;
};
/**
* Recursively traverses child nodes of a schema tree and queries for related SubmittedData.
*
* This function takes in the current data record, entity name, organization, schema-centric information,
* and tree node structure, then filters and queries dependent records recursively, constructing a nested
* structure of related data.
*
* @param data - The current data record to traverse.
* @param entityName - The name of the entity (schema) associated with the current data.
* @param organization - The organization to which the data belongs.
* @param schemaCentric - The schema-centric identifier for filtering child nodes.
* @param treeNode - The hierarchical structure representing schema relationships.
*
* @returns A promise that resolves to a nested `DataRecordNested` object, containing the traversed and filtered dependent data.
* If no child nodes or dependencies exist, it returns an empty object.
*
*/
const traverseChildNodes = async ({ data, entityName, organization, schemaCentric, treeNode, }) => {
const { getSubmittedDataFiltered } = submittedDataRepo;
const childNode = treeNode
.find((node) => node.schemaName === schemaCentric)
?.children?.filter((childNode) => childNode.parentFieldName && childNode.schemaName);
if (!childNode || childNode.length === 0) {
// return empty array when no dependents for this record
return {};
}
const filterData = childNode.map((childNode) => ({
entityName: childNode.schemaName,
dataField: childNode.fieldName || '',
dataValue: data[childNode.parentFieldName]?.toString() || '',
}));
logger.debug(LOG_MODULE, `Entity '${entityName}' has following dependencies filter '${JSON.stringify(filterData)}'`);
const directDependants = await getSubmittedDataFiltered(organization, filterData);
const groupedDependants = groupByEntityName(directDependants);
const result = {};
for (const [entityName, records] of Object.entries(groupedDependants)) {
// if enabled ensures that schema names are consistently pluralized
const dependantKeyName = recordHierarchy?.pluralizeSchemasName ? pluralizeSchemaName(entityName) : entityName;
const additionalRecordsForEntity = await Promise.all(records.map(async (record) => {
const additional = await traverseChildNodes({
data: record.data,
entityName: record.entityName,
organization: record.organization,
schemaCentric: record.entityName,
treeNode,
});
return { ...record.data, ...additional };
}));
result[dependantKeyName] = additionalRecordsForEntity;
}
return result;
};
return {
convertRecordsToCompoundDocuments,
};
};
export default viewMode;