@openforis/arena-core 2.16.8 → 2.16.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/record/_records/recordCompletion.js +2 -2
- package/dist/esm/record/_records/recordCompletion.js.map +1 -1
- package/dist/esm/record/recordCloner.js +1 -1
- package/dist/esm/record/recordCloner.js.map +1 -1
- package/dist/esm/record/recordFixer.js +3 -1
- package/dist/esm/record/recordFixer.js.map +1 -1
- package/dist/esm/record/recordNodesUpdater/recordNodesCreator.js +3 -2
- package/dist/esm/record/recordNodesUpdater/recordNodesCreator.js.map +1 -1
- package/dist/esm/utils/fileProcessor.js +28 -1
- package/dist/esm/utils/fileProcessor.js.map +1 -1
- package/dist/esm/utils/retryProcessor.js +1 -0
- package/dist/esm/utils/retryProcessor.js.map +1 -1
- package/dist/record/_records/recordCompletion.js +1 -1
- package/dist/record/_records/recordCompletion.js.map +1 -1
- package/dist/record/recordCloner.js +1 -1
- package/dist/record/recordCloner.js.map +1 -1
- package/dist/record/recordFixer.js +3 -1
- package/dist/record/recordFixer.js.map +1 -1
- package/dist/record/recordNodesUpdater/recordNodesCreator.js +2 -1
- package/dist/record/recordNodesUpdater/recordNodesCreator.js.map +1 -1
- package/dist/utils/fileProcessor.d.ts +3 -0
- package/dist/utils/fileProcessor.js +28 -1
- package/dist/utils/fileProcessor.js.map +1 -1
- package/dist/utils/retryProcessor.js +1 -0
- package/dist/utils/retryProcessor.js.map +1 -1
- package/package.json +1 -1
|
@@ -2,7 +2,7 @@ import { Nodes } from '../../node';
|
|
|
2
2
|
import { NodeDefs } from '../../nodeDef';
|
|
3
3
|
import { Surveys } from '../../survey';
|
|
4
4
|
import { Numbers } from '../../utils';
|
|
5
|
-
import { getChildren, getRoot } from './recordGetters';
|
|
5
|
+
import { getChildren, getCycle, getRoot } from './recordGetters';
|
|
6
6
|
const emptyStats = () => ({ total: 0, filled: 0 });
|
|
7
7
|
const addStats = (statsAcc, statsToAdd) => {
|
|
8
8
|
statsAcc.total += statsToAdd.total;
|
|
@@ -49,7 +49,7 @@ const getChildEntityCompletionStats = (params) => {
|
|
|
49
49
|
export const getEntityCompletionStats = (params) => {
|
|
50
50
|
const { survey, record, entity, includeNestedEntities = true } = params;
|
|
51
51
|
const entityDef = Surveys.getNodeDefByUuid({ survey, uuid: entity.nodeDefUuid });
|
|
52
|
-
const childDefs = Surveys.getNodeDefChildren({ survey, nodeDef: entityDef });
|
|
52
|
+
const childDefs = Surveys.getNodeDefChildren({ survey, nodeDef: entityDef }).filter(NodeDefs.isInCycle(getCycle(record)));
|
|
53
53
|
const stats = emptyStats();
|
|
54
54
|
for (const childDef of childDefs) {
|
|
55
55
|
if (!Nodes.isChildApplicable(entity, childDef.uuid))
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"recordCompletion.js","sourceRoot":"","sources":["../../../../src/record/_records/recordCompletion.ts"],"names":[],"mappings":"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","sourcesContent":["import { ArenaRecordNode, Nodes } from '../../node'\nimport { NodeDef, NodeDefProps, NodeDefs, NodeDefType } from '../../nodeDef'\nimport { Survey, Surveys } from '../../survey'\nimport { Numbers } from '../../utils'\nimport { ArenaRecord } from '../record'\nimport { getChildren, getRoot } from './recordGetters'\n\nexport interface EntityCompletionStats {\n total: number\n filled: number\n}\n\nconst emptyStats = (): EntityCompletionStats => ({ total: 0, filled: 0 })\n\nconst addStats = (statsAcc: EntityCompletionStats, statsToAdd: EntityCompletionStats): void => {\n statsAcc.total += statsToAdd.total\n statsAcc.filled += statsToAdd.filled\n}\n\nconst isAttributeRequired = (entity: ArenaRecordNode, nodeDef: NodeDef<NodeDefType, NodeDefProps>): boolean =>\n NodeDefs.isKey(nodeDef) ||\n NodeDefs.isRequired(nodeDef) ||\n (NodeDefs.isRequiredIfDefined(nodeDef) && Nodes.isChildRequired(entity, nodeDef.uuid))\n\nconst getAttributeCompletionStats = (params: {\n record: ArenaRecord\n entity: ArenaRecordNode\n nodeDef: NodeDef<NodeDefType, NodeDefProps>\n}): EntityCompletionStats => {\n const { record, entity, nodeDef } = params\n\n if (NodeDefs.isMultiple(nodeDef)) {\n const minCount = Nodes.getChildrenMinCount({ parentNode: entity, nodeDef })\n if (Number.isNaN(minCount) || minCount <= 0) return emptyStats()\n\n const filledNodesCount = getChildren(entity, nodeDef.uuid)(record).filter(Nodes.isValueNotBlank).length\n\n return { total: minCount, filled: Math.min(filledNodesCount, minCount) }\n }\n\n if (!isAttributeRequired(entity, nodeDef)) return emptyStats()\n\n const node = getChildren(entity, nodeDef.uuid)(record)[0]\n return { total: 1, filled: node && Nodes.isValueNotBlank(node) ? 1 : 0 }\n}\n\nconst getChildEntityCompletionStats = (params: {\n survey: Survey\n record: ArenaRecord\n parentEntity: ArenaRecordNode\n nodeDef: NodeDef<NodeDefType, NodeDefProps>\n}): EntityCompletionStats => {\n const { survey, record, parentEntity, nodeDef } = params\n const childEntities = getChildren(parentEntity, nodeDef.uuid)(record)\n\n if (NodeDefs.isSingle(nodeDef)) {\n const childEntity = childEntities[0]\n return childEntity ? getEntityCompletionStats({ survey, record, entity: childEntity }) : emptyStats()\n }\n\n const minCountRaw = Nodes.getChildrenMinCount({ parentNode: parentEntity, nodeDef })\n const minCount = Number.isNaN(minCountRaw) ? 0 : Math.max(minCountRaw, 0)\n // entities added beyond the min count are still expected to be complete\n const expectedCount = Math.max(minCount, childEntities.length)\n if (expectedCount === 0) return emptyStats()\n\n const stats = emptyStats()\n for (let index = 0; index < expectedCount; index++) {\n const childEntity = childEntities[index]\n // a missing repetition (below the min count) counts as a single incomplete unit\n addStats(\n stats,\n childEntity ? getEntityCompletionStats({ survey, record, entity: childEntity }) : { total: 1, filled: 0 }\n )\n }\n return stats\n}\n\nexport const getEntityCompletionStats = (params: {\n survey: Survey\n record: ArenaRecord\n entity: ArenaRecordNode\n includeNestedEntities?: boolean\n}): EntityCompletionStats => {\n const { survey, record, entity, includeNestedEntities = true } = params\n const entityDef = Surveys.getNodeDefByUuid({ survey, uuid: entity.nodeDefUuid })\n const childDefs = Surveys.getNodeDefChildren({ survey, nodeDef: entityDef })\n\n const stats = emptyStats()\n for (const childDef of childDefs) {\n if (!Nodes.isChildApplicable(entity, childDef.uuid)) continue\n\n if (NodeDefs.isEntity(childDef)) {\n if (!includeNestedEntities) continue\n addStats(stats, getChildEntityCompletionStats({ survey, record, parentEntity: entity, nodeDef: childDef }))\n } else {\n addStats(stats, getAttributeCompletionStats({ record, entity, nodeDef: childDef }))\n }\n }\n return stats\n}\n\nconst toCompletionPercent = (stats: EntityCompletionStats): number => {\n const { total, filled } = stats\n if (total <= 0) return 100\n return Numbers.roundToPrecision((filled / total) * 100, 2)\n}\n\n/**\n * Returns the percentage of completion of the given entity (0-100), considering every descendant recursively.\n * A required or key attribute is considered filled when it has a non empty value; multiple attributes and\n * entities are considered filled based on their min count (see Nodes.getChildrenMinCount).\n */\nexport const getEntityCompletionPercent = (params: {\n survey: Survey\n record: ArenaRecord\n entity: ArenaRecordNode\n}): number => toCompletionPercent(getEntityCompletionStats(params))\n\n/**\n * Returns the percentage of completion of the given entity (0-100), considering only its own attributes\n * (nested entities are ignored).\n */\nexport const getEntityOwnCompletionPercent = (params: {\n survey: Survey\n record: ArenaRecord\n entity: ArenaRecordNode\n}): number => toCompletionPercent(getEntityCompletionStats({ ...params, includeNestedEntities: false }))\n\n/**\n * Returns the percentage of completion of the given record (0-100), i.e. the completion of its root entity.\n */\nexport const getRecordCompletionPercent = (params: { survey: Survey; record: ArenaRecord }): number => {\n const { survey, record } = params\n const rootEntity = getRoot(record)\n return rootEntity ? getEntityCompletionPercent({ survey, record, entity: rootEntity }) : 0\n}\n"]}
|
|
1
|
+
{"version":3,"file":"recordCompletion.js","sourceRoot":"","sources":["../../../../src/record/_records/recordCompletion.ts"],"names":[],"mappings":"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","sourcesContent":["import { ArenaRecordNode, Nodes } from '../../node'\nimport { NodeDef, NodeDefProps, NodeDefs, NodeDefType } from '../../nodeDef'\nimport { Survey, Surveys } from '../../survey'\nimport { Numbers } from '../../utils'\nimport { ArenaRecord } from '../record'\nimport { getChildren, getCycle, getRoot } from './recordGetters'\n\nexport interface EntityCompletionStats {\n total: number\n filled: number\n}\n\nconst emptyStats = (): EntityCompletionStats => ({ total: 0, filled: 0 })\n\nconst addStats = (statsAcc: EntityCompletionStats, statsToAdd: EntityCompletionStats): void => {\n statsAcc.total += statsToAdd.total\n statsAcc.filled += statsToAdd.filled\n}\n\nconst isAttributeRequired = (entity: ArenaRecordNode, nodeDef: NodeDef<NodeDefType, NodeDefProps>): boolean =>\n NodeDefs.isKey(nodeDef) ||\n NodeDefs.isRequired(nodeDef) ||\n (NodeDefs.isRequiredIfDefined(nodeDef) && Nodes.isChildRequired(entity, nodeDef.uuid))\n\nconst getAttributeCompletionStats = (params: {\n record: ArenaRecord\n entity: ArenaRecordNode\n nodeDef: NodeDef<NodeDefType, NodeDefProps>\n}): EntityCompletionStats => {\n const { record, entity, nodeDef } = params\n\n if (NodeDefs.isMultiple(nodeDef)) {\n const minCount = Nodes.getChildrenMinCount({ parentNode: entity, nodeDef })\n if (Number.isNaN(minCount) || minCount <= 0) return emptyStats()\n\n const filledNodesCount = getChildren(entity, nodeDef.uuid)(record).filter(Nodes.isValueNotBlank).length\n\n return { total: minCount, filled: Math.min(filledNodesCount, minCount) }\n }\n\n if (!isAttributeRequired(entity, nodeDef)) return emptyStats()\n\n const node = getChildren(entity, nodeDef.uuid)(record)[0]\n return { total: 1, filled: node && Nodes.isValueNotBlank(node) ? 1 : 0 }\n}\n\nconst getChildEntityCompletionStats = (params: {\n survey: Survey\n record: ArenaRecord\n parentEntity: ArenaRecordNode\n nodeDef: NodeDef<NodeDefType, NodeDefProps>\n}): EntityCompletionStats => {\n const { survey, record, parentEntity, nodeDef } = params\n const childEntities = getChildren(parentEntity, nodeDef.uuid)(record)\n\n if (NodeDefs.isSingle(nodeDef)) {\n const childEntity = childEntities[0]\n return childEntity ? getEntityCompletionStats({ survey, record, entity: childEntity }) : emptyStats()\n }\n\n const minCountRaw = Nodes.getChildrenMinCount({ parentNode: parentEntity, nodeDef })\n const minCount = Number.isNaN(minCountRaw) ? 0 : Math.max(minCountRaw, 0)\n // entities added beyond the min count are still expected to be complete\n const expectedCount = Math.max(minCount, childEntities.length)\n if (expectedCount === 0) return emptyStats()\n\n const stats = emptyStats()\n for (let index = 0; index < expectedCount; index++) {\n const childEntity = childEntities[index]\n // a missing repetition (below the min count) counts as a single incomplete unit\n addStats(\n stats,\n childEntity ? getEntityCompletionStats({ survey, record, entity: childEntity }) : { total: 1, filled: 0 }\n )\n }\n return stats\n}\n\nexport const getEntityCompletionStats = (params: {\n survey: Survey\n record: ArenaRecord\n entity: ArenaRecordNode\n includeNestedEntities?: boolean\n}): EntityCompletionStats => {\n const { survey, record, entity, includeNestedEntities = true } = params\n const entityDef = Surveys.getNodeDefByUuid({ survey, uuid: entity.nodeDefUuid })\n const childDefs = Surveys.getNodeDefChildren({ survey, nodeDef: entityDef }).filter(\n NodeDefs.isInCycle(getCycle(record))\n )\n\n const stats = emptyStats()\n for (const childDef of childDefs) {\n if (!Nodes.isChildApplicable(entity, childDef.uuid)) continue\n\n if (NodeDefs.isEntity(childDef)) {\n if (!includeNestedEntities) continue\n addStats(stats, getChildEntityCompletionStats({ survey, record, parentEntity: entity, nodeDef: childDef }))\n } else {\n addStats(stats, getAttributeCompletionStats({ record, entity, nodeDef: childDef }))\n }\n }\n return stats\n}\n\nconst toCompletionPercent = (stats: EntityCompletionStats): number => {\n const { total, filled } = stats\n if (total <= 0) return 100\n return Numbers.roundToPrecision((filled / total) * 100, 2)\n}\n\n/**\n * Returns the percentage of completion of the given entity (0-100), considering every descendant recursively.\n * A required or key attribute is considered filled when it has a non empty value; multiple attributes and\n * entities are considered filled based on their min count (see Nodes.getChildrenMinCount).\n */\nexport const getEntityCompletionPercent = (params: {\n survey: Survey\n record: ArenaRecord\n entity: ArenaRecordNode\n}): number => toCompletionPercent(getEntityCompletionStats(params))\n\n/**\n * Returns the percentage of completion of the given entity (0-100), considering only its own attributes\n * (nested entities are ignored).\n */\nexport const getEntityOwnCompletionPercent = (params: {\n survey: Survey\n record: ArenaRecord\n entity: ArenaRecordNode\n}): number => toCompletionPercent(getEntityCompletionStats({ ...params, includeNestedEntities: false }))\n\n/**\n * Returns the percentage of completion of the given record (0-100), i.e. the completion of its root entity.\n */\nexport const getRecordCompletionPercent = (params: { survey: Survey; record: ArenaRecord }): number => {\n const { survey, record } = params\n const rootEntity = getRoot(record)\n return rootEntity ? getEntityCompletionPercent({ survey, record, entity: rootEntity }) : 0\n}\n"]}
|
|
@@ -130,9 +130,9 @@ const removeExcludedNodes = (params) => {
|
|
|
130
130
|
const { survey, record, sideEffect } = params;
|
|
131
131
|
const cycle = record.cycle;
|
|
132
132
|
const result = new RecordUpdateResult({ record });
|
|
133
|
+
// all the node defs are visited (not only the ones in the cycle): nodes of the ones not in the cycle must be removed
|
|
133
134
|
Surveys.visitDescendantsAndSelfNodeDef({
|
|
134
135
|
survey,
|
|
135
|
-
cycle,
|
|
136
136
|
nodeDef: Surveys.getNodeDefRoot({ survey }),
|
|
137
137
|
visitor: (nodeDef) => {
|
|
138
138
|
const hasAncestorCodeDefExcludedInClone = nodeDef.type === NodeDefType.code &&
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"recordCloner.js","sourceRoot":"","sources":["../../../src/record/recordCloner.ts"],"names":[],"mappings":"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","sourcesContent":["import { ArenaRecordNode, NodeValues, Nodes } from '../node'\nimport { NodeKeys, NodeMetaKeys } from '../node/node'\nimport { NodeDefCode, NodeDefType, NodeDefs } from '../nodeDef'\nimport { Survey, Surveys } from '../survey'\nimport { Dates, Objects, UUIDs } from '../utils'\nimport { Validation } from '../validation'\nimport { ArenaRecord } from './record'\nimport { RecordFixer } from './recordFixer'\nimport { RecordUpdateResult } from './recordNodesUpdater'\nimport { RecordValidations } from './recordValidations'\nimport { Records } from './records'\n\ntype OldUuidToNewUuidMap = {\n [key: string]: string\n}\n\nconst assignNewUuidsToNodes = (params: {\n record: ArenaRecord\n sideEffect: boolean\n}): { newNodeUuidsByOldUuid: OldUuidToNewUuidMap; newFileUuidsByOldUuid: OldUuidToNewUuidMap; record: ArenaRecord } => {\n const { record, sideEffect } = params\n\n // generate a new uuid for each node\n const newNodeUuidsByOldUuid: OldUuidToNewUuidMap = {}\n const newFileUuidsByOldUuid: OldUuidToNewUuidMap = {}\n\n // sort nodes before removing the id, to preserve their hierarchy (faster than comparing each meta.h property)\n const nodesArray = Records.getNodesArray(record).sort(\n (nodeA: ArenaRecordNode, nodeB: ArenaRecordNode): number =>\n Nodes.getHierarchy(nodeA).length - Nodes.getHierarchy(nodeB).length\n )\n\n const recordUpdated = sideEffect ? record : { ...record }\n\n const nodes = recordUpdated.nodes ?? {}\n const nodesUpdated = sideEffect ? nodes : { ...nodes }\n\n // new uuids generated in advance: meta.hCode can reference nodes cloned later (e.g. sibling code attributes)\n for (const node of nodesArray) {\n newNodeUuidsByOldUuid[node.uuid] = UUIDs.v4()\n }\n\n // update nodes recordUuid and properties (it does side effect on nodes)\n nodesArray.forEach((node) => {\n let nodeUpdated = sideEffect ? node : { ...node }\n nodeUpdated.recordUuid = record.uuid\n nodeUpdated.dateCreated = nodeUpdated.dateModified = Dates.nowFormattedForStorage()\n\n const oldUuid = node.uuid\n const newUuid = newNodeUuidsByOldUuid[oldUuid]\n nodeUpdated.uuid = newUuid\n // consider every node as just created node\n delete nodeUpdated.id\n nodeUpdated.created = true // this flag will be used by the RDB generator)\n\n // update internal node uuids, meta hierarchy, file uuid (if any)\n const parentUuid = node.parentUuid\n if (parentUuid) {\n nodeUpdated.parentUuid = newNodeUuidsByOldUuid[parentUuid]\n }\n const hierarchy = node.meta?.h ?? []\n if (hierarchy.length > 0) {\n const hierarchyUpdated = hierarchy.map((ancestorUuid) => newNodeUuidsByOldUuid[ancestorUuid])\n nodeUpdated = Objects.assocPath({\n obj: nodeUpdated,\n path: [NodeKeys.meta, NodeMetaKeys.h],\n value: hierarchyUpdated,\n sideEffect,\n })\n }\n const hierarchyCode = Nodes.getHierarchyCode(node)\n if (hierarchyCode.length > 0) {\n nodeUpdated = Objects.assocPath({\n obj: nodeUpdated,\n path: [NodeKeys.meta, NodeMetaKeys.hCode],\n value: hierarchyCode.map((ancestorCodeUuid) => newNodeUuidsByOldUuid[ancestorCodeUuid]),\n sideEffect,\n })\n }\n // assign new file uuid (file should be cloned elsewhere)\n const fileUuid = NodeValues.getFileUuid(node)\n if (fileUuid) {\n const newFileUuid = UUIDs.v4()\n nodeUpdated = Objects.assocPath({\n obj: nodeUpdated,\n path: [NodeKeys.value, NodeValues.ValuePropsFile.fileUuid],\n value: newFileUuid,\n })\n newFileUuidsByOldUuid[fileUuid] = newFileUuid\n }\n\n delete nodesUpdated[oldUuid]\n nodesUpdated[newUuid] = nodeUpdated\n })\n\n recordUpdated.nodes = nodesUpdated\n\n return { newNodeUuidsByOldUuid, newFileUuidsByOldUuid, record: recordUpdated }\n}\n\nconst assignNewUuidsToValidation = (params: {\n validation: Validation\n newNodeUuidsByOldUuid: OldUuidToNewUuidMap\n sideEffect: boolean\n}): Validation => {\n const { validation, newNodeUuidsByOldUuid, sideEffect } = params\n\n const validationFields = validation?.fields\n if (!validationFields) return validation\n\n const validationFieldsUpdated = sideEffect ? validationFields : { ...validationFields }\n\n Object.entries(validationFields).forEach(([oldFieldKey, validationField]) => {\n let newFieldKey\n if (oldFieldKey.startsWith(RecordValidations.prefixValidationFieldChildrenCount)) {\n const oldParentUuid = oldFieldKey.substring(\n RecordValidations.prefixValidationFieldChildrenCount.length,\n oldFieldKey.lastIndexOf('_')\n )\n const newParentUuid = newNodeUuidsByOldUuid[oldParentUuid]\n newFieldKey = oldFieldKey.replace(oldParentUuid, newParentUuid)\n } else {\n newFieldKey = newNodeUuidsByOldUuid[oldFieldKey]\n }\n if (newFieldKey) {\n validationFieldsUpdated[newFieldKey] = validationField\n }\n delete validationFieldsUpdated[oldFieldKey]\n })\n return Objects.assoc({\n obj: validation,\n prop: 'fields',\n value: validationFieldsUpdated,\n sideEffect,\n })\n}\n\nconst assignNewUuids = (params: {\n record: ArenaRecord\n sideEffect: boolean\n}): { newNodeUuidsByOldUuid: OldUuidToNewUuidMap; newFileUuidsByOldUuid: OldUuidToNewUuidMap; record: ArenaRecord } => {\n const { record, sideEffect } = params\n let recordUpdated = sideEffect ? record : { ...record }\n recordUpdated.uuid = UUIDs.v4()\n\n const {\n newNodeUuidsByOldUuid,\n newFileUuidsByOldUuid,\n record: recordUpdatedUuids,\n } = assignNewUuidsToNodes({ record: recordUpdated, sideEffect })\n\n recordUpdated = recordUpdatedUuids\n\n const { validation } = recordUpdated\n if (validation) {\n const validationUpdated = assignNewUuidsToValidation({\n validation,\n newNodeUuidsByOldUuid,\n sideEffect,\n })\n recordUpdated = Objects.assoc({ obj: recordUpdated, prop: 'validation', value: validationUpdated, sideEffect })\n }\n // re-build nodes index\n const nodes = recordUpdated.nodes!\n delete recordUpdated._nodesIndex\n delete recordUpdated.nodes\n recordUpdated = Records.addNodes(nodes, { sideEffect, sortNodes: true })(recordUpdated)\n\n return { newNodeUuidsByOldUuid, newFileUuidsByOldUuid, record: recordUpdated }\n}\n\nconst removeExcludedNodes = (params: {\n survey: Survey\n record: ArenaRecord\n sideEffect: boolean\n}): RecordUpdateResult => {\n const { survey, record, sideEffect } = params\n const cycle = record.cycle!\n const result = new RecordUpdateResult({ record })\n Surveys.visitDescendantsAndSelfNodeDef({\n survey,\n cycle,\n nodeDef: Surveys.getNodeDefRoot({ survey }),\n visitor: (nodeDef) => {\n const hasAncestorCodeDefExcludedInClone =\n nodeDef.type === NodeDefType.code &&\n Surveys.getNodeDefAncestorCodes({ survey, nodeDef: nodeDef as NodeDefCode }).some(NodeDefs.isExcludedInClone)\n\n if (\n !NodeDefs.isInCycle(cycle)(nodeDef) ||\n NodeDefs.isExcludedInClone(nodeDef) ||\n hasAncestorCodeDefExcludedInClone\n ) {\n const nodesToDelete = Records.getNodesByDefUuid(nodeDef.uuid)(result.record)\n if (nodesToDelete.length > 0) {\n const partialUpdateResult = Records.removeNodes(\n nodesToDelete.map((node) => node.uuid),\n { sideEffect }\n )(result.record)\n result.merge(partialUpdateResult)\n }\n }\n },\n })\n return result\n}\n\nconst cloneRecord = (params: { survey: Survey; record: ArenaRecord; cycleTo: string; sideEffect?: boolean }) => {\n const { survey, record, cycleTo, sideEffect = false } = params\n\n const recordUpdated = sideEffect ? record : { ...record }\n recordUpdated.cycle = cycleTo\n recordUpdated.dateCreated = recordUpdated.dateModified = Dates.nowFormattedForStorage()\n delete recordUpdated.id\n\n // delete nodes not included in clone\n const updateResult = new RecordUpdateResult({ record: recordUpdated })\n updateResult.merge(removeExcludedNodes({ survey, record: updateResult.record, sideEffect }))\n updateResult.merge(RecordFixer.insertMissingSingleNodes({ survey, record: updateResult.record, sideEffect }))\n\n // assign new UUIDs with side effect on record and nodes, faster when record is big\n const {\n newNodeUuidsByOldUuid,\n newFileUuidsByOldUuid,\n record: recordUpdatedUuids,\n } = assignNewUuids({ record: updateResult.record, sideEffect })\n\n updateResult.merge(new RecordUpdateResult({ record: recordUpdatedUuids }))\n\n return { record: updateResult.record, newNodeUuidsByOldUuid, newFileUuidsByOldUuid }\n}\n\nexport const RecordCloner = {\n cloneRecord,\n}\n"]}
|
|
1
|
+
{"version":3,"file":"recordCloner.js","sourceRoot":"","sources":["../../../src/record/recordCloner.ts"],"names":[],"mappings":"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","sourcesContent":["import { ArenaRecordNode, NodeValues, Nodes } from '../node'\nimport { NodeKeys, NodeMetaKeys } from '../node/node'\nimport { NodeDefCode, NodeDefType, NodeDefs } from '../nodeDef'\nimport { Survey, Surveys } from '../survey'\nimport { Dates, Objects, UUIDs } from '../utils'\nimport { Validation } from '../validation'\nimport { ArenaRecord } from './record'\nimport { RecordFixer } from './recordFixer'\nimport { RecordUpdateResult } from './recordNodesUpdater'\nimport { RecordValidations } from './recordValidations'\nimport { Records } from './records'\n\ntype OldUuidToNewUuidMap = {\n [key: string]: string\n}\n\nconst assignNewUuidsToNodes = (params: {\n record: ArenaRecord\n sideEffect: boolean\n}): { newNodeUuidsByOldUuid: OldUuidToNewUuidMap; newFileUuidsByOldUuid: OldUuidToNewUuidMap; record: ArenaRecord } => {\n const { record, sideEffect } = params\n\n // generate a new uuid for each node\n const newNodeUuidsByOldUuid: OldUuidToNewUuidMap = {}\n const newFileUuidsByOldUuid: OldUuidToNewUuidMap = {}\n\n // sort nodes before removing the id, to preserve their hierarchy (faster than comparing each meta.h property)\n const nodesArray = Records.getNodesArray(record).sort(\n (nodeA: ArenaRecordNode, nodeB: ArenaRecordNode): number =>\n Nodes.getHierarchy(nodeA).length - Nodes.getHierarchy(nodeB).length\n )\n\n const recordUpdated = sideEffect ? record : { ...record }\n\n const nodes = recordUpdated.nodes ?? {}\n const nodesUpdated = sideEffect ? nodes : { ...nodes }\n\n // new uuids generated in advance: meta.hCode can reference nodes cloned later (e.g. sibling code attributes)\n for (const node of nodesArray) {\n newNodeUuidsByOldUuid[node.uuid] = UUIDs.v4()\n }\n\n // update nodes recordUuid and properties (it does side effect on nodes)\n nodesArray.forEach((node) => {\n let nodeUpdated = sideEffect ? node : { ...node }\n nodeUpdated.recordUuid = record.uuid\n nodeUpdated.dateCreated = nodeUpdated.dateModified = Dates.nowFormattedForStorage()\n\n const oldUuid = node.uuid\n const newUuid = newNodeUuidsByOldUuid[oldUuid]\n nodeUpdated.uuid = newUuid\n // consider every node as just created node\n delete nodeUpdated.id\n nodeUpdated.created = true // this flag will be used by the RDB generator)\n\n // update internal node uuids, meta hierarchy, file uuid (if any)\n const parentUuid = node.parentUuid\n if (parentUuid) {\n nodeUpdated.parentUuid = newNodeUuidsByOldUuid[parentUuid]\n }\n const hierarchy = node.meta?.h ?? []\n if (hierarchy.length > 0) {\n const hierarchyUpdated = hierarchy.map((ancestorUuid) => newNodeUuidsByOldUuid[ancestorUuid])\n nodeUpdated = Objects.assocPath({\n obj: nodeUpdated,\n path: [NodeKeys.meta, NodeMetaKeys.h],\n value: hierarchyUpdated,\n sideEffect,\n })\n }\n const hierarchyCode = Nodes.getHierarchyCode(node)\n if (hierarchyCode.length > 0) {\n nodeUpdated = Objects.assocPath({\n obj: nodeUpdated,\n path: [NodeKeys.meta, NodeMetaKeys.hCode],\n value: hierarchyCode.map((ancestorCodeUuid) => newNodeUuidsByOldUuid[ancestorCodeUuid]),\n sideEffect,\n })\n }\n // assign new file uuid (file should be cloned elsewhere)\n const fileUuid = NodeValues.getFileUuid(node)\n if (fileUuid) {\n const newFileUuid = UUIDs.v4()\n nodeUpdated = Objects.assocPath({\n obj: nodeUpdated,\n path: [NodeKeys.value, NodeValues.ValuePropsFile.fileUuid],\n value: newFileUuid,\n })\n newFileUuidsByOldUuid[fileUuid] = newFileUuid\n }\n\n delete nodesUpdated[oldUuid]\n nodesUpdated[newUuid] = nodeUpdated\n })\n\n recordUpdated.nodes = nodesUpdated\n\n return { newNodeUuidsByOldUuid, newFileUuidsByOldUuid, record: recordUpdated }\n}\n\nconst assignNewUuidsToValidation = (params: {\n validation: Validation\n newNodeUuidsByOldUuid: OldUuidToNewUuidMap\n sideEffect: boolean\n}): Validation => {\n const { validation, newNodeUuidsByOldUuid, sideEffect } = params\n\n const validationFields = validation?.fields\n if (!validationFields) return validation\n\n const validationFieldsUpdated = sideEffect ? validationFields : { ...validationFields }\n\n Object.entries(validationFields).forEach(([oldFieldKey, validationField]) => {\n let newFieldKey\n if (oldFieldKey.startsWith(RecordValidations.prefixValidationFieldChildrenCount)) {\n const oldParentUuid = oldFieldKey.substring(\n RecordValidations.prefixValidationFieldChildrenCount.length,\n oldFieldKey.lastIndexOf('_')\n )\n const newParentUuid = newNodeUuidsByOldUuid[oldParentUuid]\n newFieldKey = oldFieldKey.replace(oldParentUuid, newParentUuid)\n } else {\n newFieldKey = newNodeUuidsByOldUuid[oldFieldKey]\n }\n if (newFieldKey) {\n validationFieldsUpdated[newFieldKey] = validationField\n }\n delete validationFieldsUpdated[oldFieldKey]\n })\n return Objects.assoc({\n obj: validation,\n prop: 'fields',\n value: validationFieldsUpdated,\n sideEffect,\n })\n}\n\nconst assignNewUuids = (params: {\n record: ArenaRecord\n sideEffect: boolean\n}): { newNodeUuidsByOldUuid: OldUuidToNewUuidMap; newFileUuidsByOldUuid: OldUuidToNewUuidMap; record: ArenaRecord } => {\n const { record, sideEffect } = params\n let recordUpdated = sideEffect ? record : { ...record }\n recordUpdated.uuid = UUIDs.v4()\n\n const {\n newNodeUuidsByOldUuid,\n newFileUuidsByOldUuid,\n record: recordUpdatedUuids,\n } = assignNewUuidsToNodes({ record: recordUpdated, sideEffect })\n\n recordUpdated = recordUpdatedUuids\n\n const { validation } = recordUpdated\n if (validation) {\n const validationUpdated = assignNewUuidsToValidation({\n validation,\n newNodeUuidsByOldUuid,\n sideEffect,\n })\n recordUpdated = Objects.assoc({ obj: recordUpdated, prop: 'validation', value: validationUpdated, sideEffect })\n }\n // re-build nodes index\n const nodes = recordUpdated.nodes!\n delete recordUpdated._nodesIndex\n delete recordUpdated.nodes\n recordUpdated = Records.addNodes(nodes, { sideEffect, sortNodes: true })(recordUpdated)\n\n return { newNodeUuidsByOldUuid, newFileUuidsByOldUuid, record: recordUpdated }\n}\n\nconst removeExcludedNodes = (params: {\n survey: Survey\n record: ArenaRecord\n sideEffect: boolean\n}): RecordUpdateResult => {\n const { survey, record, sideEffect } = params\n const cycle = record.cycle!\n const result = new RecordUpdateResult({ record })\n // all the node defs are visited (not only the ones in the cycle): nodes of the ones not in the cycle must be removed\n Surveys.visitDescendantsAndSelfNodeDef({\n survey,\n nodeDef: Surveys.getNodeDefRoot({ survey }),\n visitor: (nodeDef) => {\n const hasAncestorCodeDefExcludedInClone =\n nodeDef.type === NodeDefType.code &&\n Surveys.getNodeDefAncestorCodes({ survey, nodeDef: nodeDef as NodeDefCode }).some(NodeDefs.isExcludedInClone)\n\n if (\n !NodeDefs.isInCycle(cycle)(nodeDef) ||\n NodeDefs.isExcludedInClone(nodeDef) ||\n hasAncestorCodeDefExcludedInClone\n ) {\n const nodesToDelete = Records.getNodesByDefUuid(nodeDef.uuid)(result.record)\n if (nodesToDelete.length > 0) {\n const partialUpdateResult = Records.removeNodes(\n nodesToDelete.map((node) => node.uuid),\n { sideEffect }\n )(result.record)\n result.merge(partialUpdateResult)\n }\n }\n },\n })\n return result\n}\n\nconst cloneRecord = (params: { survey: Survey; record: ArenaRecord; cycleTo: string; sideEffect?: boolean }) => {\n const { survey, record, cycleTo, sideEffect = false } = params\n\n const recordUpdated = sideEffect ? record : { ...record }\n recordUpdated.cycle = cycleTo\n recordUpdated.dateCreated = recordUpdated.dateModified = Dates.nowFormattedForStorage()\n delete recordUpdated.id\n\n // delete nodes not included in clone\n const updateResult = new RecordUpdateResult({ record: recordUpdated })\n updateResult.merge(removeExcludedNodes({ survey, record: updateResult.record, sideEffect }))\n updateResult.merge(RecordFixer.insertMissingSingleNodes({ survey, record: updateResult.record, sideEffect }))\n\n // assign new UUIDs with side effect on record and nodes, faster when record is big\n const {\n newNodeUuidsByOldUuid,\n newFileUuidsByOldUuid,\n record: recordUpdatedUuids,\n } = assignNewUuids({ record: updateResult.record, sideEffect })\n\n updateResult.merge(new RecordUpdateResult({ record: recordUpdatedUuids }))\n\n return { record: updateResult.record, newNodeUuidsByOldUuid, newFileUuidsByOldUuid }\n}\n\nexport const RecordCloner = {\n cloneRecord,\n}\n"]}
|
|
@@ -45,11 +45,13 @@ const insertMissingSingleNode = (params) => {
|
|
|
45
45
|
const insertMissingSingleNodes = (params) => {
|
|
46
46
|
const { survey, record, sideEffect } = params;
|
|
47
47
|
const updateResult = new RecordUpdateResult({ record });
|
|
48
|
+
const cycle = Records.getCycle(record);
|
|
48
49
|
Surveys.visitNodeDefs({
|
|
49
50
|
survey,
|
|
50
51
|
visitor: (nodeDef) => {
|
|
51
52
|
const parentDefUuid = nodeDef.parentUuid;
|
|
52
|
-
|
|
53
|
+
// node defs not in the record cycle have no nodes
|
|
54
|
+
if (parentDefUuid && NodeDefs.isInCycle(cycle)(nodeDef)) {
|
|
53
55
|
const parentNodes = Records.getNodesByDefUuid(parentDefUuid)(updateResult.record);
|
|
54
56
|
for (const parentNode of parentNodes) {
|
|
55
57
|
const partialUpdateResult = insertMissingSingleNode({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"recordFixer.js","sourceRoot":"","sources":["../../../src/record/recordFixer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,WAAW,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAC7D,OAAO,EAAwB,QAAQ,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AACxE,OAAO,EAAU,OAAO,EAAE,MAAM,WAAW,CAAA;AAC3C,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAA;AAElC,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AACzD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAEnC,MAAM,gBAAgB,GAAG,CAAC,MAMzB,EAAmB,EAAE;IACpB,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,GAAG,MAAM,CAAA;IAC5D,IAAI,CAAC,QAAQ,CAAC,oBAAoB,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,UAAU,CAAC,KAAK,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;QAChG,8EAA8E;QAC9E,OAAO,IAAI,CAAA;IACb,CAAC;IACD,MAAM,UAAU,GAAG,OAAO,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAA;IAClD,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,wDAAwD;QACxD,OAAO,IAAI,CAAA;IACb,CAAC;IACD,8DAA8D;IAC9D,MAAM,KAAK,GAAG,OAAO,CAAC,yBAAyB,CAC7C,MAAM,EACN,UAAU,EACV,OAAO,CACR,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,aAAa,EAAE,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,CAAA;IACpD,MAAM,WAAW,GAAG,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,mBAAM,IAAI,CAAE,CAAA;IACnD,WAAW,CAAC,IAAI,mCAAQ,WAAW,CAAC,IAAI,KAAE,KAAK,GAAE,CAAA;IACjD,OAAO,WAAW,CAAA;AACpB,CAAC,CAAA;AAED,MAAM,uBAAuB,GAAG,CAAC,MAMhC,EAA6B,EAAE;IAC9B,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,GAAG,MAAM,CAAA;IAClE,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;QAChC,iCAAiC;QACjC,OAAO,IAAI,CAAA;IACb,CAAC;IACD,MAAM,WAAW,GAAG,OAAO,CAAC,IAAI,CAAA;IAChC,MAAM,QAAQ,GAAG,OAAO,CAAC,WAAW,CAAC,UAAU,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAA;IACtE,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC/B,+BAA+B;QAC/B,OAAO,IAAI,CAAA;IACb,CAAC;IACD,6BAA6B;IAC7B,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAA;IAC9B,IAAI,IAAI,GAAG,WAAW,CAAC,cAAc,CAAC,EAAE,WAAW,EAAE,UAAU,EAAE,UAAU,EAAE,CAAC,CAAA;IAE9E,IAAI,OAAO,CAAC,IAAI,KAAK,WAAW,CAAC,IAAI,EAAE,CAAC;QACtC,IAAI,GAAG,gBAAgB,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,OAAsB,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,CAAA;IAChG,CAAC;IAED,MAAM,aAAa,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC,MAAM,CAAC,CAAA;IACnE,OAAO,IAAI,kBAAkB,CAAC,EAAE,MAAM,EAAE,aAAa,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,CAAC,CAAA;AACxF,CAAC,CAAA;AAED,MAAM,wBAAwB,GAAG,CAAC,MAIjC,EAAsB,EAAE;IACvB,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,GAAG,MAAM,CAAA;IAC7C,MAAM,YAAY,GAAG,IAAI,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC,CAAA;IACvD,OAAO,CAAC,aAAa,CAAC;QACpB,MAAM;QACN,OAAO,EAAE,CAAC,OAAO,EAAE,EAAE;YACnB,MAAM,aAAa,GAAG,OAAO,CAAC,UAAU,CAAA;YACxC,IAAI,aAAa,EAAE,CAAC;gBAClB,MAAM,WAAW,GAAG,OAAO,CAAC,iBAAiB,CAAC,aAAa,CAAC,CAAC,YAAY,CAAC,MAAM,CAAC,CAAA;gBACjF,KAAK,MAAM,UAAU,IAAI,WAAW,EAAE,CAAC;oBACrC,MAAM,mBAAmB,GAAG,uBAAuB,CAAC;wBAClD,MAAM;wBACN,OAAO;wBACP,MAAM,EAAE,YAAY,CAAC,MAAM;wBAC3B,UAAU;wBACV,UAAU;qBACX,CAAC,CAAA;oBACF,IAAI,mBAAmB,EAAE,CAAC;wBACxB,YAAY,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAA;oBACzC,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;KACF,CAAC,CAAA;IACF,OAAO,YAAY,CAAA;AACrB,CAAC,CAAA;AAED,MAAM,oBAAoB,GAAG,CAAC,MAAyE,EAAE,EAAE;IACzG,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,UAAU,EAAE,GAAG,MAAM,CAAA;IAClD,MAAM,YAAY,GAAG,IAAI,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC,CAAA;IAEvD,MAAM,mBAAmB,GAAG,EAAE,UAAU,EAAE,CAAA;IAE1C,MAAM,aAAa,GAAG,OAAO,CAAC,iBAAiB,CAAC,WAAW,CAAC,CAAC,YAAY,CAAC,MAAM,CAAC,CAAA;IACjF,KAAK,MAAM,YAAY,IAAI,aAAa,EAAE,CAAC;QACzC,8BAA8B;QAC9B,MAAM,UAAU,GAAG,OAAO,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC,YAAY,CAAC,MAAM,CAAC,CAAA;QACvE,IAAI,UAAU,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC,UAAU,EAAE,WAAW,CAAC,EAAE,CAAC;YACpE,MAAM,iBAAiB,GAAG,KAAK,CAAC,wBAAwB,CAAC,UAAU,EAAE,WAAW,CAAC,CAAA;YACjF,MAAM,2BAA2B,GAAG,OAAO,CAAC,OAAO,CAAC,iBAAiB,EAAE,mBAAmB,CAAC,CAAC,YAAY,CAAC,MAAM,CAAC,CAAA;YAChH,YAAY,CAAC,KAAK,CAAC,IAAI,kBAAkB,CAAC,EAAE,MAAM,EAAE,2BAA2B,EAAE,CAAC,CAAC,CAAA;QACrF,CAAC;IACH,CAAC;IACD,MAAM,iBAAiB,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAChE,MAAM,uBAAuB,GAAG,OAAO,CAAC,WAAW,CAAC,iBAAiB,EAAE,mBAAmB,CAAC,CAAC,YAAY,CAAC,MAAM,CAAC,CAAA;IAChH,YAAY,CAAC,KAAK,CAAC,uBAAuB,CAAC,CAAA;IAE3C,OAAO,YAAY,CAAA;AACrB,CAAC,CAAA;AAED;;;;;GAKG;AACH,MAAM,SAAS,GAAG,CAAC,MAAqE,EAAsB,EAAE;IAC9G,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,GAAG,KAAK,EAAE,GAAG,MAAM,CAAA;IACrD,MAAM,MAAM,GAAG,IAAI,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC,CAAA;IAEjD,iGAAiG;IACjG,MAAM,mBAAmB,GAAG,IAAI,GAAG,EAAU,CAAA;IAC7C,KAAK,MAAM,EAAE,WAAW,EAAE,IAAI,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,EAAE,CAAC;QAC5D,IAAI,CAAC,OAAO,CAAC,iBAAiB,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC,EAAE,CAAC;YAC9D,mBAAmB,CAAC,GAAG,CAAC,WAAW,CAAC,CAAA;QACtC,CAAC;IACH,CAAC;IACD,KAAK,MAAM,WAAW,IAAI,mBAAmB,EAAE,CAAC;QAC9C,MAAM,CAAC,KAAK,CAAC,oBAAoB,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,EAAE,UAAU,EAAE,CAAC,CAAC,CAAA;IACxF,CAAC;IAED,6GAA6G;IAC7G,MAAM,YAAY,GAAuC,EAAE,CAAA;IAC3D,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;QACxD,MAAM,OAAO,GAAG,OAAO,CAAC,gBAAgB,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC,CAAA;QAC5E,sBAAsB;QACtB,IAAI,WAAW,GAAG,KAAK,CAAC,iBAAiB,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,CAAA;QAE/D,IAAI,OAAO,CAAC,IAAI,KAAK,WAAW,CAAC,IAAI,EAAE,CAAC;YACtC,WAAW,GAAG,gBAAgB,CAAC;gBAC7B,MAAM;gBACN,OAAO,EAAE,OAAsB;gBAC/B,MAAM,EAAE,MAAM,CAAC,MAAM;gBACrB,IAAI,EAAE,WAAW;gBACjB,UAAU;aACX,CAAC,CAAA;QACJ,CAAC;QACD,YAAY,CAAC,WAAW,CAAC,IAAI,CAAC,GAAG,WAAW,CAAA;IAC9C,CAAC;IACD,MAAM,sBAAsB,GAAG,OAAO,CAAC,QAAQ,CAAC,YAAY,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;IAC5F,MAAM,CAAC,KAAK,CAAC,IAAI,kBAAkB,CAAC,EAAE,MAAM,EAAE,sBAAsB,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC,CAAC,CAAA;IAE7F,MAAM,wBAAwB,GAAG,wBAAwB,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,EAAE,CAAC,CAAA;IACxG,MAAM,CAAC,KAAK,CAAC,wBAAwB,CAAC,CAAA;IACtC,OAAO,MAAM,CAAA;AACf,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,SAAS;IACT,wBAAwB;CACzB,CAAA","sourcesContent":["import { ArenaRecordNode, NodeFactory, Nodes } from '../node'\nimport { NodeDef, NodeDefCode, NodeDefs, NodeDefType } from '../nodeDef'\nimport { Survey, Surveys } from '../survey'\nimport { Objects } from '../utils'\nimport type { ArenaRecord } from './record'\nimport { RecordUpdateResult } from './recordNodesUpdater'\nimport { Records } from './records'\n\nconst fixCodeAttribute = (params: {\n survey: Survey\n nodeDef: NodeDefCode\n record: ArenaRecord\n node: ArenaRecordNode\n sideEffect: boolean\n}): ArenaRecordNode => {\n const { survey, nodeDef, record, node, sideEffect } = params\n if (!NodeDefs.getParentCodeDefUuid(nodeDef) || Objects.isNotEmpty(Nodes.getHierarchyCode(node))) {\n // nodeDef is not a code attribute or meta.hCode already populated: do nothing\n return node\n }\n const parentNode = Records.getParent(node)(record)\n if (!parentNode) {\n // missing parent node; node parentUuid could be invalid\n return node\n }\n // populate meta.hCode with ancestor code attribute node uuids\n const hCode = Records.getAncestorCodeAttributes(\n survey,\n parentNode,\n nodeDef\n )(record).map((codeAttribute) => codeAttribute.uuid)\n const nodeUpdated = sideEffect ? node : { ...node }\n nodeUpdated.meta = { ...nodeUpdated.meta, hCode }\n return nodeUpdated\n}\n\nconst insertMissingSingleNode = (params: {\n survey: Survey\n nodeDef: NodeDef<any>\n record: ArenaRecord\n parentNode: ArenaRecordNode\n sideEffect: boolean\n}): RecordUpdateResult | null => {\n const { survey, nodeDef, record, parentNode, sideEffect } = params\n if (!NodeDefs.isSingle(nodeDef)) {\n // multiple node: don't insert it\n return null\n }\n const nodeDefUuid = nodeDef.uuid\n const children = Records.getChildren(parentNode, nodeDef.uuid)(record)\n if (!Objects.isEmpty(children)) {\n // single node already inserted\n return null\n }\n // insert missing single node\n const recordUuid = record.uuid\n let node = NodeFactory.createInstance({ nodeDefUuid, recordUuid, parentNode })\n\n if (nodeDef.type === NodeDefType.code) {\n node = fixCodeAttribute({ survey, nodeDef: nodeDef as NodeDefCode, record, node, sideEffect })\n }\n\n const recordUpdated = Records.addNode(node, { sideEffect })(record)\n return new RecordUpdateResult({ record: recordUpdated, nodes: { [node.uuid]: node } })\n}\n\nconst insertMissingSingleNodes = (params: {\n survey: Survey\n record: ArenaRecord\n sideEffect: boolean\n}): RecordUpdateResult => {\n const { survey, record, sideEffect } = params\n const updateResult = new RecordUpdateResult({ record })\n Surveys.visitNodeDefs({\n survey,\n visitor: (nodeDef) => {\n const parentDefUuid = nodeDef.parentUuid\n if (parentDefUuid) {\n const parentNodes = Records.getNodesByDefUuid(parentDefUuid)(updateResult.record)\n for (const parentNode of parentNodes) {\n const partialUpdateResult = insertMissingSingleNode({\n survey,\n nodeDef,\n record: updateResult.record,\n parentNode,\n sideEffect,\n })\n if (partialUpdateResult) {\n updateResult.merge(partialUpdateResult)\n }\n }\n }\n },\n })\n return updateResult\n}\n\nconst deleteNodesByDefUuid = (params: { record: ArenaRecord; nodeDefUuid: string; sideEffect: boolean }) => {\n const { record, nodeDefUuid, sideEffect } = params\n const updateResult = new RecordUpdateResult({ record })\n\n const recordUpdateOptions = { sideEffect }\n\n const nodesToDelete = Records.getNodesByDefUuid(nodeDefUuid)(updateResult.record)\n for (const nodeToDelete of nodesToDelete) {\n // cleanup child applicability\n const parentNode = Records.getParent(nodeToDelete)(updateResult.record)\n if (parentNode && !Nodes.isChildApplicable(parentNode, nodeDefUuid)) {\n const parentNodeUpdated = Nodes.dissocChildApplicability(parentNode, nodeDefUuid)\n const recordWithParentNodeUpdated = Records.addNode(parentNodeUpdated, recordUpdateOptions)(updateResult.record)\n updateResult.merge(new RecordUpdateResult({ record: recordWithParentNodeUpdated }))\n }\n }\n const nodeUuidsToDelete = nodesToDelete.map((node) => node.uuid)\n const nodesDeleteUpdateResult = Records.removeNodes(nodeUuidsToDelete, recordUpdateOptions)(updateResult.record)\n updateResult.merge(nodesDeleteUpdateResult)\n\n return updateResult\n}\n\n/**\n * Fix a record by:\n * - inserting missing single nodes\n * - deleting nodes with non existing node defs\n * - removing status flags (created, deleted, updated) from all nodes\n */\nconst fixRecord = (params: { survey: Survey; record: ArenaRecord; sideEffect?: boolean }): RecordUpdateResult => {\n const { survey, record, sideEffect = false } = params\n const result = new RecordUpdateResult({ record })\n\n // 1. delete nodes with non existing node defs (and their descendants), once per missing node def\n const missingNodeDefUuids = new Set<string>()\n for (const { nodeDefUuid } of Records.getNodesArray(record)) {\n if (!Surveys.findNodeDefByUuid({ survey, uuid: nodeDefUuid })) {\n missingNodeDefUuids.add(nodeDefUuid)\n }\n }\n for (const nodeDefUuid of missingNodeDefUuids) {\n result.merge(deleteNodesByDefUuid({ record: result.record, nodeDefUuid, sideEffect }))\n }\n\n // 2. fix the remaining nodes (read from the updated record, without the deleted nodes); add them all at once\n const nodesUpdated: { [key: string]: ArenaRecordNode } = {}\n for (const node of Records.getNodesArray(result.record)) {\n const nodeDef = Surveys.getNodeDefByUuid({ survey, uuid: node.nodeDefUuid })\n // remove status flags\n let nodeUpdated = Nodes.removeStatusFlags({ node, sideEffect })\n\n if (nodeDef.type === NodeDefType.code) {\n nodeUpdated = fixCodeAttribute({\n survey,\n nodeDef: nodeDef as NodeDefCode,\n record: result.record,\n node: nodeUpdated,\n sideEffect,\n })\n }\n nodesUpdated[nodeUpdated.uuid] = nodeUpdated\n }\n const recordWithNodesUpdated = Records.addNodes(nodesUpdated, { sideEffect })(result.record)\n result.merge(new RecordUpdateResult({ record: recordWithNodesUpdated, nodes: nodesUpdated }))\n\n const missingNodesUpdateResult = insertMissingSingleNodes({ survey, record: result.record, sideEffect })\n result.merge(missingNodesUpdateResult)\n return result\n}\n\nexport const RecordFixer = {\n fixRecord,\n insertMissingSingleNodes,\n}\n"]}
|
|
1
|
+
{"version":3,"file":"recordFixer.js","sourceRoot":"","sources":["../../../src/record/recordFixer.ts"],"names":[],"mappings":"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","sourcesContent":["import { ArenaRecordNode, NodeFactory, Nodes } from '../node'\nimport { NodeDef, NodeDefCode, NodeDefs, NodeDefType } from '../nodeDef'\nimport { Survey, Surveys } from '../survey'\nimport { Objects } from '../utils'\nimport type { ArenaRecord } from './record'\nimport { RecordUpdateResult } from './recordNodesUpdater'\nimport { Records } from './records'\n\nconst fixCodeAttribute = (params: {\n survey: Survey\n nodeDef: NodeDefCode\n record: ArenaRecord\n node: ArenaRecordNode\n sideEffect: boolean\n}): ArenaRecordNode => {\n const { survey, nodeDef, record, node, sideEffect } = params\n if (!NodeDefs.getParentCodeDefUuid(nodeDef) || Objects.isNotEmpty(Nodes.getHierarchyCode(node))) {\n // nodeDef is not a code attribute or meta.hCode already populated: do nothing\n return node\n }\n const parentNode = Records.getParent(node)(record)\n if (!parentNode) {\n // missing parent node; node parentUuid could be invalid\n return node\n }\n // populate meta.hCode with ancestor code attribute node uuids\n const hCode = Records.getAncestorCodeAttributes(\n survey,\n parentNode,\n nodeDef\n )(record).map((codeAttribute) => codeAttribute.uuid)\n const nodeUpdated = sideEffect ? node : { ...node }\n nodeUpdated.meta = { ...nodeUpdated.meta, hCode }\n return nodeUpdated\n}\n\nconst insertMissingSingleNode = (params: {\n survey: Survey\n nodeDef: NodeDef<any>\n record: ArenaRecord\n parentNode: ArenaRecordNode\n sideEffect: boolean\n}): RecordUpdateResult | null => {\n const { survey, nodeDef, record, parentNode, sideEffect } = params\n if (!NodeDefs.isSingle(nodeDef)) {\n // multiple node: don't insert it\n return null\n }\n const nodeDefUuid = nodeDef.uuid\n const children = Records.getChildren(parentNode, nodeDef.uuid)(record)\n if (!Objects.isEmpty(children)) {\n // single node already inserted\n return null\n }\n // insert missing single node\n const recordUuid = record.uuid\n let node = NodeFactory.createInstance({ nodeDefUuid, recordUuid, parentNode })\n\n if (nodeDef.type === NodeDefType.code) {\n node = fixCodeAttribute({ survey, nodeDef: nodeDef as NodeDefCode, record, node, sideEffect })\n }\n\n const recordUpdated = Records.addNode(node, { sideEffect })(record)\n return new RecordUpdateResult({ record: recordUpdated, nodes: { [node.uuid]: node } })\n}\n\nconst insertMissingSingleNodes = (params: {\n survey: Survey\n record: ArenaRecord\n sideEffect: boolean\n}): RecordUpdateResult => {\n const { survey, record, sideEffect } = params\n const updateResult = new RecordUpdateResult({ record })\n const cycle = Records.getCycle(record)\n Surveys.visitNodeDefs({\n survey,\n visitor: (nodeDef) => {\n const parentDefUuid = nodeDef.parentUuid\n // node defs not in the record cycle have no nodes\n if (parentDefUuid && NodeDefs.isInCycle(cycle)(nodeDef)) {\n const parentNodes = Records.getNodesByDefUuid(parentDefUuid)(updateResult.record)\n for (const parentNode of parentNodes) {\n const partialUpdateResult = insertMissingSingleNode({\n survey,\n nodeDef,\n record: updateResult.record,\n parentNode,\n sideEffect,\n })\n if (partialUpdateResult) {\n updateResult.merge(partialUpdateResult)\n }\n }\n }\n },\n })\n return updateResult\n}\n\nconst deleteNodesByDefUuid = (params: { record: ArenaRecord; nodeDefUuid: string; sideEffect: boolean }) => {\n const { record, nodeDefUuid, sideEffect } = params\n const updateResult = new RecordUpdateResult({ record })\n\n const recordUpdateOptions = { sideEffect }\n\n const nodesToDelete = Records.getNodesByDefUuid(nodeDefUuid)(updateResult.record)\n for (const nodeToDelete of nodesToDelete) {\n // cleanup child applicability\n const parentNode = Records.getParent(nodeToDelete)(updateResult.record)\n if (parentNode && !Nodes.isChildApplicable(parentNode, nodeDefUuid)) {\n const parentNodeUpdated = Nodes.dissocChildApplicability(parentNode, nodeDefUuid)\n const recordWithParentNodeUpdated = Records.addNode(parentNodeUpdated, recordUpdateOptions)(updateResult.record)\n updateResult.merge(new RecordUpdateResult({ record: recordWithParentNodeUpdated }))\n }\n }\n const nodeUuidsToDelete = nodesToDelete.map((node) => node.uuid)\n const nodesDeleteUpdateResult = Records.removeNodes(nodeUuidsToDelete, recordUpdateOptions)(updateResult.record)\n updateResult.merge(nodesDeleteUpdateResult)\n\n return updateResult\n}\n\n/**\n * Fix a record by:\n * - inserting missing single nodes\n * - deleting nodes with non existing node defs\n * - removing status flags (created, deleted, updated) from all nodes\n */\nconst fixRecord = (params: { survey: Survey; record: ArenaRecord; sideEffect?: boolean }): RecordUpdateResult => {\n const { survey, record, sideEffect = false } = params\n const result = new RecordUpdateResult({ record })\n\n // 1. delete nodes with non existing node defs (and their descendants), once per missing node def\n const missingNodeDefUuids = new Set<string>()\n for (const { nodeDefUuid } of Records.getNodesArray(record)) {\n if (!Surveys.findNodeDefByUuid({ survey, uuid: nodeDefUuid })) {\n missingNodeDefUuids.add(nodeDefUuid)\n }\n }\n for (const nodeDefUuid of missingNodeDefUuids) {\n result.merge(deleteNodesByDefUuid({ record: result.record, nodeDefUuid, sideEffect }))\n }\n\n // 2. fix the remaining nodes (read from the updated record, without the deleted nodes); add them all at once\n const nodesUpdated: { [key: string]: ArenaRecordNode } = {}\n for (const node of Records.getNodesArray(result.record)) {\n const nodeDef = Surveys.getNodeDefByUuid({ survey, uuid: node.nodeDefUuid })\n // remove status flags\n let nodeUpdated = Nodes.removeStatusFlags({ node, sideEffect })\n\n if (nodeDef.type === NodeDefType.code) {\n nodeUpdated = fixCodeAttribute({\n survey,\n nodeDef: nodeDef as NodeDefCode,\n record: result.record,\n node: nodeUpdated,\n sideEffect,\n })\n }\n nodesUpdated[nodeUpdated.uuid] = nodeUpdated\n }\n const recordWithNodesUpdated = Records.addNodes(nodesUpdated, { sideEffect })(result.record)\n result.merge(new RecordUpdateResult({ record: recordWithNodesUpdated, nodes: nodesUpdated }))\n\n const missingNodesUpdateResult = insertMissingSingleNodes({ survey, record: result.record, sideEffect })\n result.merge(missingNodesUpdateResult)\n return result\n}\n\nexport const RecordFixer = {\n fixRecord,\n insertMissingSingleNodes,\n}\n"]}
|
|
@@ -12,7 +12,7 @@ import { NodeFactory, Nodes } from '../../node';
|
|
|
12
12
|
import { NodeValues } from '../../node/nodeValues';
|
|
13
13
|
import { NodeDefType, NodeDefs } from '../../nodeDef';
|
|
14
14
|
import { getNodeDefChildren, getNodeDefEnumerator } from '../../survey/surveys/nodeDefs';
|
|
15
|
-
import { getAncestorCodeAttributes } from '../_records/recordGetters';
|
|
15
|
+
import { getAncestorCodeAttributes, getCycle } from '../_records/recordGetters';
|
|
16
16
|
import { getEnumeratingCategoryItems } from '../_records/recordUtils';
|
|
17
17
|
import { getEnumeratingItemsAllowedCodes } from './recordEnumeratingItemsExpressionEvaluator';
|
|
18
18
|
import { RecordUpdateResult } from './recordUpdateResult';
|
|
@@ -103,7 +103,8 @@ export const addDescendants = (params) => __awaiter(void 0, void 0, void 0, func
|
|
|
103
103
|
const { survey, record, nodeDef } = params;
|
|
104
104
|
const updateResult = new RecordUpdateResult({ record });
|
|
105
105
|
if (NodeDefs.isEntity(nodeDef)) {
|
|
106
|
-
|
|
106
|
+
// node defs not in the record cycle have no nodes
|
|
107
|
+
const childDefs = getNodeDefChildren({ survey, nodeDef }).filter(NodeDefs.isInCycle(getCycle(record)));
|
|
107
108
|
// Add only child single nodes (it allows to apply default values)
|
|
108
109
|
for (const childDef of childDefs) {
|
|
109
110
|
// sequential: each iteration updates the record
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"recordNodesCreator.js","sourceRoot":"","sources":["../../../../src/record/recordNodesUpdater/recordNodesCreator.ts"],"names":[],"mappings":"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","sourcesContent":["import { User } from '../../auth'\nimport { SystemError, SystemErrorKeys } from '../../error'\nimport { CategoryItem } from '../../category'\nimport { ArenaRecordNode, NodeFactory, Nodes } from '../../node'\nimport { NodeValues } from '../../node/nodeValues'\nimport { NodeDef, NodeDefCode, NodeDefEntity, NodeDefType, NodeDefs } from '../../nodeDef'\nimport { CategoryItemProvider } from '../../nodeDefExpressionEvaluator/categoryItemProvider'\nimport { Survey } from '../../survey'\nimport { getNodeDefChildren, getNodeDefEnumerator } from '../../survey/surveys/nodeDefs'\nimport { getAncestorCodeAttributes } from '../_records/recordGetters'\nimport { getEnumeratingCategoryItems } from '../_records/recordUtils'\nimport { getEnumeratingItemsAllowedCodes } from './recordEnumeratingItemsExpressionEvaluator'\nimport { RecordExpressionEvaluationContext } from './recordExpressionEvaluationContext'\nimport { RecordUpdateResult } from './recordUpdateResult'\n\nexport type NodesUpdateParams = RecordExpressionEvaluationContext & {\n dateModified?: string\n clearNonApplicableValues?: boolean\n}\n\nexport type NodeCreateParams = NodesUpdateParams & {\n parentNode?: ArenaRecordNode\n nodeDef: NodeDef<any>\n createMultipleEntities?: boolean\n}\n\nconst getNodesToInsertCount = (params: { parentNode: ArenaRecordNode | undefined; nodeDef: NodeDef<any> }): number => {\n const { nodeDef, parentNode } = params\n if (!parentNode || NodeDefs.isSingle(nodeDef)) return 1\n if (nodeDef.type === NodeDefType.code) return 0 // never create nodes for multiple code attributes\n\n if (NodeDefs.isMultipleEntity(nodeDef) && !NodeDefs.isAutoCreateMinCountItems(nodeDef)) return 0\n\n const minCount = Nodes.getChildrenMinCount({ parentNode, nodeDef })\n if (Number.isNaN(minCount) || minCount <= 0) return 0\n return minCount\n}\n\nexport const createEnumeratedEntityNode = async (params: {\n user: User\n survey: Survey\n parentNode: ArenaRecordNode\n nodeDef: NodeDefEntity\n enumeratorDef: NodeDefCode\n categoryItem: CategoryItem\n updateResult: RecordUpdateResult\n sideEffect?: boolean\n}): Promise<void> => {\n const { user, survey, parentNode, nodeDef, enumeratorDef, categoryItem, updateResult, sideEffect } = params\n\n const { record } = updateResult\n\n const childUpdateResult = await addNodeAndDescendants({\n user,\n survey,\n record: updateResult.record,\n parentNode,\n nodeDef,\n sideEffect,\n })\n const enumeratorNode = Object.values(childUpdateResult.nodes).find((node) => node.nodeDefUuid === enumeratorDef.uuid)\n if (!enumeratorNode) {\n throw new SystemError(SystemErrorKeys.record.enumeratorNodeNotFound, {\n recordUuid: record.uuid,\n entityDef: NodeDefs.getName(nodeDef),\n enumerator: NodeDefs.getName(enumeratorDef),\n })\n }\n enumeratorNode.value = NodeValues.newCodeValue({ itemUuid: categoryItem.uuid })\n enumeratorNode.meta = { ...enumeratorNode.meta, defaultValueApplied: true }\n enumeratorNode.refData = { categoryItem }\n\n updateResult.merge(childUpdateResult)\n}\n\nexport const createEnumeratedEntityNodes = async (params: {\n user: User\n survey: Survey\n parentNode: ArenaRecordNode\n nodeDef: NodeDefEntity\n updateResult: RecordUpdateResult\n categoryItemProvider?: CategoryItemProvider\n sideEffect?: boolean\n}): Promise<boolean> => {\n const { user, survey, parentNode, nodeDef, updateResult, categoryItemProvider } = params\n\n const enumeratorDef = getNodeDefEnumerator({ survey, entityDef: nodeDef })\n if (!enumeratorDef) return false\n\n const allowedCodes = await getEnumeratingItemsAllowedCodes({\n survey,\n user,\n record: updateResult.record,\n entityDef: nodeDef,\n parentNode,\n })\n\n const categoryItems = await getEnumeratingCategoryItems({\n survey,\n enumeratorDef,\n parentNode,\n categoryItemProvider,\n allowedCodes,\n })(updateResult.record)\n if (categoryItems.length === 0) return false\n\n for (const categoryItem of categoryItems) {\n // sequential: each iteration updates the record\n await createEnumeratedEntityNode({ ...params, enumeratorDef, categoryItem }) // NOSONAR\n }\n return true\n}\n\nconst createChildNodesBasedOnMinCount = async (\n params: NodeCreateParams & { updateResult: RecordUpdateResult }\n): Promise<void> => {\n const { parentNode, nodeDef, updateResult, createMultipleEntities = true } = params\n\n if (NodeDefs.isMultipleEntity(nodeDef) && NodeDefs.isEnumerate(nodeDef)) {\n if (await createEnumeratedEntityNodes({ ...params, parentNode: parentNode! })) {\n return\n }\n }\n\n const nodesToInsertCount = getNodesToInsertCount({ parentNode, nodeDef })\n if (\n nodesToInsertCount === 0 ||\n (NodeDefs.isMultipleEntity(nodeDef) &&\n (!createMultipleEntities || (parentNode && !Nodes.isChildApplicable(parentNode, nodeDef.uuid))))\n ) {\n return // do nothing\n }\n for (let index = 0; index < nodesToInsertCount; index++) {\n // sequential: each iteration updates the record\n const childUpdateResult = await addNodeAndDescendants({ ...params, record: updateResult.record }) // NOSONAR\n updateResult.merge(childUpdateResult)\n }\n}\n\nexport const addDescendants = async (params: NodeCreateParams): Promise<RecordUpdateResult> => {\n const { survey, record, nodeDef } = params\n\n const updateResult = new RecordUpdateResult({ record })\n\n if (NodeDefs.isEntity(nodeDef)) {\n const childDefs = getNodeDefChildren({ survey, nodeDef })\n\n // Add only child single nodes (it allows to apply default values)\n for (const childDef of childDefs) {\n // sequential: each iteration updates the record\n await createChildNodesBasedOnMinCount({ ...params, updateResult, nodeDef: childDef }) // NOSONAR\n }\n }\n return updateResult\n}\n\nexport const addNodeAndDescendants = async (params: NodeCreateParams): Promise<RecordUpdateResult> => {\n const { survey, record, parentNode, nodeDef, sideEffect = false } = params\n\n const node = NodeFactory.createInstance({\n nodeDefUuid: nodeDef.uuid,\n recordUuid: record.uuid,\n parentNode,\n surveyUuid: survey.uuid,\n })\n if (parentNode && nodeDef.type === NodeDefType.code && NodeDefs.getParentCodeDefUuid(nodeDef as NodeDefCode)) {\n // meta.hCode indexes the node as dependent of its ancestor code attributes (cleared when they change)\n const ancestorCodeAttributes = getAncestorCodeAttributes(survey, parentNode, nodeDef as NodeDefCode)(record)\n node.meta = { ...node.meta, hCode: ancestorCodeAttributes.map((codeAttribute) => codeAttribute.uuid) }\n }\n\n const updateResult = new RecordUpdateResult({ record })\n updateResult.addNode(node, { sideEffect })\n\n // Add children if entity\n if (NodeDefs.isEntity(nodeDef)) {\n const descendantsUpdateResult = await addDescendants({\n ...params,\n record: updateResult.record,\n parentNode: node,\n })\n updateResult.merge(descendantsUpdateResult)\n }\n return updateResult\n}\n"]}
|
|
1
|
+
{"version":3,"file":"recordNodesCreator.js","sourceRoot":"","sources":["../../../../src/record/recordNodesUpdater/recordNodesCreator.ts"],"names":[],"mappings":"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","sourcesContent":["import { User } from '../../auth'\nimport { SystemError, SystemErrorKeys } from '../../error'\nimport { CategoryItem } from '../../category'\nimport { ArenaRecordNode, NodeFactory, Nodes } from '../../node'\nimport { NodeValues } from '../../node/nodeValues'\nimport { NodeDef, NodeDefCode, NodeDefEntity, NodeDefType, NodeDefs } from '../../nodeDef'\nimport { CategoryItemProvider } from '../../nodeDefExpressionEvaluator/categoryItemProvider'\nimport { Survey } from '../../survey'\nimport { getNodeDefChildren, getNodeDefEnumerator } from '../../survey/surveys/nodeDefs'\nimport { getAncestorCodeAttributes, getCycle } from '../_records/recordGetters'\nimport { getEnumeratingCategoryItems } from '../_records/recordUtils'\nimport { getEnumeratingItemsAllowedCodes } from './recordEnumeratingItemsExpressionEvaluator'\nimport { RecordExpressionEvaluationContext } from './recordExpressionEvaluationContext'\nimport { RecordUpdateResult } from './recordUpdateResult'\n\nexport type NodesUpdateParams = RecordExpressionEvaluationContext & {\n dateModified?: string\n clearNonApplicableValues?: boolean\n}\n\nexport type NodeCreateParams = NodesUpdateParams & {\n parentNode?: ArenaRecordNode\n nodeDef: NodeDef<any>\n createMultipleEntities?: boolean\n}\n\nconst getNodesToInsertCount = (params: { parentNode: ArenaRecordNode | undefined; nodeDef: NodeDef<any> }): number => {\n const { nodeDef, parentNode } = params\n if (!parentNode || NodeDefs.isSingle(nodeDef)) return 1\n if (nodeDef.type === NodeDefType.code) return 0 // never create nodes for multiple code attributes\n\n if (NodeDefs.isMultipleEntity(nodeDef) && !NodeDefs.isAutoCreateMinCountItems(nodeDef)) return 0\n\n const minCount = Nodes.getChildrenMinCount({ parentNode, nodeDef })\n if (Number.isNaN(minCount) || minCount <= 0) return 0\n return minCount\n}\n\nexport const createEnumeratedEntityNode = async (params: {\n user: User\n survey: Survey\n parentNode: ArenaRecordNode\n nodeDef: NodeDefEntity\n enumeratorDef: NodeDefCode\n categoryItem: CategoryItem\n updateResult: RecordUpdateResult\n sideEffect?: boolean\n}): Promise<void> => {\n const { user, survey, parentNode, nodeDef, enumeratorDef, categoryItem, updateResult, sideEffect } = params\n\n const { record } = updateResult\n\n const childUpdateResult = await addNodeAndDescendants({\n user,\n survey,\n record: updateResult.record,\n parentNode,\n nodeDef,\n sideEffect,\n })\n const enumeratorNode = Object.values(childUpdateResult.nodes).find((node) => node.nodeDefUuid === enumeratorDef.uuid)\n if (!enumeratorNode) {\n throw new SystemError(SystemErrorKeys.record.enumeratorNodeNotFound, {\n recordUuid: record.uuid,\n entityDef: NodeDefs.getName(nodeDef),\n enumerator: NodeDefs.getName(enumeratorDef),\n })\n }\n enumeratorNode.value = NodeValues.newCodeValue({ itemUuid: categoryItem.uuid })\n enumeratorNode.meta = { ...enumeratorNode.meta, defaultValueApplied: true }\n enumeratorNode.refData = { categoryItem }\n\n updateResult.merge(childUpdateResult)\n}\n\nexport const createEnumeratedEntityNodes = async (params: {\n user: User\n survey: Survey\n parentNode: ArenaRecordNode\n nodeDef: NodeDefEntity\n updateResult: RecordUpdateResult\n categoryItemProvider?: CategoryItemProvider\n sideEffect?: boolean\n}): Promise<boolean> => {\n const { user, survey, parentNode, nodeDef, updateResult, categoryItemProvider } = params\n\n const enumeratorDef = getNodeDefEnumerator({ survey, entityDef: nodeDef })\n if (!enumeratorDef) return false\n\n const allowedCodes = await getEnumeratingItemsAllowedCodes({\n survey,\n user,\n record: updateResult.record,\n entityDef: nodeDef,\n parentNode,\n })\n\n const categoryItems = await getEnumeratingCategoryItems({\n survey,\n enumeratorDef,\n parentNode,\n categoryItemProvider,\n allowedCodes,\n })(updateResult.record)\n if (categoryItems.length === 0) return false\n\n for (const categoryItem of categoryItems) {\n // sequential: each iteration updates the record\n await createEnumeratedEntityNode({ ...params, enumeratorDef, categoryItem }) // NOSONAR\n }\n return true\n}\n\nconst createChildNodesBasedOnMinCount = async (\n params: NodeCreateParams & { updateResult: RecordUpdateResult }\n): Promise<void> => {\n const { parentNode, nodeDef, updateResult, createMultipleEntities = true } = params\n\n if (NodeDefs.isMultipleEntity(nodeDef) && NodeDefs.isEnumerate(nodeDef)) {\n if (await createEnumeratedEntityNodes({ ...params, parentNode: parentNode! })) {\n return\n }\n }\n\n const nodesToInsertCount = getNodesToInsertCount({ parentNode, nodeDef })\n if (\n nodesToInsertCount === 0 ||\n (NodeDefs.isMultipleEntity(nodeDef) &&\n (!createMultipleEntities || (parentNode && !Nodes.isChildApplicable(parentNode, nodeDef.uuid))))\n ) {\n return // do nothing\n }\n for (let index = 0; index < nodesToInsertCount; index++) {\n // sequential: each iteration updates the record\n const childUpdateResult = await addNodeAndDescendants({ ...params, record: updateResult.record }) // NOSONAR\n updateResult.merge(childUpdateResult)\n }\n}\n\nexport const addDescendants = async (params: NodeCreateParams): Promise<RecordUpdateResult> => {\n const { survey, record, nodeDef } = params\n\n const updateResult = new RecordUpdateResult({ record })\n\n if (NodeDefs.isEntity(nodeDef)) {\n // node defs not in the record cycle have no nodes\n const childDefs = getNodeDefChildren({ survey, nodeDef }).filter(NodeDefs.isInCycle(getCycle(record)))\n\n // Add only child single nodes (it allows to apply default values)\n for (const childDef of childDefs) {\n // sequential: each iteration updates the record\n await createChildNodesBasedOnMinCount({ ...params, updateResult, nodeDef: childDef }) // NOSONAR\n }\n }\n return updateResult\n}\n\nexport const addNodeAndDescendants = async (params: NodeCreateParams): Promise<RecordUpdateResult> => {\n const { survey, record, parentNode, nodeDef, sideEffect = false } = params\n\n const node = NodeFactory.createInstance({\n nodeDefUuid: nodeDef.uuid,\n recordUuid: record.uuid,\n parentNode,\n surveyUuid: survey.uuid,\n })\n if (parentNode && nodeDef.type === NodeDefType.code && NodeDefs.getParentCodeDefUuid(nodeDef as NodeDefCode)) {\n // meta.hCode indexes the node as dependent of its ancestor code attributes (cleared when they change)\n const ancestorCodeAttributes = getAncestorCodeAttributes(survey, parentNode, nodeDef as NodeDefCode)(record)\n node.meta = { ...node.meta, hCode: ancestorCodeAttributes.map((codeAttribute) => codeAttribute.uuid) }\n }\n\n const updateResult = new RecordUpdateResult({ record })\n updateResult.addNode(node, { sideEffect })\n\n // Add children if entity\n if (NodeDefs.isEntity(nodeDef)) {\n const descendantsUpdateResult = await addDescendants({\n ...params,\n record: updateResult.record,\n parentNode: node,\n })\n updateResult.merge(descendantsUpdateResult)\n }\n return updateResult\n}\n"]}
|
|
@@ -9,6 +9,8 @@ export class FileProcessor {
|
|
|
9
9
|
// true while a chunk is being processed (resume must not start a second processing chain)
|
|
10
10
|
this.processingChunk = false;
|
|
11
11
|
this.completed = false;
|
|
12
|
+
// incremented when a processing run is stopped: callbacks of the chunks of previous runs are ignored
|
|
13
|
+
this.runId = 0;
|
|
12
14
|
this.file = file;
|
|
13
15
|
this.filePath = filePath;
|
|
14
16
|
this.chunkProcessor = chunkProcessor;
|
|
@@ -22,7 +24,14 @@ export class FileProcessor {
|
|
|
22
24
|
}
|
|
23
25
|
this.reset();
|
|
24
26
|
}
|
|
27
|
+
cancelCurrentRun() {
|
|
28
|
+
var _a;
|
|
29
|
+
this.runId += 1;
|
|
30
|
+
(_a = this.retryProcessor) === null || _a === void 0 ? void 0 : _a.stop();
|
|
31
|
+
this.retryProcessor = undefined;
|
|
32
|
+
}
|
|
25
33
|
reset() {
|
|
34
|
+
this.cancelCurrentRun();
|
|
26
35
|
this.running = false;
|
|
27
36
|
this.totalChunks = 0;
|
|
28
37
|
this.currentChunkNumber = 0;
|
|
@@ -47,14 +56,19 @@ export class FileProcessor {
|
|
|
47
56
|
return Promise.resolve(file.slice((currentChunkNumber - 1) * chunkSize, currentChunkNumber === totalChunks ? undefined : currentChunkNumber * chunkSize));
|
|
48
57
|
}
|
|
49
58
|
processNextChunk() {
|
|
50
|
-
const { chunkProcessor, currentChunkNumber, totalFileSize, totalChunks, maxTryings, retryDelay } = this;
|
|
59
|
+
const { chunkProcessor, currentChunkNumber, totalFileSize, totalChunks, maxTryings, retryDelay, runId } = this;
|
|
60
|
+
const isStale = () => runId !== this.runId;
|
|
51
61
|
this.processingChunk = true;
|
|
52
62
|
this.extractCurrentFileChunk()
|
|
53
63
|
.then((content) => {
|
|
64
|
+
if (isStale())
|
|
65
|
+
return;
|
|
54
66
|
const retryProcessor = new RetryProcessor({
|
|
55
67
|
processor: () => chunkProcessor({ chunk: currentChunkNumber, totalFileSize, totalChunks, content }),
|
|
56
68
|
onSuccess: (chunkResult) => {
|
|
57
69
|
var _a;
|
|
70
|
+
if (isStale())
|
|
71
|
+
return;
|
|
58
72
|
this.processingChunk = false;
|
|
59
73
|
if (this.currentChunkNumber < totalChunks) {
|
|
60
74
|
// move to the next chunk also when paused: resume will continue from it
|
|
@@ -71,6 +85,8 @@ export class FileProcessor {
|
|
|
71
85
|
}
|
|
72
86
|
},
|
|
73
87
|
onFail: (error) => {
|
|
88
|
+
if (isStale())
|
|
89
|
+
return;
|
|
74
90
|
this.processingChunk = false;
|
|
75
91
|
this.onFail(error);
|
|
76
92
|
this.running = false;
|
|
@@ -78,20 +94,29 @@ export class FileProcessor {
|
|
|
78
94
|
maxTryings,
|
|
79
95
|
retryDelay,
|
|
80
96
|
});
|
|
97
|
+
this.retryProcessor = retryProcessor;
|
|
81
98
|
retryProcessor.start();
|
|
82
99
|
})
|
|
83
100
|
.catch((error) => {
|
|
101
|
+
if (isStale())
|
|
102
|
+
return;
|
|
84
103
|
this.processingChunk = false;
|
|
85
104
|
this.onFail(error);
|
|
86
105
|
this.running = false;
|
|
87
106
|
});
|
|
88
107
|
}
|
|
89
108
|
start(startFromChunk = 1) {
|
|
109
|
+
// a previous run still in progress must not continue in parallel
|
|
110
|
+
this.cancelCurrentRun();
|
|
111
|
+
const { runId } = this;
|
|
112
|
+
this.processingChunk = false;
|
|
90
113
|
this.running = true;
|
|
91
114
|
this.completed = false;
|
|
92
115
|
this.currentChunkNumber = startFromChunk;
|
|
93
116
|
this.calculateFileSize()
|
|
94
117
|
.then((fileSize) => {
|
|
118
|
+
if (runId !== this.runId)
|
|
119
|
+
return;
|
|
95
120
|
this.totalFileSize = fileSize;
|
|
96
121
|
this.totalChunks = Math.ceil(fileSize / this.chunkSize);
|
|
97
122
|
if (this.totalChunks > 0) {
|
|
@@ -99,6 +124,8 @@ export class FileProcessor {
|
|
|
99
124
|
}
|
|
100
125
|
})
|
|
101
126
|
.catch((error) => {
|
|
127
|
+
if (runId !== this.runId)
|
|
128
|
+
return;
|
|
102
129
|
this.onFail(error);
|
|
103
130
|
this.running = false;
|
|
104
131
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fileProcessor.js","sourceRoot":"","sources":["../../../src/utils/fileProcessor.ts"],"names":[],"mappings":"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","sourcesContent":["import { RetryProcessor } from './retryProcessor'\n\ntype ChunkProcessor = (args: {\n chunk: number\n content: Blob | string | Uint8Array\n totalChunks: number\n totalFileSize: number\n}) => Promise<any>\n\nexport type FileProcessorConstructorArgs = {\n file?: File\n filePath?: string\n chunkProcessor: ChunkProcessor\n onError?: (error: Error) => void\n onComplete?: (result: any) => void\n chunkSize?: number\n maxTryings?: number\n /**\n * Delay (in milliseconds) before retrying a failed chunk, doubled at every further retry.\n */\n retryDelay?: number\n}\n\nexport class FileProcessor {\n static readonly defaultChunkSize = 1024 * 1024 * 10 // 10MB\n static readonly defaultMaxTryings = 5\n static readonly defaultRetryDelay = 1000 // 1s, then 2s, 4s...\n\n private readonly file?: File\n private readonly filePath?: string\n private readonly chunkProcessor: ChunkProcessor\n private readonly chunkSize: number\n private readonly maxTryings: number\n private readonly retryDelay: number\n private readonly onError?: (error: Error) => void\n private readonly onComplete?: (result?: any) => void\n\n // State properties\n protected totalFileSize: number = 0\n protected running: boolean = false\n protected totalChunks: number = 0\n protected currentChunkNumber: number = 0\n // true while a chunk is being processed (resume must not start a second processing chain)\n protected processingChunk: boolean = false\n protected completed: boolean = false\n\n constructor({\n file,\n filePath,\n chunkProcessor,\n onError,\n onComplete,\n chunkSize = FileProcessor.defaultChunkSize,\n maxTryings = FileProcessor.defaultMaxTryings,\n retryDelay = FileProcessor.defaultRetryDelay,\n }: FileProcessorConstructorArgs) {\n this.file = file\n this.filePath = filePath\n this.chunkProcessor = chunkProcessor\n this.chunkSize = chunkSize\n this.maxTryings = maxTryings\n this.retryDelay = retryDelay\n this.onError = onError\n this.onComplete = onComplete\n\n if (!this.file && !this.filePath) {\n throw new Error('File or filePath not specified')\n }\n\n this.reset()\n }\n\n protected reset(): void {\n this.running = false\n this.totalChunks = 0\n this.currentChunkNumber = 0\n this.totalFileSize = 0\n this.processingChunk = false\n this.completed = false\n }\n\n protected calculateFileSize(): Promise<number> {\n return Promise.resolve(this.file?.size ?? 0)\n }\n\n protected onFail(error: Error): void {\n this.onError?.(error)\n }\n\n protected extractCurrentFileChunk(): Promise<Blob | string | Uint8Array> {\n const { file, currentChunkNumber, totalChunks, chunkSize } = this\n if (!file) {\n return Promise.reject(new Error('File property not initialized'))\n }\n return Promise.resolve(\n file.slice(\n (currentChunkNumber - 1) * chunkSize,\n currentChunkNumber === totalChunks ? undefined : currentChunkNumber * chunkSize\n )\n )\n }\n\n protected processNextChunk(): void {\n const { chunkProcessor, currentChunkNumber, totalFileSize, totalChunks, maxTryings, retryDelay } = this\n\n this.processingChunk = true\n this.extractCurrentFileChunk()\n .then((content) => {\n const retryProcessor = new RetryProcessor<any>({\n processor: () => chunkProcessor({ chunk: currentChunkNumber, totalFileSize, totalChunks, content }),\n onSuccess: (chunkResult) => {\n this.processingChunk = false\n if (this.currentChunkNumber < totalChunks) {\n // move to the next chunk also when paused: resume will continue from it\n this.currentChunkNumber += 1\n if (this.running) {\n this.processNextChunk()\n }\n } else {\n this.running = false\n this.completed = true\n // called outside the retried processor: if it throws, the last chunk is not processed again\n this.onComplete?.(chunkResult)\n }\n },\n onFail: (error: Error) => {\n this.processingChunk = false\n this.onFail(error)\n this.running = false\n },\n maxTryings,\n retryDelay,\n })\n retryProcessor.start()\n })\n .catch((error) => {\n this.processingChunk = false\n this.onFail(error)\n this.running = false\n })\n }\n\n start(startFromChunk: number = 1): void {\n this.running = true\n this.completed = false\n this.currentChunkNumber = startFromChunk\n this.calculateFileSize()\n .then((fileSize) => {\n this.totalFileSize = fileSize\n this.totalChunks = Math.ceil(fileSize / this.chunkSize)\n if (this.totalChunks > 0) {\n this.processNextChunk()\n }\n })\n .catch((error) => {\n this.onFail(error)\n this.running = false\n })\n }\n\n stop(): void {\n this.reset()\n }\n\n pause(): void {\n this.running = false\n }\n\n resume(): void {\n if (this.running || this.completed) return\n this.running = true\n // if a chunk is still being processed, processing continues when it completes\n if (!this.processingChunk && this.totalChunks > 0) {\n this.processNextChunk()\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"fileProcessor.js","sourceRoot":"","sources":["../../../src/utils/fileProcessor.ts"],"names":[],"mappings":"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","sourcesContent":["import { RetryProcessor } from './retryProcessor'\n\ntype ChunkProcessor = (args: {\n chunk: number\n content: Blob | string | Uint8Array\n totalChunks: number\n totalFileSize: number\n}) => Promise<any>\n\nexport type FileProcessorConstructorArgs = {\n file?: File\n filePath?: string\n chunkProcessor: ChunkProcessor\n onError?: (error: Error) => void\n onComplete?: (result: any) => void\n chunkSize?: number\n maxTryings?: number\n /**\n * Delay (in milliseconds) before retrying a failed chunk, doubled at every further retry.\n */\n retryDelay?: number\n}\n\nexport class FileProcessor {\n static readonly defaultChunkSize = 1024 * 1024 * 10 // 10MB\n static readonly defaultMaxTryings = 5\n static readonly defaultRetryDelay = 1000 // 1s, then 2s, 4s...\n\n private readonly file?: File\n private readonly filePath?: string\n private readonly chunkProcessor: ChunkProcessor\n private readonly chunkSize: number\n private readonly maxTryings: number\n private readonly retryDelay: number\n private readonly onError?: (error: Error) => void\n private readonly onComplete?: (result?: any) => void\n\n // State properties\n protected totalFileSize: number = 0\n protected running: boolean = false\n protected totalChunks: number = 0\n protected currentChunkNumber: number = 0\n // true while a chunk is being processed (resume must not start a second processing chain)\n protected processingChunk: boolean = false\n protected completed: boolean = false\n // incremented when a processing run is stopped: callbacks of the chunks of previous runs are ignored\n private runId: number = 0\n private retryProcessor?: RetryProcessor<any>\n\n constructor({\n file,\n filePath,\n chunkProcessor,\n onError,\n onComplete,\n chunkSize = FileProcessor.defaultChunkSize,\n maxTryings = FileProcessor.defaultMaxTryings,\n retryDelay = FileProcessor.defaultRetryDelay,\n }: FileProcessorConstructorArgs) {\n this.file = file\n this.filePath = filePath\n this.chunkProcessor = chunkProcessor\n this.chunkSize = chunkSize\n this.maxTryings = maxTryings\n this.retryDelay = retryDelay\n this.onError = onError\n this.onComplete = onComplete\n\n if (!this.file && !this.filePath) {\n throw new Error('File or filePath not specified')\n }\n\n this.reset()\n }\n\n private cancelCurrentRun(): void {\n this.runId += 1\n this.retryProcessor?.stop()\n this.retryProcessor = undefined\n }\n\n protected reset(): void {\n this.cancelCurrentRun()\n this.running = false\n this.totalChunks = 0\n this.currentChunkNumber = 0\n this.totalFileSize = 0\n this.processingChunk = false\n this.completed = false\n }\n\n protected calculateFileSize(): Promise<number> {\n return Promise.resolve(this.file?.size ?? 0)\n }\n\n protected onFail(error: Error): void {\n this.onError?.(error)\n }\n\n protected extractCurrentFileChunk(): Promise<Blob | string | Uint8Array> {\n const { file, currentChunkNumber, totalChunks, chunkSize } = this\n if (!file) {\n return Promise.reject(new Error('File property not initialized'))\n }\n return Promise.resolve(\n file.slice(\n (currentChunkNumber - 1) * chunkSize,\n currentChunkNumber === totalChunks ? undefined : currentChunkNumber * chunkSize\n )\n )\n }\n\n protected processNextChunk(): void {\n const { chunkProcessor, currentChunkNumber, totalFileSize, totalChunks, maxTryings, retryDelay, runId } = this\n const isStale = () => runId !== this.runId\n\n this.processingChunk = true\n this.extractCurrentFileChunk()\n .then((content) => {\n if (isStale()) return\n const retryProcessor = new RetryProcessor<any>({\n processor: () => chunkProcessor({ chunk: currentChunkNumber, totalFileSize, totalChunks, content }),\n onSuccess: (chunkResult) => {\n if (isStale()) return\n this.processingChunk = false\n if (this.currentChunkNumber < totalChunks) {\n // move to the next chunk also when paused: resume will continue from it\n this.currentChunkNumber += 1\n if (this.running) {\n this.processNextChunk()\n }\n } else {\n this.running = false\n this.completed = true\n // called outside the retried processor: if it throws, the last chunk is not processed again\n this.onComplete?.(chunkResult)\n }\n },\n onFail: (error: Error) => {\n if (isStale()) return\n this.processingChunk = false\n this.onFail(error)\n this.running = false\n },\n maxTryings,\n retryDelay,\n })\n this.retryProcessor = retryProcessor\n retryProcessor.start()\n })\n .catch((error) => {\n if (isStale()) return\n this.processingChunk = false\n this.onFail(error)\n this.running = false\n })\n }\n\n start(startFromChunk: number = 1): void {\n // a previous run still in progress must not continue in parallel\n this.cancelCurrentRun()\n const { runId } = this\n this.processingChunk = false\n this.running = true\n this.completed = false\n this.currentChunkNumber = startFromChunk\n this.calculateFileSize()\n .then((fileSize) => {\n if (runId !== this.runId) return\n this.totalFileSize = fileSize\n this.totalChunks = Math.ceil(fileSize / this.chunkSize)\n if (this.totalChunks > 0) {\n this.processNextChunk()\n }\n })\n .catch((error) => {\n if (runId !== this.runId) return\n this.onFail(error)\n this.running = false\n })\n }\n\n stop(): void {\n this.reset()\n }\n\n pause(): void {\n this.running = false\n }\n\n resume(): void {\n if (this.running || this.completed) return\n this.running = true\n // if a chunk is still being processed, processing continues when it completes\n if (!this.processingChunk && this.totalChunks > 0) {\n this.processNextChunk()\n }\n }\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"retryProcessor.js","sourceRoot":"","sources":["../../../src/utils/retryProcessor.ts"],"names":[],"mappings":"AAYA,MAAM,OAAO,GAAG,CAAC,KAAc,EAAS,EAAE,CAAC,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;AAEtG,MAAM,OAAO,cAAc;IAWzB,YAAY,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,GAAG,CAAC,EAAE,UAAU,GAAG,CAAC,EAAuC;QAJjH,mBAAmB;QACX,kBAAa,GAAW,CAAC,CAAA;QACzB,YAAO,GAAY,KAAK,CAAA;QAG9B,IAAI,CAAC,SAAS,GAAG,SAAS,CAAA;QAC1B,IAAI,CAAC,SAAS,GAAG,SAAS,CAAA;QAC1B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAA;QACpB,IAAI,CAAC,UAAU,GAAG,UAAU,CAAA;QAC5B,IAAI,CAAC,UAAU,GAAG,UAAU,CAAA;IAC9B,CAAC;IAES,KAAK;QACb,IAAI,CAAC,aAAa,GAAG,CAAC,CAAA;QACtB,IAAI,CAAC,OAAO,GAAG,KAAK,CAAA;IACtB,CAAC;IAES,WAAW;QACnB,IAAI,CAAC,aAAa,IAAI,CAAC,CAAA;QAEvB,2FAA2F;QAC3F,IAAI,CAAC,SAAS,EAAE,CAAC,IAAI,CACnB,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,EACtC,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,oBAAoB,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CACrD,CAAA;IACH,CAAC;IAEO,aAAa,CAAC,MAAuB;QAC3C,IAAI,CAAC;YACH,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAA;QACxB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAA;QAC7B,CAAC;IACH,CAAC;IAEO,oBAAoB,CAAC,KAAY;QACvC,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,aAAa,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YAC3D,kCAAkC;YAClC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;YAClB,OAAM;QACR,CAAC;QACD,MAAM,KAAK,GAAG,IAAI,CAAC,UAAU,GAAG,SAAA,CAAC,EAAI,CAAC,IAAI,CAAC,aAAa,GAAG,CAAC,CAAC,CAAA,CAAA;QAC7D,IAAI,KAAK,IAAI,CAAC,EAAE,CAAC;YACf,IAAI,CAAC,WAAW,EAAE,CAAA;YAClB,OAAM;QACR,CAAC;QACD,UAAU,CAAC,GAAG,EAAE;YACd,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;gBACjB,IAAI,CAAC,WAAW,EAAE,CAAA;YACpB,CAAC;iBAAM,CAAC;gBACN,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;YACpB,CAAC;QACH,CAAC,EAAE,KAAK,CAAC,CAAA;IACX,CAAC;IAED,iBAAiB;IACjB,KAAK;QACH,IAAI,CAAC,OAAO,GAAG,IAAI,CAAA;QACnB,IAAI,CAAC,WAAW,EAAE,CAAA;IACpB,CAAC;IAED,IAAI;QACF,IAAI,CAAC,KAAK,EAAE,CAAA;IACd,CAAC;CACF","sourcesContent":["interface RetryProcessorArgs<ProcessorResult> {\n processor: () => Promise<ProcessorResult>\n onSuccess: (result: ProcessorResult) => void\n onFail: (error: Error) => void\n maxTryings?: number\n /**\n * Delay (in milliseconds) before the first retry, doubled at every further retry (exponential backoff).\n * Defaults to 0 (immediate retry).\n */\n retryDelay?: number\n}\n\nconst toError = (error: unknown): Error => (error instanceof Error ? error : new Error(String(error)))\n\nexport class RetryProcessor<ProcessorResult> {\n private readonly processor: () => Promise<ProcessorResult>\n private readonly onSuccess: (result: ProcessorResult) => void\n private readonly onFail: (error: Error) => void\n private readonly maxTryings: number\n private readonly retryDelay: number\n\n // State properties\n private currentTrying: number = 0\n private running: boolean = false\n\n constructor({ processor, onSuccess, onFail, maxTryings = 2, retryDelay = 0 }: RetryProcessorArgs<ProcessorResult>) {\n this.processor = processor\n this.onSuccess = onSuccess\n this.onFail = onFail\n this.maxTryings = maxTryings\n this.retryDelay = retryDelay\n }\n\n protected reset() {\n this.currentTrying = 0\n this.running = false\n }\n\n protected processNext(): void {\n this.currentTrying += 1\n\n // onSuccess runs outside the retried chain: if it throws, the processor must not run again\n this.processor().then(\n (result) => this.handleSuccess(result),\n (error) => this.handleProcessorError(toError(error))\n )\n }\n\n private handleSuccess(result: ProcessorResult): void {\n try {\n this.onSuccess(result)\n } catch (error) {\n this.onFail(toError(error))\n }\n }\n\n private handleProcessorError(error: Error): void {\n if (!this.running || this.currentTrying >= this.maxTryings) {\n // stopped or max attempts reached\n this.onFail(error)\n return\n }\n const delay = this.retryDelay * 2 ** (this.currentTrying - 1)\n if (delay <= 0) {\n this.processNext()\n return\n }\n setTimeout(() => {\n if (this.running) {\n this.processNext()\n } else {\n this.onFail(error)\n }\n }, delay)\n }\n\n // Public methods\n start(): void {\n this.running = true\n this.processNext()\n }\n\n stop(): void {\n this.reset()\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"retryProcessor.js","sourceRoot":"","sources":["../../../src/utils/retryProcessor.ts"],"names":[],"mappings":"AAYA,MAAM,OAAO,GAAG,CAAC,KAAc,EAAS,EAAE,CAAC,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;AAEtG,MAAM,OAAO,cAAc;IAWzB,YAAY,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,GAAG,CAAC,EAAE,UAAU,GAAG,CAAC,EAAuC;QAJjH,mBAAmB;QACX,kBAAa,GAAW,CAAC,CAAA;QACzB,YAAO,GAAY,KAAK,CAAA;QAG9B,IAAI,CAAC,SAAS,GAAG,SAAS,CAAA;QAC1B,IAAI,CAAC,SAAS,GAAG,SAAS,CAAA;QAC1B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAA;QACpB,IAAI,CAAC,UAAU,GAAG,UAAU,CAAA;QAC5B,IAAI,CAAC,UAAU,GAAG,UAAU,CAAA;IAC9B,CAAC;IAES,KAAK;QACb,IAAI,CAAC,aAAa,GAAG,CAAC,CAAA;QACtB,IAAI,CAAC,OAAO,GAAG,KAAK,CAAA;IACtB,CAAC;IAES,WAAW;QACnB,IAAI,CAAC,aAAa,IAAI,CAAC,CAAA;QAEvB,2FAA2F;QAC3F,IAAI,CAAC,SAAS,EAAE,CAAC,IAAI,CACnB,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,EACtC,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,oBAAoB,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CACrD,CAAA;IACH,CAAC;IAEO,aAAa,CAAC,MAAuB;QAC3C,IAAI,CAAC;YACH,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAA;QACxB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAA;QAC7B,CAAC;IACH,CAAC;IAEO,oBAAoB,CAAC,KAAY;QACvC,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,aAAa,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YAC3D,kCAAkC;YAClC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;YAClB,OAAM;QACR,CAAC;QACD,MAAM,KAAK,GAAG,IAAI,CAAC,UAAU,GAAG,SAAA,CAAC,EAAI,CAAC,IAAI,CAAC,aAAa,GAAG,CAAC,CAAC,CAAA,CAAA;QAC7D,IAAI,KAAK,IAAI,CAAC,EAAE,CAAC;YACf,IAAI,CAAC,WAAW,EAAE,CAAA;YAClB,OAAM;QACR,CAAC;QACD,UAAU,CAAC,GAAG,EAAE;YACd,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;gBACjB,IAAI,CAAC,WAAW,EAAE,CAAA;YACpB,CAAC;iBAAM,CAAC;gBACN,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;YACpB,CAAC;QACH,CAAC,EAAE,KAAK,CAAC,CAAA;IACX,CAAC;IAED,iBAAiB;IACjB,KAAK;QACH,IAAI,CAAC,aAAa,GAAG,CAAC,CAAA;QACtB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAA;QACnB,IAAI,CAAC,WAAW,EAAE,CAAA;IACpB,CAAC;IAED,IAAI;QACF,IAAI,CAAC,KAAK,EAAE,CAAA;IACd,CAAC;CACF","sourcesContent":["interface RetryProcessorArgs<ProcessorResult> {\n processor: () => Promise<ProcessorResult>\n onSuccess: (result: ProcessorResult) => void\n onFail: (error: Error) => void\n maxTryings?: number\n /**\n * Delay (in milliseconds) before the first retry, doubled at every further retry (exponential backoff).\n * Defaults to 0 (immediate retry).\n */\n retryDelay?: number\n}\n\nconst toError = (error: unknown): Error => (error instanceof Error ? error : new Error(String(error)))\n\nexport class RetryProcessor<ProcessorResult> {\n private readonly processor: () => Promise<ProcessorResult>\n private readonly onSuccess: (result: ProcessorResult) => void\n private readonly onFail: (error: Error) => void\n private readonly maxTryings: number\n private readonly retryDelay: number\n\n // State properties\n private currentTrying: number = 0\n private running: boolean = false\n\n constructor({ processor, onSuccess, onFail, maxTryings = 2, retryDelay = 0 }: RetryProcessorArgs<ProcessorResult>) {\n this.processor = processor\n this.onSuccess = onSuccess\n this.onFail = onFail\n this.maxTryings = maxTryings\n this.retryDelay = retryDelay\n }\n\n protected reset() {\n this.currentTrying = 0\n this.running = false\n }\n\n protected processNext(): void {\n this.currentTrying += 1\n\n // onSuccess runs outside the retried chain: if it throws, the processor must not run again\n this.processor().then(\n (result) => this.handleSuccess(result),\n (error) => this.handleProcessorError(toError(error))\n )\n }\n\n private handleSuccess(result: ProcessorResult): void {\n try {\n this.onSuccess(result)\n } catch (error) {\n this.onFail(toError(error))\n }\n }\n\n private handleProcessorError(error: Error): void {\n if (!this.running || this.currentTrying >= this.maxTryings) {\n // stopped or max attempts reached\n this.onFail(error)\n return\n }\n const delay = this.retryDelay * 2 ** (this.currentTrying - 1)\n if (delay <= 0) {\n this.processNext()\n return\n }\n setTimeout(() => {\n if (this.running) {\n this.processNext()\n } else {\n this.onFail(error)\n }\n }, delay)\n }\n\n // Public methods\n start(): void {\n this.currentTrying = 0\n this.running = true\n this.processNext()\n }\n\n stop(): void {\n this.reset()\n }\n}\n"]}
|
|
@@ -52,7 +52,7 @@ const getChildEntityCompletionStats = (params) => {
|
|
|
52
52
|
const getEntityCompletionStats = (params) => {
|
|
53
53
|
const { survey, record, entity, includeNestedEntities = true } = params;
|
|
54
54
|
const entityDef = survey_1.Surveys.getNodeDefByUuid({ survey, uuid: entity.nodeDefUuid });
|
|
55
|
-
const childDefs = survey_1.Surveys.getNodeDefChildren({ survey, nodeDef: entityDef });
|
|
55
|
+
const childDefs = survey_1.Surveys.getNodeDefChildren({ survey, nodeDef: entityDef }).filter(nodeDef_1.NodeDefs.isInCycle((0, recordGetters_1.getCycle)(record)));
|
|
56
56
|
const stats = emptyStats();
|
|
57
57
|
for (const childDef of childDefs) {
|
|
58
58
|
if (!node_1.Nodes.isChildApplicable(entity, childDef.uuid))
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"recordCompletion.js","sourceRoot":"","sources":["../../../src/record/_records/recordCompletion.ts"],"names":[],"mappings":"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","sourcesContent":["import { ArenaRecordNode, Nodes } from '../../node'\nimport { NodeDef, NodeDefProps, NodeDefs, NodeDefType } from '../../nodeDef'\nimport { Survey, Surveys } from '../../survey'\nimport { Numbers } from '../../utils'\nimport { ArenaRecord } from '../record'\nimport { getChildren, getRoot } from './recordGetters'\n\nexport interface EntityCompletionStats {\n total: number\n filled: number\n}\n\nconst emptyStats = (): EntityCompletionStats => ({ total: 0, filled: 0 })\n\nconst addStats = (statsAcc: EntityCompletionStats, statsToAdd: EntityCompletionStats): void => {\n statsAcc.total += statsToAdd.total\n statsAcc.filled += statsToAdd.filled\n}\n\nconst isAttributeRequired = (entity: ArenaRecordNode, nodeDef: NodeDef<NodeDefType, NodeDefProps>): boolean =>\n NodeDefs.isKey(nodeDef) ||\n NodeDefs.isRequired(nodeDef) ||\n (NodeDefs.isRequiredIfDefined(nodeDef) && Nodes.isChildRequired(entity, nodeDef.uuid))\n\nconst getAttributeCompletionStats = (params: {\n record: ArenaRecord\n entity: ArenaRecordNode\n nodeDef: NodeDef<NodeDefType, NodeDefProps>\n}): EntityCompletionStats => {\n const { record, entity, nodeDef } = params\n\n if (NodeDefs.isMultiple(nodeDef)) {\n const minCount = Nodes.getChildrenMinCount({ parentNode: entity, nodeDef })\n if (Number.isNaN(minCount) || minCount <= 0) return emptyStats()\n\n const filledNodesCount = getChildren(entity, nodeDef.uuid)(record).filter(Nodes.isValueNotBlank).length\n\n return { total: minCount, filled: Math.min(filledNodesCount, minCount) }\n }\n\n if (!isAttributeRequired(entity, nodeDef)) return emptyStats()\n\n const node = getChildren(entity, nodeDef.uuid)(record)[0]\n return { total: 1, filled: node && Nodes.isValueNotBlank(node) ? 1 : 0 }\n}\n\nconst getChildEntityCompletionStats = (params: {\n survey: Survey\n record: ArenaRecord\n parentEntity: ArenaRecordNode\n nodeDef: NodeDef<NodeDefType, NodeDefProps>\n}): EntityCompletionStats => {\n const { survey, record, parentEntity, nodeDef } = params\n const childEntities = getChildren(parentEntity, nodeDef.uuid)(record)\n\n if (NodeDefs.isSingle(nodeDef)) {\n const childEntity = childEntities[0]\n return childEntity ? getEntityCompletionStats({ survey, record, entity: childEntity }) : emptyStats()\n }\n\n const minCountRaw = Nodes.getChildrenMinCount({ parentNode: parentEntity, nodeDef })\n const minCount = Number.isNaN(minCountRaw) ? 0 : Math.max(minCountRaw, 0)\n // entities added beyond the min count are still expected to be complete\n const expectedCount = Math.max(minCount, childEntities.length)\n if (expectedCount === 0) return emptyStats()\n\n const stats = emptyStats()\n for (let index = 0; index < expectedCount; index++) {\n const childEntity = childEntities[index]\n // a missing repetition (below the min count) counts as a single incomplete unit\n addStats(\n stats,\n childEntity ? getEntityCompletionStats({ survey, record, entity: childEntity }) : { total: 1, filled: 0 }\n )\n }\n return stats\n}\n\nexport const getEntityCompletionStats = (params: {\n survey: Survey\n record: ArenaRecord\n entity: ArenaRecordNode\n includeNestedEntities?: boolean\n}): EntityCompletionStats => {\n const { survey, record, entity, includeNestedEntities = true } = params\n const entityDef = Surveys.getNodeDefByUuid({ survey, uuid: entity.nodeDefUuid })\n const childDefs = Surveys.getNodeDefChildren({ survey, nodeDef: entityDef })\n\n const stats = emptyStats()\n for (const childDef of childDefs) {\n if (!Nodes.isChildApplicable(entity, childDef.uuid)) continue\n\n if (NodeDefs.isEntity(childDef)) {\n if (!includeNestedEntities) continue\n addStats(stats, getChildEntityCompletionStats({ survey, record, parentEntity: entity, nodeDef: childDef }))\n } else {\n addStats(stats, getAttributeCompletionStats({ record, entity, nodeDef: childDef }))\n }\n }\n return stats\n}\n\nconst toCompletionPercent = (stats: EntityCompletionStats): number => {\n const { total, filled } = stats\n if (total <= 0) return 100\n return Numbers.roundToPrecision((filled / total) * 100, 2)\n}\n\n/**\n * Returns the percentage of completion of the given entity (0-100), considering every descendant recursively.\n * A required or key attribute is considered filled when it has a non empty value; multiple attributes and\n * entities are considered filled based on their min count (see Nodes.getChildrenMinCount).\n */\nexport const getEntityCompletionPercent = (params: {\n survey: Survey\n record: ArenaRecord\n entity: ArenaRecordNode\n}): number => toCompletionPercent(getEntityCompletionStats(params))\n\n/**\n * Returns the percentage of completion of the given entity (0-100), considering only its own attributes\n * (nested entities are ignored).\n */\nexport const getEntityOwnCompletionPercent = (params: {\n survey: Survey\n record: ArenaRecord\n entity: ArenaRecordNode\n}): number => toCompletionPercent(getEntityCompletionStats({ ...params, includeNestedEntities: false }))\n\n/**\n * Returns the percentage of completion of the given record (0-100), i.e. the completion of its root entity.\n */\nexport const getRecordCompletionPercent = (params: { survey: Survey; record: ArenaRecord }): number => {\n const { survey, record } = params\n const rootEntity = getRoot(record)\n return rootEntity ? getEntityCompletionPercent({ survey, record, entity: rootEntity }) : 0\n}\n"]}
|
|
1
|
+
{"version":3,"file":"recordCompletion.js","sourceRoot":"","sources":["../../../src/record/_records/recordCompletion.ts"],"names":[],"mappings":"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","sourcesContent":["import { ArenaRecordNode, Nodes } from '../../node'\nimport { NodeDef, NodeDefProps, NodeDefs, NodeDefType } from '../../nodeDef'\nimport { Survey, Surveys } from '../../survey'\nimport { Numbers } from '../../utils'\nimport { ArenaRecord } from '../record'\nimport { getChildren, getCycle, getRoot } from './recordGetters'\n\nexport interface EntityCompletionStats {\n total: number\n filled: number\n}\n\nconst emptyStats = (): EntityCompletionStats => ({ total: 0, filled: 0 })\n\nconst addStats = (statsAcc: EntityCompletionStats, statsToAdd: EntityCompletionStats): void => {\n statsAcc.total += statsToAdd.total\n statsAcc.filled += statsToAdd.filled\n}\n\nconst isAttributeRequired = (entity: ArenaRecordNode, nodeDef: NodeDef<NodeDefType, NodeDefProps>): boolean =>\n NodeDefs.isKey(nodeDef) ||\n NodeDefs.isRequired(nodeDef) ||\n (NodeDefs.isRequiredIfDefined(nodeDef) && Nodes.isChildRequired(entity, nodeDef.uuid))\n\nconst getAttributeCompletionStats = (params: {\n record: ArenaRecord\n entity: ArenaRecordNode\n nodeDef: NodeDef<NodeDefType, NodeDefProps>\n}): EntityCompletionStats => {\n const { record, entity, nodeDef } = params\n\n if (NodeDefs.isMultiple(nodeDef)) {\n const minCount = Nodes.getChildrenMinCount({ parentNode: entity, nodeDef })\n if (Number.isNaN(minCount) || minCount <= 0) return emptyStats()\n\n const filledNodesCount = getChildren(entity, nodeDef.uuid)(record).filter(Nodes.isValueNotBlank).length\n\n return { total: minCount, filled: Math.min(filledNodesCount, minCount) }\n }\n\n if (!isAttributeRequired(entity, nodeDef)) return emptyStats()\n\n const node = getChildren(entity, nodeDef.uuid)(record)[0]\n return { total: 1, filled: node && Nodes.isValueNotBlank(node) ? 1 : 0 }\n}\n\nconst getChildEntityCompletionStats = (params: {\n survey: Survey\n record: ArenaRecord\n parentEntity: ArenaRecordNode\n nodeDef: NodeDef<NodeDefType, NodeDefProps>\n}): EntityCompletionStats => {\n const { survey, record, parentEntity, nodeDef } = params\n const childEntities = getChildren(parentEntity, nodeDef.uuid)(record)\n\n if (NodeDefs.isSingle(nodeDef)) {\n const childEntity = childEntities[0]\n return childEntity ? getEntityCompletionStats({ survey, record, entity: childEntity }) : emptyStats()\n }\n\n const minCountRaw = Nodes.getChildrenMinCount({ parentNode: parentEntity, nodeDef })\n const minCount = Number.isNaN(minCountRaw) ? 0 : Math.max(minCountRaw, 0)\n // entities added beyond the min count are still expected to be complete\n const expectedCount = Math.max(minCount, childEntities.length)\n if (expectedCount === 0) return emptyStats()\n\n const stats = emptyStats()\n for (let index = 0; index < expectedCount; index++) {\n const childEntity = childEntities[index]\n // a missing repetition (below the min count) counts as a single incomplete unit\n addStats(\n stats,\n childEntity ? getEntityCompletionStats({ survey, record, entity: childEntity }) : { total: 1, filled: 0 }\n )\n }\n return stats\n}\n\nexport const getEntityCompletionStats = (params: {\n survey: Survey\n record: ArenaRecord\n entity: ArenaRecordNode\n includeNestedEntities?: boolean\n}): EntityCompletionStats => {\n const { survey, record, entity, includeNestedEntities = true } = params\n const entityDef = Surveys.getNodeDefByUuid({ survey, uuid: entity.nodeDefUuid })\n const childDefs = Surveys.getNodeDefChildren({ survey, nodeDef: entityDef }).filter(\n NodeDefs.isInCycle(getCycle(record))\n )\n\n const stats = emptyStats()\n for (const childDef of childDefs) {\n if (!Nodes.isChildApplicable(entity, childDef.uuid)) continue\n\n if (NodeDefs.isEntity(childDef)) {\n if (!includeNestedEntities) continue\n addStats(stats, getChildEntityCompletionStats({ survey, record, parentEntity: entity, nodeDef: childDef }))\n } else {\n addStats(stats, getAttributeCompletionStats({ record, entity, nodeDef: childDef }))\n }\n }\n return stats\n}\n\nconst toCompletionPercent = (stats: EntityCompletionStats): number => {\n const { total, filled } = stats\n if (total <= 0) return 100\n return Numbers.roundToPrecision((filled / total) * 100, 2)\n}\n\n/**\n * Returns the percentage of completion of the given entity (0-100), considering every descendant recursively.\n * A required or key attribute is considered filled when it has a non empty value; multiple attributes and\n * entities are considered filled based on their min count (see Nodes.getChildrenMinCount).\n */\nexport const getEntityCompletionPercent = (params: {\n survey: Survey\n record: ArenaRecord\n entity: ArenaRecordNode\n}): number => toCompletionPercent(getEntityCompletionStats(params))\n\n/**\n * Returns the percentage of completion of the given entity (0-100), considering only its own attributes\n * (nested entities are ignored).\n */\nexport const getEntityOwnCompletionPercent = (params: {\n survey: Survey\n record: ArenaRecord\n entity: ArenaRecordNode\n}): number => toCompletionPercent(getEntityCompletionStats({ ...params, includeNestedEntities: false }))\n\n/**\n * Returns the percentage of completion of the given record (0-100), i.e. the completion of its root entity.\n */\nexport const getRecordCompletionPercent = (params: { survey: Survey; record: ArenaRecord }): number => {\n const { survey, record } = params\n const rootEntity = getRoot(record)\n return rootEntity ? getEntityCompletionPercent({ survey, record, entity: rootEntity }) : 0\n}\n"]}
|
|
@@ -133,9 +133,9 @@ const removeExcludedNodes = (params) => {
|
|
|
133
133
|
const { survey, record, sideEffect } = params;
|
|
134
134
|
const cycle = record.cycle;
|
|
135
135
|
const result = new recordNodesUpdater_1.RecordUpdateResult({ record });
|
|
136
|
+
// all the node defs are visited (not only the ones in the cycle): nodes of the ones not in the cycle must be removed
|
|
136
137
|
survey_1.Surveys.visitDescendantsAndSelfNodeDef({
|
|
137
138
|
survey,
|
|
138
|
-
cycle,
|
|
139
139
|
nodeDef: survey_1.Surveys.getNodeDefRoot({ survey }),
|
|
140
140
|
visitor: (nodeDef) => {
|
|
141
141
|
const hasAncestorCodeDefExcludedInClone = nodeDef.type === nodeDef_1.NodeDefType.code &&
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"recordCloner.js","sourceRoot":"","sources":["../../src/record/recordCloner.ts"],"names":[],"mappings":"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","sourcesContent":["import { ArenaRecordNode, NodeValues, Nodes } from '../node'\nimport { NodeKeys, NodeMetaKeys } from '../node/node'\nimport { NodeDefCode, NodeDefType, NodeDefs } from '../nodeDef'\nimport { Survey, Surveys } from '../survey'\nimport { Dates, Objects, UUIDs } from '../utils'\nimport { Validation } from '../validation'\nimport { ArenaRecord } from './record'\nimport { RecordFixer } from './recordFixer'\nimport { RecordUpdateResult } from './recordNodesUpdater'\nimport { RecordValidations } from './recordValidations'\nimport { Records } from './records'\n\ntype OldUuidToNewUuidMap = {\n [key: string]: string\n}\n\nconst assignNewUuidsToNodes = (params: {\n record: ArenaRecord\n sideEffect: boolean\n}): { newNodeUuidsByOldUuid: OldUuidToNewUuidMap; newFileUuidsByOldUuid: OldUuidToNewUuidMap; record: ArenaRecord } => {\n const { record, sideEffect } = params\n\n // generate a new uuid for each node\n const newNodeUuidsByOldUuid: OldUuidToNewUuidMap = {}\n const newFileUuidsByOldUuid: OldUuidToNewUuidMap = {}\n\n // sort nodes before removing the id, to preserve their hierarchy (faster than comparing each meta.h property)\n const nodesArray = Records.getNodesArray(record).sort(\n (nodeA: ArenaRecordNode, nodeB: ArenaRecordNode): number =>\n Nodes.getHierarchy(nodeA).length - Nodes.getHierarchy(nodeB).length\n )\n\n const recordUpdated = sideEffect ? record : { ...record }\n\n const nodes = recordUpdated.nodes ?? {}\n const nodesUpdated = sideEffect ? nodes : { ...nodes }\n\n // new uuids generated in advance: meta.hCode can reference nodes cloned later (e.g. sibling code attributes)\n for (const node of nodesArray) {\n newNodeUuidsByOldUuid[node.uuid] = UUIDs.v4()\n }\n\n // update nodes recordUuid and properties (it does side effect on nodes)\n nodesArray.forEach((node) => {\n let nodeUpdated = sideEffect ? node : { ...node }\n nodeUpdated.recordUuid = record.uuid\n nodeUpdated.dateCreated = nodeUpdated.dateModified = Dates.nowFormattedForStorage()\n\n const oldUuid = node.uuid\n const newUuid = newNodeUuidsByOldUuid[oldUuid]\n nodeUpdated.uuid = newUuid\n // consider every node as just created node\n delete nodeUpdated.id\n nodeUpdated.created = true // this flag will be used by the RDB generator)\n\n // update internal node uuids, meta hierarchy, file uuid (if any)\n const parentUuid = node.parentUuid\n if (parentUuid) {\n nodeUpdated.parentUuid = newNodeUuidsByOldUuid[parentUuid]\n }\n const hierarchy = node.meta?.h ?? []\n if (hierarchy.length > 0) {\n const hierarchyUpdated = hierarchy.map((ancestorUuid) => newNodeUuidsByOldUuid[ancestorUuid])\n nodeUpdated = Objects.assocPath({\n obj: nodeUpdated,\n path: [NodeKeys.meta, NodeMetaKeys.h],\n value: hierarchyUpdated,\n sideEffect,\n })\n }\n const hierarchyCode = Nodes.getHierarchyCode(node)\n if (hierarchyCode.length > 0) {\n nodeUpdated = Objects.assocPath({\n obj: nodeUpdated,\n path: [NodeKeys.meta, NodeMetaKeys.hCode],\n value: hierarchyCode.map((ancestorCodeUuid) => newNodeUuidsByOldUuid[ancestorCodeUuid]),\n sideEffect,\n })\n }\n // assign new file uuid (file should be cloned elsewhere)\n const fileUuid = NodeValues.getFileUuid(node)\n if (fileUuid) {\n const newFileUuid = UUIDs.v4()\n nodeUpdated = Objects.assocPath({\n obj: nodeUpdated,\n path: [NodeKeys.value, NodeValues.ValuePropsFile.fileUuid],\n value: newFileUuid,\n })\n newFileUuidsByOldUuid[fileUuid] = newFileUuid\n }\n\n delete nodesUpdated[oldUuid]\n nodesUpdated[newUuid] = nodeUpdated\n })\n\n recordUpdated.nodes = nodesUpdated\n\n return { newNodeUuidsByOldUuid, newFileUuidsByOldUuid, record: recordUpdated }\n}\n\nconst assignNewUuidsToValidation = (params: {\n validation: Validation\n newNodeUuidsByOldUuid: OldUuidToNewUuidMap\n sideEffect: boolean\n}): Validation => {\n const { validation, newNodeUuidsByOldUuid, sideEffect } = params\n\n const validationFields = validation?.fields\n if (!validationFields) return validation\n\n const validationFieldsUpdated = sideEffect ? validationFields : { ...validationFields }\n\n Object.entries(validationFields).forEach(([oldFieldKey, validationField]) => {\n let newFieldKey\n if (oldFieldKey.startsWith(RecordValidations.prefixValidationFieldChildrenCount)) {\n const oldParentUuid = oldFieldKey.substring(\n RecordValidations.prefixValidationFieldChildrenCount.length,\n oldFieldKey.lastIndexOf('_')\n )\n const newParentUuid = newNodeUuidsByOldUuid[oldParentUuid]\n newFieldKey = oldFieldKey.replace(oldParentUuid, newParentUuid)\n } else {\n newFieldKey = newNodeUuidsByOldUuid[oldFieldKey]\n }\n if (newFieldKey) {\n validationFieldsUpdated[newFieldKey] = validationField\n }\n delete validationFieldsUpdated[oldFieldKey]\n })\n return Objects.assoc({\n obj: validation,\n prop: 'fields',\n value: validationFieldsUpdated,\n sideEffect,\n })\n}\n\nconst assignNewUuids = (params: {\n record: ArenaRecord\n sideEffect: boolean\n}): { newNodeUuidsByOldUuid: OldUuidToNewUuidMap; newFileUuidsByOldUuid: OldUuidToNewUuidMap; record: ArenaRecord } => {\n const { record, sideEffect } = params\n let recordUpdated = sideEffect ? record : { ...record }\n recordUpdated.uuid = UUIDs.v4()\n\n const {\n newNodeUuidsByOldUuid,\n newFileUuidsByOldUuid,\n record: recordUpdatedUuids,\n } = assignNewUuidsToNodes({ record: recordUpdated, sideEffect })\n\n recordUpdated = recordUpdatedUuids\n\n const { validation } = recordUpdated\n if (validation) {\n const validationUpdated = assignNewUuidsToValidation({\n validation,\n newNodeUuidsByOldUuid,\n sideEffect,\n })\n recordUpdated = Objects.assoc({ obj: recordUpdated, prop: 'validation', value: validationUpdated, sideEffect })\n }\n // re-build nodes index\n const nodes = recordUpdated.nodes!\n delete recordUpdated._nodesIndex\n delete recordUpdated.nodes\n recordUpdated = Records.addNodes(nodes, { sideEffect, sortNodes: true })(recordUpdated)\n\n return { newNodeUuidsByOldUuid, newFileUuidsByOldUuid, record: recordUpdated }\n}\n\nconst removeExcludedNodes = (params: {\n survey: Survey\n record: ArenaRecord\n sideEffect: boolean\n}): RecordUpdateResult => {\n const { survey, record, sideEffect } = params\n const cycle = record.cycle!\n const result = new RecordUpdateResult({ record })\n Surveys.visitDescendantsAndSelfNodeDef({\n survey,\n cycle,\n nodeDef: Surveys.getNodeDefRoot({ survey }),\n visitor: (nodeDef) => {\n const hasAncestorCodeDefExcludedInClone =\n nodeDef.type === NodeDefType.code &&\n Surveys.getNodeDefAncestorCodes({ survey, nodeDef: nodeDef as NodeDefCode }).some(NodeDefs.isExcludedInClone)\n\n if (\n !NodeDefs.isInCycle(cycle)(nodeDef) ||\n NodeDefs.isExcludedInClone(nodeDef) ||\n hasAncestorCodeDefExcludedInClone\n ) {\n const nodesToDelete = Records.getNodesByDefUuid(nodeDef.uuid)(result.record)\n if (nodesToDelete.length > 0) {\n const partialUpdateResult = Records.removeNodes(\n nodesToDelete.map((node) => node.uuid),\n { sideEffect }\n )(result.record)\n result.merge(partialUpdateResult)\n }\n }\n },\n })\n return result\n}\n\nconst cloneRecord = (params: { survey: Survey; record: ArenaRecord; cycleTo: string; sideEffect?: boolean }) => {\n const { survey, record, cycleTo, sideEffect = false } = params\n\n const recordUpdated = sideEffect ? record : { ...record }\n recordUpdated.cycle = cycleTo\n recordUpdated.dateCreated = recordUpdated.dateModified = Dates.nowFormattedForStorage()\n delete recordUpdated.id\n\n // delete nodes not included in clone\n const updateResult = new RecordUpdateResult({ record: recordUpdated })\n updateResult.merge(removeExcludedNodes({ survey, record: updateResult.record, sideEffect }))\n updateResult.merge(RecordFixer.insertMissingSingleNodes({ survey, record: updateResult.record, sideEffect }))\n\n // assign new UUIDs with side effect on record and nodes, faster when record is big\n const {\n newNodeUuidsByOldUuid,\n newFileUuidsByOldUuid,\n record: recordUpdatedUuids,\n } = assignNewUuids({ record: updateResult.record, sideEffect })\n\n updateResult.merge(new RecordUpdateResult({ record: recordUpdatedUuids }))\n\n return { record: updateResult.record, newNodeUuidsByOldUuid, newFileUuidsByOldUuid }\n}\n\nexport const RecordCloner = {\n cloneRecord,\n}\n"]}
|
|
1
|
+
{"version":3,"file":"recordCloner.js","sourceRoot":"","sources":["../../src/record/recordCloner.ts"],"names":[],"mappings":"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","sourcesContent":["import { ArenaRecordNode, NodeValues, Nodes } from '../node'\nimport { NodeKeys, NodeMetaKeys } from '../node/node'\nimport { NodeDefCode, NodeDefType, NodeDefs } from '../nodeDef'\nimport { Survey, Surveys } from '../survey'\nimport { Dates, Objects, UUIDs } from '../utils'\nimport { Validation } from '../validation'\nimport { ArenaRecord } from './record'\nimport { RecordFixer } from './recordFixer'\nimport { RecordUpdateResult } from './recordNodesUpdater'\nimport { RecordValidations } from './recordValidations'\nimport { Records } from './records'\n\ntype OldUuidToNewUuidMap = {\n [key: string]: string\n}\n\nconst assignNewUuidsToNodes = (params: {\n record: ArenaRecord\n sideEffect: boolean\n}): { newNodeUuidsByOldUuid: OldUuidToNewUuidMap; newFileUuidsByOldUuid: OldUuidToNewUuidMap; record: ArenaRecord } => {\n const { record, sideEffect } = params\n\n // generate a new uuid for each node\n const newNodeUuidsByOldUuid: OldUuidToNewUuidMap = {}\n const newFileUuidsByOldUuid: OldUuidToNewUuidMap = {}\n\n // sort nodes before removing the id, to preserve their hierarchy (faster than comparing each meta.h property)\n const nodesArray = Records.getNodesArray(record).sort(\n (nodeA: ArenaRecordNode, nodeB: ArenaRecordNode): number =>\n Nodes.getHierarchy(nodeA).length - Nodes.getHierarchy(nodeB).length\n )\n\n const recordUpdated = sideEffect ? record : { ...record }\n\n const nodes = recordUpdated.nodes ?? {}\n const nodesUpdated = sideEffect ? nodes : { ...nodes }\n\n // new uuids generated in advance: meta.hCode can reference nodes cloned later (e.g. sibling code attributes)\n for (const node of nodesArray) {\n newNodeUuidsByOldUuid[node.uuid] = UUIDs.v4()\n }\n\n // update nodes recordUuid and properties (it does side effect on nodes)\n nodesArray.forEach((node) => {\n let nodeUpdated = sideEffect ? node : { ...node }\n nodeUpdated.recordUuid = record.uuid\n nodeUpdated.dateCreated = nodeUpdated.dateModified = Dates.nowFormattedForStorage()\n\n const oldUuid = node.uuid\n const newUuid = newNodeUuidsByOldUuid[oldUuid]\n nodeUpdated.uuid = newUuid\n // consider every node as just created node\n delete nodeUpdated.id\n nodeUpdated.created = true // this flag will be used by the RDB generator)\n\n // update internal node uuids, meta hierarchy, file uuid (if any)\n const parentUuid = node.parentUuid\n if (parentUuid) {\n nodeUpdated.parentUuid = newNodeUuidsByOldUuid[parentUuid]\n }\n const hierarchy = node.meta?.h ?? []\n if (hierarchy.length > 0) {\n const hierarchyUpdated = hierarchy.map((ancestorUuid) => newNodeUuidsByOldUuid[ancestorUuid])\n nodeUpdated = Objects.assocPath({\n obj: nodeUpdated,\n path: [NodeKeys.meta, NodeMetaKeys.h],\n value: hierarchyUpdated,\n sideEffect,\n })\n }\n const hierarchyCode = Nodes.getHierarchyCode(node)\n if (hierarchyCode.length > 0) {\n nodeUpdated = Objects.assocPath({\n obj: nodeUpdated,\n path: [NodeKeys.meta, NodeMetaKeys.hCode],\n value: hierarchyCode.map((ancestorCodeUuid) => newNodeUuidsByOldUuid[ancestorCodeUuid]),\n sideEffect,\n })\n }\n // assign new file uuid (file should be cloned elsewhere)\n const fileUuid = NodeValues.getFileUuid(node)\n if (fileUuid) {\n const newFileUuid = UUIDs.v4()\n nodeUpdated = Objects.assocPath({\n obj: nodeUpdated,\n path: [NodeKeys.value, NodeValues.ValuePropsFile.fileUuid],\n value: newFileUuid,\n })\n newFileUuidsByOldUuid[fileUuid] = newFileUuid\n }\n\n delete nodesUpdated[oldUuid]\n nodesUpdated[newUuid] = nodeUpdated\n })\n\n recordUpdated.nodes = nodesUpdated\n\n return { newNodeUuidsByOldUuid, newFileUuidsByOldUuid, record: recordUpdated }\n}\n\nconst assignNewUuidsToValidation = (params: {\n validation: Validation\n newNodeUuidsByOldUuid: OldUuidToNewUuidMap\n sideEffect: boolean\n}): Validation => {\n const { validation, newNodeUuidsByOldUuid, sideEffect } = params\n\n const validationFields = validation?.fields\n if (!validationFields) return validation\n\n const validationFieldsUpdated = sideEffect ? validationFields : { ...validationFields }\n\n Object.entries(validationFields).forEach(([oldFieldKey, validationField]) => {\n let newFieldKey\n if (oldFieldKey.startsWith(RecordValidations.prefixValidationFieldChildrenCount)) {\n const oldParentUuid = oldFieldKey.substring(\n RecordValidations.prefixValidationFieldChildrenCount.length,\n oldFieldKey.lastIndexOf('_')\n )\n const newParentUuid = newNodeUuidsByOldUuid[oldParentUuid]\n newFieldKey = oldFieldKey.replace(oldParentUuid, newParentUuid)\n } else {\n newFieldKey = newNodeUuidsByOldUuid[oldFieldKey]\n }\n if (newFieldKey) {\n validationFieldsUpdated[newFieldKey] = validationField\n }\n delete validationFieldsUpdated[oldFieldKey]\n })\n return Objects.assoc({\n obj: validation,\n prop: 'fields',\n value: validationFieldsUpdated,\n sideEffect,\n })\n}\n\nconst assignNewUuids = (params: {\n record: ArenaRecord\n sideEffect: boolean\n}): { newNodeUuidsByOldUuid: OldUuidToNewUuidMap; newFileUuidsByOldUuid: OldUuidToNewUuidMap; record: ArenaRecord } => {\n const { record, sideEffect } = params\n let recordUpdated = sideEffect ? record : { ...record }\n recordUpdated.uuid = UUIDs.v4()\n\n const {\n newNodeUuidsByOldUuid,\n newFileUuidsByOldUuid,\n record: recordUpdatedUuids,\n } = assignNewUuidsToNodes({ record: recordUpdated, sideEffect })\n\n recordUpdated = recordUpdatedUuids\n\n const { validation } = recordUpdated\n if (validation) {\n const validationUpdated = assignNewUuidsToValidation({\n validation,\n newNodeUuidsByOldUuid,\n sideEffect,\n })\n recordUpdated = Objects.assoc({ obj: recordUpdated, prop: 'validation', value: validationUpdated, sideEffect })\n }\n // re-build nodes index\n const nodes = recordUpdated.nodes!\n delete recordUpdated._nodesIndex\n delete recordUpdated.nodes\n recordUpdated = Records.addNodes(nodes, { sideEffect, sortNodes: true })(recordUpdated)\n\n return { newNodeUuidsByOldUuid, newFileUuidsByOldUuid, record: recordUpdated }\n}\n\nconst removeExcludedNodes = (params: {\n survey: Survey\n record: ArenaRecord\n sideEffect: boolean\n}): RecordUpdateResult => {\n const { survey, record, sideEffect } = params\n const cycle = record.cycle!\n const result = new RecordUpdateResult({ record })\n // all the node defs are visited (not only the ones in the cycle): nodes of the ones not in the cycle must be removed\n Surveys.visitDescendantsAndSelfNodeDef({\n survey,\n nodeDef: Surveys.getNodeDefRoot({ survey }),\n visitor: (nodeDef) => {\n const hasAncestorCodeDefExcludedInClone =\n nodeDef.type === NodeDefType.code &&\n Surveys.getNodeDefAncestorCodes({ survey, nodeDef: nodeDef as NodeDefCode }).some(NodeDefs.isExcludedInClone)\n\n if (\n !NodeDefs.isInCycle(cycle)(nodeDef) ||\n NodeDefs.isExcludedInClone(nodeDef) ||\n hasAncestorCodeDefExcludedInClone\n ) {\n const nodesToDelete = Records.getNodesByDefUuid(nodeDef.uuid)(result.record)\n if (nodesToDelete.length > 0) {\n const partialUpdateResult = Records.removeNodes(\n nodesToDelete.map((node) => node.uuid),\n { sideEffect }\n )(result.record)\n result.merge(partialUpdateResult)\n }\n }\n },\n })\n return result\n}\n\nconst cloneRecord = (params: { survey: Survey; record: ArenaRecord; cycleTo: string; sideEffect?: boolean }) => {\n const { survey, record, cycleTo, sideEffect = false } = params\n\n const recordUpdated = sideEffect ? record : { ...record }\n recordUpdated.cycle = cycleTo\n recordUpdated.dateCreated = recordUpdated.dateModified = Dates.nowFormattedForStorage()\n delete recordUpdated.id\n\n // delete nodes not included in clone\n const updateResult = new RecordUpdateResult({ record: recordUpdated })\n updateResult.merge(removeExcludedNodes({ survey, record: updateResult.record, sideEffect }))\n updateResult.merge(RecordFixer.insertMissingSingleNodes({ survey, record: updateResult.record, sideEffect }))\n\n // assign new UUIDs with side effect on record and nodes, faster when record is big\n const {\n newNodeUuidsByOldUuid,\n newFileUuidsByOldUuid,\n record: recordUpdatedUuids,\n } = assignNewUuids({ record: updateResult.record, sideEffect })\n\n updateResult.merge(new RecordUpdateResult({ record: recordUpdatedUuids }))\n\n return { record: updateResult.record, newNodeUuidsByOldUuid, newFileUuidsByOldUuid }\n}\n\nexport const RecordCloner = {\n cloneRecord,\n}\n"]}
|
|
@@ -48,11 +48,13 @@ const insertMissingSingleNode = (params) => {
|
|
|
48
48
|
const insertMissingSingleNodes = (params) => {
|
|
49
49
|
const { survey, record, sideEffect } = params;
|
|
50
50
|
const updateResult = new recordNodesUpdater_1.RecordUpdateResult({ record });
|
|
51
|
+
const cycle = records_1.Records.getCycle(record);
|
|
51
52
|
survey_1.Surveys.visitNodeDefs({
|
|
52
53
|
survey,
|
|
53
54
|
visitor: (nodeDef) => {
|
|
54
55
|
const parentDefUuid = nodeDef.parentUuid;
|
|
55
|
-
|
|
56
|
+
// node defs not in the record cycle have no nodes
|
|
57
|
+
if (parentDefUuid && nodeDef_1.NodeDefs.isInCycle(cycle)(nodeDef)) {
|
|
56
58
|
const parentNodes = records_1.Records.getNodesByDefUuid(parentDefUuid)(updateResult.record);
|
|
57
59
|
for (const parentNode of parentNodes) {
|
|
58
60
|
const partialUpdateResult = insertMissingSingleNode({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"recordFixer.js","sourceRoot":"","sources":["../../src/record/recordFixer.ts"],"names":[],"mappings":"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","sourcesContent":["import { ArenaRecordNode, NodeFactory, Nodes } from '../node'\nimport { NodeDef, NodeDefCode, NodeDefs, NodeDefType } from '../nodeDef'\nimport { Survey, Surveys } from '../survey'\nimport { Objects } from '../utils'\nimport type { ArenaRecord } from './record'\nimport { RecordUpdateResult } from './recordNodesUpdater'\nimport { Records } from './records'\n\nconst fixCodeAttribute = (params: {\n survey: Survey\n nodeDef: NodeDefCode\n record: ArenaRecord\n node: ArenaRecordNode\n sideEffect: boolean\n}): ArenaRecordNode => {\n const { survey, nodeDef, record, node, sideEffect } = params\n if (!NodeDefs.getParentCodeDefUuid(nodeDef) || Objects.isNotEmpty(Nodes.getHierarchyCode(node))) {\n // nodeDef is not a code attribute or meta.hCode already populated: do nothing\n return node\n }\n const parentNode = Records.getParent(node)(record)\n if (!parentNode) {\n // missing parent node; node parentUuid could be invalid\n return node\n }\n // populate meta.hCode with ancestor code attribute node uuids\n const hCode = Records.getAncestorCodeAttributes(\n survey,\n parentNode,\n nodeDef\n )(record).map((codeAttribute) => codeAttribute.uuid)\n const nodeUpdated = sideEffect ? node : { ...node }\n nodeUpdated.meta = { ...nodeUpdated.meta, hCode }\n return nodeUpdated\n}\n\nconst insertMissingSingleNode = (params: {\n survey: Survey\n nodeDef: NodeDef<any>\n record: ArenaRecord\n parentNode: ArenaRecordNode\n sideEffect: boolean\n}): RecordUpdateResult | null => {\n const { survey, nodeDef, record, parentNode, sideEffect } = params\n if (!NodeDefs.isSingle(nodeDef)) {\n // multiple node: don't insert it\n return null\n }\n const nodeDefUuid = nodeDef.uuid\n const children = Records.getChildren(parentNode, nodeDef.uuid)(record)\n if (!Objects.isEmpty(children)) {\n // single node already inserted\n return null\n }\n // insert missing single node\n const recordUuid = record.uuid\n let node = NodeFactory.createInstance({ nodeDefUuid, recordUuid, parentNode })\n\n if (nodeDef.type === NodeDefType.code) {\n node = fixCodeAttribute({ survey, nodeDef: nodeDef as NodeDefCode, record, node, sideEffect })\n }\n\n const recordUpdated = Records.addNode(node, { sideEffect })(record)\n return new RecordUpdateResult({ record: recordUpdated, nodes: { [node.uuid]: node } })\n}\n\nconst insertMissingSingleNodes = (params: {\n survey: Survey\n record: ArenaRecord\n sideEffect: boolean\n}): RecordUpdateResult => {\n const { survey, record, sideEffect } = params\n const updateResult = new RecordUpdateResult({ record })\n Surveys.visitNodeDefs({\n survey,\n visitor: (nodeDef) => {\n const parentDefUuid = nodeDef.parentUuid\n if (parentDefUuid) {\n const parentNodes = Records.getNodesByDefUuid(parentDefUuid)(updateResult.record)\n for (const parentNode of parentNodes) {\n const partialUpdateResult = insertMissingSingleNode({\n survey,\n nodeDef,\n record: updateResult.record,\n parentNode,\n sideEffect,\n })\n if (partialUpdateResult) {\n updateResult.merge(partialUpdateResult)\n }\n }\n }\n },\n })\n return updateResult\n}\n\nconst deleteNodesByDefUuid = (params: { record: ArenaRecord; nodeDefUuid: string; sideEffect: boolean }) => {\n const { record, nodeDefUuid, sideEffect } = params\n const updateResult = new RecordUpdateResult({ record })\n\n const recordUpdateOptions = { sideEffect }\n\n const nodesToDelete = Records.getNodesByDefUuid(nodeDefUuid)(updateResult.record)\n for (const nodeToDelete of nodesToDelete) {\n // cleanup child applicability\n const parentNode = Records.getParent(nodeToDelete)(updateResult.record)\n if (parentNode && !Nodes.isChildApplicable(parentNode, nodeDefUuid)) {\n const parentNodeUpdated = Nodes.dissocChildApplicability(parentNode, nodeDefUuid)\n const recordWithParentNodeUpdated = Records.addNode(parentNodeUpdated, recordUpdateOptions)(updateResult.record)\n updateResult.merge(new RecordUpdateResult({ record: recordWithParentNodeUpdated }))\n }\n }\n const nodeUuidsToDelete = nodesToDelete.map((node) => node.uuid)\n const nodesDeleteUpdateResult = Records.removeNodes(nodeUuidsToDelete, recordUpdateOptions)(updateResult.record)\n updateResult.merge(nodesDeleteUpdateResult)\n\n return updateResult\n}\n\n/**\n * Fix a record by:\n * - inserting missing single nodes\n * - deleting nodes with non existing node defs\n * - removing status flags (created, deleted, updated) from all nodes\n */\nconst fixRecord = (params: { survey: Survey; record: ArenaRecord; sideEffect?: boolean }): RecordUpdateResult => {\n const { survey, record, sideEffect = false } = params\n const result = new RecordUpdateResult({ record })\n\n // 1. delete nodes with non existing node defs (and their descendants), once per missing node def\n const missingNodeDefUuids = new Set<string>()\n for (const { nodeDefUuid } of Records.getNodesArray(record)) {\n if (!Surveys.findNodeDefByUuid({ survey, uuid: nodeDefUuid })) {\n missingNodeDefUuids.add(nodeDefUuid)\n }\n }\n for (const nodeDefUuid of missingNodeDefUuids) {\n result.merge(deleteNodesByDefUuid({ record: result.record, nodeDefUuid, sideEffect }))\n }\n\n // 2. fix the remaining nodes (read from the updated record, without the deleted nodes); add them all at once\n const nodesUpdated: { [key: string]: ArenaRecordNode } = {}\n for (const node of Records.getNodesArray(result.record)) {\n const nodeDef = Surveys.getNodeDefByUuid({ survey, uuid: node.nodeDefUuid })\n // remove status flags\n let nodeUpdated = Nodes.removeStatusFlags({ node, sideEffect })\n\n if (nodeDef.type === NodeDefType.code) {\n nodeUpdated = fixCodeAttribute({\n survey,\n nodeDef: nodeDef as NodeDefCode,\n record: result.record,\n node: nodeUpdated,\n sideEffect,\n })\n }\n nodesUpdated[nodeUpdated.uuid] = nodeUpdated\n }\n const recordWithNodesUpdated = Records.addNodes(nodesUpdated, { sideEffect })(result.record)\n result.merge(new RecordUpdateResult({ record: recordWithNodesUpdated, nodes: nodesUpdated }))\n\n const missingNodesUpdateResult = insertMissingSingleNodes({ survey, record: result.record, sideEffect })\n result.merge(missingNodesUpdateResult)\n return result\n}\n\nexport const RecordFixer = {\n fixRecord,\n insertMissingSingleNodes,\n}\n"]}
|
|
1
|
+
{"version":3,"file":"recordFixer.js","sourceRoot":"","sources":["../../src/record/recordFixer.ts"],"names":[],"mappings":"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","sourcesContent":["import { ArenaRecordNode, NodeFactory, Nodes } from '../node'\nimport { NodeDef, NodeDefCode, NodeDefs, NodeDefType } from '../nodeDef'\nimport { Survey, Surveys } from '../survey'\nimport { Objects } from '../utils'\nimport type { ArenaRecord } from './record'\nimport { RecordUpdateResult } from './recordNodesUpdater'\nimport { Records } from './records'\n\nconst fixCodeAttribute = (params: {\n survey: Survey\n nodeDef: NodeDefCode\n record: ArenaRecord\n node: ArenaRecordNode\n sideEffect: boolean\n}): ArenaRecordNode => {\n const { survey, nodeDef, record, node, sideEffect } = params\n if (!NodeDefs.getParentCodeDefUuid(nodeDef) || Objects.isNotEmpty(Nodes.getHierarchyCode(node))) {\n // nodeDef is not a code attribute or meta.hCode already populated: do nothing\n return node\n }\n const parentNode = Records.getParent(node)(record)\n if (!parentNode) {\n // missing parent node; node parentUuid could be invalid\n return node\n }\n // populate meta.hCode with ancestor code attribute node uuids\n const hCode = Records.getAncestorCodeAttributes(\n survey,\n parentNode,\n nodeDef\n )(record).map((codeAttribute) => codeAttribute.uuid)\n const nodeUpdated = sideEffect ? node : { ...node }\n nodeUpdated.meta = { ...nodeUpdated.meta, hCode }\n return nodeUpdated\n}\n\nconst insertMissingSingleNode = (params: {\n survey: Survey\n nodeDef: NodeDef<any>\n record: ArenaRecord\n parentNode: ArenaRecordNode\n sideEffect: boolean\n}): RecordUpdateResult | null => {\n const { survey, nodeDef, record, parentNode, sideEffect } = params\n if (!NodeDefs.isSingle(nodeDef)) {\n // multiple node: don't insert it\n return null\n }\n const nodeDefUuid = nodeDef.uuid\n const children = Records.getChildren(parentNode, nodeDef.uuid)(record)\n if (!Objects.isEmpty(children)) {\n // single node already inserted\n return null\n }\n // insert missing single node\n const recordUuid = record.uuid\n let node = NodeFactory.createInstance({ nodeDefUuid, recordUuid, parentNode })\n\n if (nodeDef.type === NodeDefType.code) {\n node = fixCodeAttribute({ survey, nodeDef: nodeDef as NodeDefCode, record, node, sideEffect })\n }\n\n const recordUpdated = Records.addNode(node, { sideEffect })(record)\n return new RecordUpdateResult({ record: recordUpdated, nodes: { [node.uuid]: node } })\n}\n\nconst insertMissingSingleNodes = (params: {\n survey: Survey\n record: ArenaRecord\n sideEffect: boolean\n}): RecordUpdateResult => {\n const { survey, record, sideEffect } = params\n const updateResult = new RecordUpdateResult({ record })\n const cycle = Records.getCycle(record)\n Surveys.visitNodeDefs({\n survey,\n visitor: (nodeDef) => {\n const parentDefUuid = nodeDef.parentUuid\n // node defs not in the record cycle have no nodes\n if (parentDefUuid && NodeDefs.isInCycle(cycle)(nodeDef)) {\n const parentNodes = Records.getNodesByDefUuid(parentDefUuid)(updateResult.record)\n for (const parentNode of parentNodes) {\n const partialUpdateResult = insertMissingSingleNode({\n survey,\n nodeDef,\n record: updateResult.record,\n parentNode,\n sideEffect,\n })\n if (partialUpdateResult) {\n updateResult.merge(partialUpdateResult)\n }\n }\n }\n },\n })\n return updateResult\n}\n\nconst deleteNodesByDefUuid = (params: { record: ArenaRecord; nodeDefUuid: string; sideEffect: boolean }) => {\n const { record, nodeDefUuid, sideEffect } = params\n const updateResult = new RecordUpdateResult({ record })\n\n const recordUpdateOptions = { sideEffect }\n\n const nodesToDelete = Records.getNodesByDefUuid(nodeDefUuid)(updateResult.record)\n for (const nodeToDelete of nodesToDelete) {\n // cleanup child applicability\n const parentNode = Records.getParent(nodeToDelete)(updateResult.record)\n if (parentNode && !Nodes.isChildApplicable(parentNode, nodeDefUuid)) {\n const parentNodeUpdated = Nodes.dissocChildApplicability(parentNode, nodeDefUuid)\n const recordWithParentNodeUpdated = Records.addNode(parentNodeUpdated, recordUpdateOptions)(updateResult.record)\n updateResult.merge(new RecordUpdateResult({ record: recordWithParentNodeUpdated }))\n }\n }\n const nodeUuidsToDelete = nodesToDelete.map((node) => node.uuid)\n const nodesDeleteUpdateResult = Records.removeNodes(nodeUuidsToDelete, recordUpdateOptions)(updateResult.record)\n updateResult.merge(nodesDeleteUpdateResult)\n\n return updateResult\n}\n\n/**\n * Fix a record by:\n * - inserting missing single nodes\n * - deleting nodes with non existing node defs\n * - removing status flags (created, deleted, updated) from all nodes\n */\nconst fixRecord = (params: { survey: Survey; record: ArenaRecord; sideEffect?: boolean }): RecordUpdateResult => {\n const { survey, record, sideEffect = false } = params\n const result = new RecordUpdateResult({ record })\n\n // 1. delete nodes with non existing node defs (and their descendants), once per missing node def\n const missingNodeDefUuids = new Set<string>()\n for (const { nodeDefUuid } of Records.getNodesArray(record)) {\n if (!Surveys.findNodeDefByUuid({ survey, uuid: nodeDefUuid })) {\n missingNodeDefUuids.add(nodeDefUuid)\n }\n }\n for (const nodeDefUuid of missingNodeDefUuids) {\n result.merge(deleteNodesByDefUuid({ record: result.record, nodeDefUuid, sideEffect }))\n }\n\n // 2. fix the remaining nodes (read from the updated record, without the deleted nodes); add them all at once\n const nodesUpdated: { [key: string]: ArenaRecordNode } = {}\n for (const node of Records.getNodesArray(result.record)) {\n const nodeDef = Surveys.getNodeDefByUuid({ survey, uuid: node.nodeDefUuid })\n // remove status flags\n let nodeUpdated = Nodes.removeStatusFlags({ node, sideEffect })\n\n if (nodeDef.type === NodeDefType.code) {\n nodeUpdated = fixCodeAttribute({\n survey,\n nodeDef: nodeDef as NodeDefCode,\n record: result.record,\n node: nodeUpdated,\n sideEffect,\n })\n }\n nodesUpdated[nodeUpdated.uuid] = nodeUpdated\n }\n const recordWithNodesUpdated = Records.addNodes(nodesUpdated, { sideEffect })(result.record)\n result.merge(new RecordUpdateResult({ record: recordWithNodesUpdated, nodes: nodesUpdated }))\n\n const missingNodesUpdateResult = insertMissingSingleNodes({ survey, record: result.record, sideEffect })\n result.merge(missingNodesUpdateResult)\n return result\n}\n\nexport const RecordFixer = {\n fixRecord,\n insertMissingSingleNodes,\n}\n"]}
|
|
@@ -108,7 +108,8 @@ const addDescendants = (params) => __awaiter(void 0, void 0, void 0, function* (
|
|
|
108
108
|
const { survey, record, nodeDef } = params;
|
|
109
109
|
const updateResult = new recordUpdateResult_1.RecordUpdateResult({ record });
|
|
110
110
|
if (nodeDef_1.NodeDefs.isEntity(nodeDef)) {
|
|
111
|
-
|
|
111
|
+
// node defs not in the record cycle have no nodes
|
|
112
|
+
const childDefs = (0, nodeDefs_1.getNodeDefChildren)({ survey, nodeDef }).filter(nodeDef_1.NodeDefs.isInCycle((0, recordGetters_1.getCycle)(record)));
|
|
112
113
|
// Add only child single nodes (it allows to apply default values)
|
|
113
114
|
for (const childDef of childDefs) {
|
|
114
115
|
// sequential: each iteration updates the record
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"recordNodesCreator.js","sourceRoot":"","sources":["../../../src/record/recordNodesUpdater/recordNodesCreator.ts"],"names":[],"mappings":"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","sourcesContent":["import { User } from '../../auth'\nimport { SystemError, SystemErrorKeys } from '../../error'\nimport { CategoryItem } from '../../category'\nimport { ArenaRecordNode, NodeFactory, Nodes } from '../../node'\nimport { NodeValues } from '../../node/nodeValues'\nimport { NodeDef, NodeDefCode, NodeDefEntity, NodeDefType, NodeDefs } from '../../nodeDef'\nimport { CategoryItemProvider } from '../../nodeDefExpressionEvaluator/categoryItemProvider'\nimport { Survey } from '../../survey'\nimport { getNodeDefChildren, getNodeDefEnumerator } from '../../survey/surveys/nodeDefs'\nimport { getAncestorCodeAttributes } from '../_records/recordGetters'\nimport { getEnumeratingCategoryItems } from '../_records/recordUtils'\nimport { getEnumeratingItemsAllowedCodes } from './recordEnumeratingItemsExpressionEvaluator'\nimport { RecordExpressionEvaluationContext } from './recordExpressionEvaluationContext'\nimport { RecordUpdateResult } from './recordUpdateResult'\n\nexport type NodesUpdateParams = RecordExpressionEvaluationContext & {\n dateModified?: string\n clearNonApplicableValues?: boolean\n}\n\nexport type NodeCreateParams = NodesUpdateParams & {\n parentNode?: ArenaRecordNode\n nodeDef: NodeDef<any>\n createMultipleEntities?: boolean\n}\n\nconst getNodesToInsertCount = (params: { parentNode: ArenaRecordNode | undefined; nodeDef: NodeDef<any> }): number => {\n const { nodeDef, parentNode } = params\n if (!parentNode || NodeDefs.isSingle(nodeDef)) return 1\n if (nodeDef.type === NodeDefType.code) return 0 // never create nodes for multiple code attributes\n\n if (NodeDefs.isMultipleEntity(nodeDef) && !NodeDefs.isAutoCreateMinCountItems(nodeDef)) return 0\n\n const minCount = Nodes.getChildrenMinCount({ parentNode, nodeDef })\n if (Number.isNaN(minCount) || minCount <= 0) return 0\n return minCount\n}\n\nexport const createEnumeratedEntityNode = async (params: {\n user: User\n survey: Survey\n parentNode: ArenaRecordNode\n nodeDef: NodeDefEntity\n enumeratorDef: NodeDefCode\n categoryItem: CategoryItem\n updateResult: RecordUpdateResult\n sideEffect?: boolean\n}): Promise<void> => {\n const { user, survey, parentNode, nodeDef, enumeratorDef, categoryItem, updateResult, sideEffect } = params\n\n const { record } = updateResult\n\n const childUpdateResult = await addNodeAndDescendants({\n user,\n survey,\n record: updateResult.record,\n parentNode,\n nodeDef,\n sideEffect,\n })\n const enumeratorNode = Object.values(childUpdateResult.nodes).find((node) => node.nodeDefUuid === enumeratorDef.uuid)\n if (!enumeratorNode) {\n throw new SystemError(SystemErrorKeys.record.enumeratorNodeNotFound, {\n recordUuid: record.uuid,\n entityDef: NodeDefs.getName(nodeDef),\n enumerator: NodeDefs.getName(enumeratorDef),\n })\n }\n enumeratorNode.value = NodeValues.newCodeValue({ itemUuid: categoryItem.uuid })\n enumeratorNode.meta = { ...enumeratorNode.meta, defaultValueApplied: true }\n enumeratorNode.refData = { categoryItem }\n\n updateResult.merge(childUpdateResult)\n}\n\nexport const createEnumeratedEntityNodes = async (params: {\n user: User\n survey: Survey\n parentNode: ArenaRecordNode\n nodeDef: NodeDefEntity\n updateResult: RecordUpdateResult\n categoryItemProvider?: CategoryItemProvider\n sideEffect?: boolean\n}): Promise<boolean> => {\n const { user, survey, parentNode, nodeDef, updateResult, categoryItemProvider } = params\n\n const enumeratorDef = getNodeDefEnumerator({ survey, entityDef: nodeDef })\n if (!enumeratorDef) return false\n\n const allowedCodes = await getEnumeratingItemsAllowedCodes({\n survey,\n user,\n record: updateResult.record,\n entityDef: nodeDef,\n parentNode,\n })\n\n const categoryItems = await getEnumeratingCategoryItems({\n survey,\n enumeratorDef,\n parentNode,\n categoryItemProvider,\n allowedCodes,\n })(updateResult.record)\n if (categoryItems.length === 0) return false\n\n for (const categoryItem of categoryItems) {\n // sequential: each iteration updates the record\n await createEnumeratedEntityNode({ ...params, enumeratorDef, categoryItem }) // NOSONAR\n }\n return true\n}\n\nconst createChildNodesBasedOnMinCount = async (\n params: NodeCreateParams & { updateResult: RecordUpdateResult }\n): Promise<void> => {\n const { parentNode, nodeDef, updateResult, createMultipleEntities = true } = params\n\n if (NodeDefs.isMultipleEntity(nodeDef) && NodeDefs.isEnumerate(nodeDef)) {\n if (await createEnumeratedEntityNodes({ ...params, parentNode: parentNode! })) {\n return\n }\n }\n\n const nodesToInsertCount = getNodesToInsertCount({ parentNode, nodeDef })\n if (\n nodesToInsertCount === 0 ||\n (NodeDefs.isMultipleEntity(nodeDef) &&\n (!createMultipleEntities || (parentNode && !Nodes.isChildApplicable(parentNode, nodeDef.uuid))))\n ) {\n return // do nothing\n }\n for (let index = 0; index < nodesToInsertCount; index++) {\n // sequential: each iteration updates the record\n const childUpdateResult = await addNodeAndDescendants({ ...params, record: updateResult.record }) // NOSONAR\n updateResult.merge(childUpdateResult)\n }\n}\n\nexport const addDescendants = async (params: NodeCreateParams): Promise<RecordUpdateResult> => {\n const { survey, record, nodeDef } = params\n\n const updateResult = new RecordUpdateResult({ record })\n\n if (NodeDefs.isEntity(nodeDef)) {\n const childDefs = getNodeDefChildren({ survey, nodeDef })\n\n // Add only child single nodes (it allows to apply default values)\n for (const childDef of childDefs) {\n // sequential: each iteration updates the record\n await createChildNodesBasedOnMinCount({ ...params, updateResult, nodeDef: childDef }) // NOSONAR\n }\n }\n return updateResult\n}\n\nexport const addNodeAndDescendants = async (params: NodeCreateParams): Promise<RecordUpdateResult> => {\n const { survey, record, parentNode, nodeDef, sideEffect = false } = params\n\n const node = NodeFactory.createInstance({\n nodeDefUuid: nodeDef.uuid,\n recordUuid: record.uuid,\n parentNode,\n surveyUuid: survey.uuid,\n })\n if (parentNode && nodeDef.type === NodeDefType.code && NodeDefs.getParentCodeDefUuid(nodeDef as NodeDefCode)) {\n // meta.hCode indexes the node as dependent of its ancestor code attributes (cleared when they change)\n const ancestorCodeAttributes = getAncestorCodeAttributes(survey, parentNode, nodeDef as NodeDefCode)(record)\n node.meta = { ...node.meta, hCode: ancestorCodeAttributes.map((codeAttribute) => codeAttribute.uuid) }\n }\n\n const updateResult = new RecordUpdateResult({ record })\n updateResult.addNode(node, { sideEffect })\n\n // Add children if entity\n if (NodeDefs.isEntity(nodeDef)) {\n const descendantsUpdateResult = await addDescendants({\n ...params,\n record: updateResult.record,\n parentNode: node,\n })\n updateResult.merge(descendantsUpdateResult)\n }\n return updateResult\n}\n"]}
|
|
1
|
+
{"version":3,"file":"recordNodesCreator.js","sourceRoot":"","sources":["../../../src/record/recordNodesUpdater/recordNodesCreator.ts"],"names":[],"mappings":"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","sourcesContent":["import { User } from '../../auth'\nimport { SystemError, SystemErrorKeys } from '../../error'\nimport { CategoryItem } from '../../category'\nimport { ArenaRecordNode, NodeFactory, Nodes } from '../../node'\nimport { NodeValues } from '../../node/nodeValues'\nimport { NodeDef, NodeDefCode, NodeDefEntity, NodeDefType, NodeDefs } from '../../nodeDef'\nimport { CategoryItemProvider } from '../../nodeDefExpressionEvaluator/categoryItemProvider'\nimport { Survey } from '../../survey'\nimport { getNodeDefChildren, getNodeDefEnumerator } from '../../survey/surveys/nodeDefs'\nimport { getAncestorCodeAttributes, getCycle } from '../_records/recordGetters'\nimport { getEnumeratingCategoryItems } from '../_records/recordUtils'\nimport { getEnumeratingItemsAllowedCodes } from './recordEnumeratingItemsExpressionEvaluator'\nimport { RecordExpressionEvaluationContext } from './recordExpressionEvaluationContext'\nimport { RecordUpdateResult } from './recordUpdateResult'\n\nexport type NodesUpdateParams = RecordExpressionEvaluationContext & {\n dateModified?: string\n clearNonApplicableValues?: boolean\n}\n\nexport type NodeCreateParams = NodesUpdateParams & {\n parentNode?: ArenaRecordNode\n nodeDef: NodeDef<any>\n createMultipleEntities?: boolean\n}\n\nconst getNodesToInsertCount = (params: { parentNode: ArenaRecordNode | undefined; nodeDef: NodeDef<any> }): number => {\n const { nodeDef, parentNode } = params\n if (!parentNode || NodeDefs.isSingle(nodeDef)) return 1\n if (nodeDef.type === NodeDefType.code) return 0 // never create nodes for multiple code attributes\n\n if (NodeDefs.isMultipleEntity(nodeDef) && !NodeDefs.isAutoCreateMinCountItems(nodeDef)) return 0\n\n const minCount = Nodes.getChildrenMinCount({ parentNode, nodeDef })\n if (Number.isNaN(minCount) || minCount <= 0) return 0\n return minCount\n}\n\nexport const createEnumeratedEntityNode = async (params: {\n user: User\n survey: Survey\n parentNode: ArenaRecordNode\n nodeDef: NodeDefEntity\n enumeratorDef: NodeDefCode\n categoryItem: CategoryItem\n updateResult: RecordUpdateResult\n sideEffect?: boolean\n}): Promise<void> => {\n const { user, survey, parentNode, nodeDef, enumeratorDef, categoryItem, updateResult, sideEffect } = params\n\n const { record } = updateResult\n\n const childUpdateResult = await addNodeAndDescendants({\n user,\n survey,\n record: updateResult.record,\n parentNode,\n nodeDef,\n sideEffect,\n })\n const enumeratorNode = Object.values(childUpdateResult.nodes).find((node) => node.nodeDefUuid === enumeratorDef.uuid)\n if (!enumeratorNode) {\n throw new SystemError(SystemErrorKeys.record.enumeratorNodeNotFound, {\n recordUuid: record.uuid,\n entityDef: NodeDefs.getName(nodeDef),\n enumerator: NodeDefs.getName(enumeratorDef),\n })\n }\n enumeratorNode.value = NodeValues.newCodeValue({ itemUuid: categoryItem.uuid })\n enumeratorNode.meta = { ...enumeratorNode.meta, defaultValueApplied: true }\n enumeratorNode.refData = { categoryItem }\n\n updateResult.merge(childUpdateResult)\n}\n\nexport const createEnumeratedEntityNodes = async (params: {\n user: User\n survey: Survey\n parentNode: ArenaRecordNode\n nodeDef: NodeDefEntity\n updateResult: RecordUpdateResult\n categoryItemProvider?: CategoryItemProvider\n sideEffect?: boolean\n}): Promise<boolean> => {\n const { user, survey, parentNode, nodeDef, updateResult, categoryItemProvider } = params\n\n const enumeratorDef = getNodeDefEnumerator({ survey, entityDef: nodeDef })\n if (!enumeratorDef) return false\n\n const allowedCodes = await getEnumeratingItemsAllowedCodes({\n survey,\n user,\n record: updateResult.record,\n entityDef: nodeDef,\n parentNode,\n })\n\n const categoryItems = await getEnumeratingCategoryItems({\n survey,\n enumeratorDef,\n parentNode,\n categoryItemProvider,\n allowedCodes,\n })(updateResult.record)\n if (categoryItems.length === 0) return false\n\n for (const categoryItem of categoryItems) {\n // sequential: each iteration updates the record\n await createEnumeratedEntityNode({ ...params, enumeratorDef, categoryItem }) // NOSONAR\n }\n return true\n}\n\nconst createChildNodesBasedOnMinCount = async (\n params: NodeCreateParams & { updateResult: RecordUpdateResult }\n): Promise<void> => {\n const { parentNode, nodeDef, updateResult, createMultipleEntities = true } = params\n\n if (NodeDefs.isMultipleEntity(nodeDef) && NodeDefs.isEnumerate(nodeDef)) {\n if (await createEnumeratedEntityNodes({ ...params, parentNode: parentNode! })) {\n return\n }\n }\n\n const nodesToInsertCount = getNodesToInsertCount({ parentNode, nodeDef })\n if (\n nodesToInsertCount === 0 ||\n (NodeDefs.isMultipleEntity(nodeDef) &&\n (!createMultipleEntities || (parentNode && !Nodes.isChildApplicable(parentNode, nodeDef.uuid))))\n ) {\n return // do nothing\n }\n for (let index = 0; index < nodesToInsertCount; index++) {\n // sequential: each iteration updates the record\n const childUpdateResult = await addNodeAndDescendants({ ...params, record: updateResult.record }) // NOSONAR\n updateResult.merge(childUpdateResult)\n }\n}\n\nexport const addDescendants = async (params: NodeCreateParams): Promise<RecordUpdateResult> => {\n const { survey, record, nodeDef } = params\n\n const updateResult = new RecordUpdateResult({ record })\n\n if (NodeDefs.isEntity(nodeDef)) {\n // node defs not in the record cycle have no nodes\n const childDefs = getNodeDefChildren({ survey, nodeDef }).filter(NodeDefs.isInCycle(getCycle(record)))\n\n // Add only child single nodes (it allows to apply default values)\n for (const childDef of childDefs) {\n // sequential: each iteration updates the record\n await createChildNodesBasedOnMinCount({ ...params, updateResult, nodeDef: childDef }) // NOSONAR\n }\n }\n return updateResult\n}\n\nexport const addNodeAndDescendants = async (params: NodeCreateParams): Promise<RecordUpdateResult> => {\n const { survey, record, parentNode, nodeDef, sideEffect = false } = params\n\n const node = NodeFactory.createInstance({\n nodeDefUuid: nodeDef.uuid,\n recordUuid: record.uuid,\n parentNode,\n surveyUuid: survey.uuid,\n })\n if (parentNode && nodeDef.type === NodeDefType.code && NodeDefs.getParentCodeDefUuid(nodeDef as NodeDefCode)) {\n // meta.hCode indexes the node as dependent of its ancestor code attributes (cleared when they change)\n const ancestorCodeAttributes = getAncestorCodeAttributes(survey, parentNode, nodeDef as NodeDefCode)(record)\n node.meta = { ...node.meta, hCode: ancestorCodeAttributes.map((codeAttribute) => codeAttribute.uuid) }\n }\n\n const updateResult = new RecordUpdateResult({ record })\n updateResult.addNode(node, { sideEffect })\n\n // Add children if entity\n if (NodeDefs.isEntity(nodeDef)) {\n const descendantsUpdateResult = await addDescendants({\n ...params,\n record: updateResult.record,\n parentNode: node,\n })\n updateResult.merge(descendantsUpdateResult)\n }\n return updateResult\n}\n"]}
|
|
@@ -35,7 +35,10 @@ export declare class FileProcessor {
|
|
|
35
35
|
protected currentChunkNumber: number;
|
|
36
36
|
protected processingChunk: boolean;
|
|
37
37
|
protected completed: boolean;
|
|
38
|
+
private runId;
|
|
39
|
+
private retryProcessor?;
|
|
38
40
|
constructor({ file, filePath, chunkProcessor, onError, onComplete, chunkSize, maxTryings, retryDelay, }: FileProcessorConstructorArgs);
|
|
41
|
+
private cancelCurrentRun;
|
|
39
42
|
protected reset(): void;
|
|
40
43
|
protected calculateFileSize(): Promise<number>;
|
|
41
44
|
protected onFail(error: Error): void;
|
|
@@ -12,6 +12,8 @@ class FileProcessor {
|
|
|
12
12
|
// true while a chunk is being processed (resume must not start a second processing chain)
|
|
13
13
|
this.processingChunk = false;
|
|
14
14
|
this.completed = false;
|
|
15
|
+
// incremented when a processing run is stopped: callbacks of the chunks of previous runs are ignored
|
|
16
|
+
this.runId = 0;
|
|
15
17
|
this.file = file;
|
|
16
18
|
this.filePath = filePath;
|
|
17
19
|
this.chunkProcessor = chunkProcessor;
|
|
@@ -25,7 +27,14 @@ class FileProcessor {
|
|
|
25
27
|
}
|
|
26
28
|
this.reset();
|
|
27
29
|
}
|
|
30
|
+
cancelCurrentRun() {
|
|
31
|
+
var _a;
|
|
32
|
+
this.runId += 1;
|
|
33
|
+
(_a = this.retryProcessor) === null || _a === void 0 ? void 0 : _a.stop();
|
|
34
|
+
this.retryProcessor = undefined;
|
|
35
|
+
}
|
|
28
36
|
reset() {
|
|
37
|
+
this.cancelCurrentRun();
|
|
29
38
|
this.running = false;
|
|
30
39
|
this.totalChunks = 0;
|
|
31
40
|
this.currentChunkNumber = 0;
|
|
@@ -50,14 +59,19 @@ class FileProcessor {
|
|
|
50
59
|
return Promise.resolve(file.slice((currentChunkNumber - 1) * chunkSize, currentChunkNumber === totalChunks ? undefined : currentChunkNumber * chunkSize));
|
|
51
60
|
}
|
|
52
61
|
processNextChunk() {
|
|
53
|
-
const { chunkProcessor, currentChunkNumber, totalFileSize, totalChunks, maxTryings, retryDelay } = this;
|
|
62
|
+
const { chunkProcessor, currentChunkNumber, totalFileSize, totalChunks, maxTryings, retryDelay, runId } = this;
|
|
63
|
+
const isStale = () => runId !== this.runId;
|
|
54
64
|
this.processingChunk = true;
|
|
55
65
|
this.extractCurrentFileChunk()
|
|
56
66
|
.then((content) => {
|
|
67
|
+
if (isStale())
|
|
68
|
+
return;
|
|
57
69
|
const retryProcessor = new retryProcessor_1.RetryProcessor({
|
|
58
70
|
processor: () => chunkProcessor({ chunk: currentChunkNumber, totalFileSize, totalChunks, content }),
|
|
59
71
|
onSuccess: (chunkResult) => {
|
|
60
72
|
var _a;
|
|
73
|
+
if (isStale())
|
|
74
|
+
return;
|
|
61
75
|
this.processingChunk = false;
|
|
62
76
|
if (this.currentChunkNumber < totalChunks) {
|
|
63
77
|
// move to the next chunk also when paused: resume will continue from it
|
|
@@ -74,6 +88,8 @@ class FileProcessor {
|
|
|
74
88
|
}
|
|
75
89
|
},
|
|
76
90
|
onFail: (error) => {
|
|
91
|
+
if (isStale())
|
|
92
|
+
return;
|
|
77
93
|
this.processingChunk = false;
|
|
78
94
|
this.onFail(error);
|
|
79
95
|
this.running = false;
|
|
@@ -81,20 +97,29 @@ class FileProcessor {
|
|
|
81
97
|
maxTryings,
|
|
82
98
|
retryDelay,
|
|
83
99
|
});
|
|
100
|
+
this.retryProcessor = retryProcessor;
|
|
84
101
|
retryProcessor.start();
|
|
85
102
|
})
|
|
86
103
|
.catch((error) => {
|
|
104
|
+
if (isStale())
|
|
105
|
+
return;
|
|
87
106
|
this.processingChunk = false;
|
|
88
107
|
this.onFail(error);
|
|
89
108
|
this.running = false;
|
|
90
109
|
});
|
|
91
110
|
}
|
|
92
111
|
start(startFromChunk = 1) {
|
|
112
|
+
// a previous run still in progress must not continue in parallel
|
|
113
|
+
this.cancelCurrentRun();
|
|
114
|
+
const { runId } = this;
|
|
115
|
+
this.processingChunk = false;
|
|
93
116
|
this.running = true;
|
|
94
117
|
this.completed = false;
|
|
95
118
|
this.currentChunkNumber = startFromChunk;
|
|
96
119
|
this.calculateFileSize()
|
|
97
120
|
.then((fileSize) => {
|
|
121
|
+
if (runId !== this.runId)
|
|
122
|
+
return;
|
|
98
123
|
this.totalFileSize = fileSize;
|
|
99
124
|
this.totalChunks = Math.ceil(fileSize / this.chunkSize);
|
|
100
125
|
if (this.totalChunks > 0) {
|
|
@@ -102,6 +127,8 @@ class FileProcessor {
|
|
|
102
127
|
}
|
|
103
128
|
})
|
|
104
129
|
.catch((error) => {
|
|
130
|
+
if (runId !== this.runId)
|
|
131
|
+
return;
|
|
105
132
|
this.onFail(error);
|
|
106
133
|
this.running = false;
|
|
107
134
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fileProcessor.js","sourceRoot":"","sources":["../../src/utils/fileProcessor.ts"],"names":[],"mappings":"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","sourcesContent":["import { RetryProcessor } from './retryProcessor'\n\ntype ChunkProcessor = (args: {\n chunk: number\n content: Blob | string | Uint8Array\n totalChunks: number\n totalFileSize: number\n}) => Promise<any>\n\nexport type FileProcessorConstructorArgs = {\n file?: File\n filePath?: string\n chunkProcessor: ChunkProcessor\n onError?: (error: Error) => void\n onComplete?: (result: any) => void\n chunkSize?: number\n maxTryings?: number\n /**\n * Delay (in milliseconds) before retrying a failed chunk, doubled at every further retry.\n */\n retryDelay?: number\n}\n\nexport class FileProcessor {\n static readonly defaultChunkSize = 1024 * 1024 * 10 // 10MB\n static readonly defaultMaxTryings = 5\n static readonly defaultRetryDelay = 1000 // 1s, then 2s, 4s...\n\n private readonly file?: File\n private readonly filePath?: string\n private readonly chunkProcessor: ChunkProcessor\n private readonly chunkSize: number\n private readonly maxTryings: number\n private readonly retryDelay: number\n private readonly onError?: (error: Error) => void\n private readonly onComplete?: (result?: any) => void\n\n // State properties\n protected totalFileSize: number = 0\n protected running: boolean = false\n protected totalChunks: number = 0\n protected currentChunkNumber: number = 0\n // true while a chunk is being processed (resume must not start a second processing chain)\n protected processingChunk: boolean = false\n protected completed: boolean = false\n\n constructor({\n file,\n filePath,\n chunkProcessor,\n onError,\n onComplete,\n chunkSize = FileProcessor.defaultChunkSize,\n maxTryings = FileProcessor.defaultMaxTryings,\n retryDelay = FileProcessor.defaultRetryDelay,\n }: FileProcessorConstructorArgs) {\n this.file = file\n this.filePath = filePath\n this.chunkProcessor = chunkProcessor\n this.chunkSize = chunkSize\n this.maxTryings = maxTryings\n this.retryDelay = retryDelay\n this.onError = onError\n this.onComplete = onComplete\n\n if (!this.file && !this.filePath) {\n throw new Error('File or filePath not specified')\n }\n\n this.reset()\n }\n\n protected reset(): void {\n this.running = false\n this.totalChunks = 0\n this.currentChunkNumber = 0\n this.totalFileSize = 0\n this.processingChunk = false\n this.completed = false\n }\n\n protected calculateFileSize(): Promise<number> {\n return Promise.resolve(this.file?.size ?? 0)\n }\n\n protected onFail(error: Error): void {\n this.onError?.(error)\n }\n\n protected extractCurrentFileChunk(): Promise<Blob | string | Uint8Array> {\n const { file, currentChunkNumber, totalChunks, chunkSize } = this\n if (!file) {\n return Promise.reject(new Error('File property not initialized'))\n }\n return Promise.resolve(\n file.slice(\n (currentChunkNumber - 1) * chunkSize,\n currentChunkNumber === totalChunks ? undefined : currentChunkNumber * chunkSize\n )\n )\n }\n\n protected processNextChunk(): void {\n const { chunkProcessor, currentChunkNumber, totalFileSize, totalChunks, maxTryings, retryDelay } = this\n\n this.processingChunk = true\n this.extractCurrentFileChunk()\n .then((content) => {\n const retryProcessor = new RetryProcessor<any>({\n processor: () => chunkProcessor({ chunk: currentChunkNumber, totalFileSize, totalChunks, content }),\n onSuccess: (chunkResult) => {\n this.processingChunk = false\n if (this.currentChunkNumber < totalChunks) {\n // move to the next chunk also when paused: resume will continue from it\n this.currentChunkNumber += 1\n if (this.running) {\n this.processNextChunk()\n }\n } else {\n this.running = false\n this.completed = true\n // called outside the retried processor: if it throws, the last chunk is not processed again\n this.onComplete?.(chunkResult)\n }\n },\n onFail: (error: Error) => {\n this.processingChunk = false\n this.onFail(error)\n this.running = false\n },\n maxTryings,\n retryDelay,\n })\n retryProcessor.start()\n })\n .catch((error) => {\n this.processingChunk = false\n this.onFail(error)\n this.running = false\n })\n }\n\n start(startFromChunk: number = 1): void {\n this.running = true\n this.completed = false\n this.currentChunkNumber = startFromChunk\n this.calculateFileSize()\n .then((fileSize) => {\n this.totalFileSize = fileSize\n this.totalChunks = Math.ceil(fileSize / this.chunkSize)\n if (this.totalChunks > 0) {\n this.processNextChunk()\n }\n })\n .catch((error) => {\n this.onFail(error)\n this.running = false\n })\n }\n\n stop(): void {\n this.reset()\n }\n\n pause(): void {\n this.running = false\n }\n\n resume(): void {\n if (this.running || this.completed) return\n this.running = true\n // if a chunk is still being processed, processing continues when it completes\n if (!this.processingChunk && this.totalChunks > 0) {\n this.processNextChunk()\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"fileProcessor.js","sourceRoot":"","sources":["../../src/utils/fileProcessor.ts"],"names":[],"mappings":"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","sourcesContent":["import { RetryProcessor } from './retryProcessor'\n\ntype ChunkProcessor = (args: {\n chunk: number\n content: Blob | string | Uint8Array\n totalChunks: number\n totalFileSize: number\n}) => Promise<any>\n\nexport type FileProcessorConstructorArgs = {\n file?: File\n filePath?: string\n chunkProcessor: ChunkProcessor\n onError?: (error: Error) => void\n onComplete?: (result: any) => void\n chunkSize?: number\n maxTryings?: number\n /**\n * Delay (in milliseconds) before retrying a failed chunk, doubled at every further retry.\n */\n retryDelay?: number\n}\n\nexport class FileProcessor {\n static readonly defaultChunkSize = 1024 * 1024 * 10 // 10MB\n static readonly defaultMaxTryings = 5\n static readonly defaultRetryDelay = 1000 // 1s, then 2s, 4s...\n\n private readonly file?: File\n private readonly filePath?: string\n private readonly chunkProcessor: ChunkProcessor\n private readonly chunkSize: number\n private readonly maxTryings: number\n private readonly retryDelay: number\n private readonly onError?: (error: Error) => void\n private readonly onComplete?: (result?: any) => void\n\n // State properties\n protected totalFileSize: number = 0\n protected running: boolean = false\n protected totalChunks: number = 0\n protected currentChunkNumber: number = 0\n // true while a chunk is being processed (resume must not start a second processing chain)\n protected processingChunk: boolean = false\n protected completed: boolean = false\n // incremented when a processing run is stopped: callbacks of the chunks of previous runs are ignored\n private runId: number = 0\n private retryProcessor?: RetryProcessor<any>\n\n constructor({\n file,\n filePath,\n chunkProcessor,\n onError,\n onComplete,\n chunkSize = FileProcessor.defaultChunkSize,\n maxTryings = FileProcessor.defaultMaxTryings,\n retryDelay = FileProcessor.defaultRetryDelay,\n }: FileProcessorConstructorArgs) {\n this.file = file\n this.filePath = filePath\n this.chunkProcessor = chunkProcessor\n this.chunkSize = chunkSize\n this.maxTryings = maxTryings\n this.retryDelay = retryDelay\n this.onError = onError\n this.onComplete = onComplete\n\n if (!this.file && !this.filePath) {\n throw new Error('File or filePath not specified')\n }\n\n this.reset()\n }\n\n private cancelCurrentRun(): void {\n this.runId += 1\n this.retryProcessor?.stop()\n this.retryProcessor = undefined\n }\n\n protected reset(): void {\n this.cancelCurrentRun()\n this.running = false\n this.totalChunks = 0\n this.currentChunkNumber = 0\n this.totalFileSize = 0\n this.processingChunk = false\n this.completed = false\n }\n\n protected calculateFileSize(): Promise<number> {\n return Promise.resolve(this.file?.size ?? 0)\n }\n\n protected onFail(error: Error): void {\n this.onError?.(error)\n }\n\n protected extractCurrentFileChunk(): Promise<Blob | string | Uint8Array> {\n const { file, currentChunkNumber, totalChunks, chunkSize } = this\n if (!file) {\n return Promise.reject(new Error('File property not initialized'))\n }\n return Promise.resolve(\n file.slice(\n (currentChunkNumber - 1) * chunkSize,\n currentChunkNumber === totalChunks ? undefined : currentChunkNumber * chunkSize\n )\n )\n }\n\n protected processNextChunk(): void {\n const { chunkProcessor, currentChunkNumber, totalFileSize, totalChunks, maxTryings, retryDelay, runId } = this\n const isStale = () => runId !== this.runId\n\n this.processingChunk = true\n this.extractCurrentFileChunk()\n .then((content) => {\n if (isStale()) return\n const retryProcessor = new RetryProcessor<any>({\n processor: () => chunkProcessor({ chunk: currentChunkNumber, totalFileSize, totalChunks, content }),\n onSuccess: (chunkResult) => {\n if (isStale()) return\n this.processingChunk = false\n if (this.currentChunkNumber < totalChunks) {\n // move to the next chunk also when paused: resume will continue from it\n this.currentChunkNumber += 1\n if (this.running) {\n this.processNextChunk()\n }\n } else {\n this.running = false\n this.completed = true\n // called outside the retried processor: if it throws, the last chunk is not processed again\n this.onComplete?.(chunkResult)\n }\n },\n onFail: (error: Error) => {\n if (isStale()) return\n this.processingChunk = false\n this.onFail(error)\n this.running = false\n },\n maxTryings,\n retryDelay,\n })\n this.retryProcessor = retryProcessor\n retryProcessor.start()\n })\n .catch((error) => {\n if (isStale()) return\n this.processingChunk = false\n this.onFail(error)\n this.running = false\n })\n }\n\n start(startFromChunk: number = 1): void {\n // a previous run still in progress must not continue in parallel\n this.cancelCurrentRun()\n const { runId } = this\n this.processingChunk = false\n this.running = true\n this.completed = false\n this.currentChunkNumber = startFromChunk\n this.calculateFileSize()\n .then((fileSize) => {\n if (runId !== this.runId) return\n this.totalFileSize = fileSize\n this.totalChunks = Math.ceil(fileSize / this.chunkSize)\n if (this.totalChunks > 0) {\n this.processNextChunk()\n }\n })\n .catch((error) => {\n if (runId !== this.runId) return\n this.onFail(error)\n this.running = false\n })\n }\n\n stop(): void {\n this.reset()\n }\n\n pause(): void {\n this.running = false\n }\n\n resume(): void {\n if (this.running || this.completed) return\n this.running = true\n // if a chunk is still being processed, processing continues when it completes\n if (!this.processingChunk && this.totalChunks > 0) {\n this.processNextChunk()\n }\n }\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"retryProcessor.js","sourceRoot":"","sources":["../../src/utils/retryProcessor.ts"],"names":[],"mappings":";;;AAYA,MAAM,OAAO,GAAG,CAAC,KAAc,EAAS,EAAE,CAAC,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;AAEtG;IAWE,YAAY,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,GAAG,CAAC,EAAE,UAAU,GAAG,CAAC,EAAuC;QAJjH,mBAAmB;QACX,kBAAa,GAAW,CAAC,CAAA;QACzB,YAAO,GAAY,KAAK,CAAA;QAG9B,IAAI,CAAC,SAAS,GAAG,SAAS,CAAA;QAC1B,IAAI,CAAC,SAAS,GAAG,SAAS,CAAA;QAC1B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAA;QACpB,IAAI,CAAC,UAAU,GAAG,UAAU,CAAA;QAC5B,IAAI,CAAC,UAAU,GAAG,UAAU,CAAA;IAC9B,CAAC;IAES,KAAK;QACb,IAAI,CAAC,aAAa,GAAG,CAAC,CAAA;QACtB,IAAI,CAAC,OAAO,GAAG,KAAK,CAAA;IACtB,CAAC;IAES,WAAW;QACnB,IAAI,CAAC,aAAa,IAAI,CAAC,CAAA;QAEvB,2FAA2F;QAC3F,IAAI,CAAC,SAAS,EAAE,CAAC,IAAI,CACnB,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,EACtC,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,oBAAoB,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CACrD,CAAA;IACH,CAAC;IAEO,aAAa,CAAC,MAAuB;QAC3C,IAAI,CAAC;YACH,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAA;QACxB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAA;QAC7B,CAAC;IACH,CAAC;IAEO,oBAAoB,CAAC,KAAY;QACvC,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,aAAa,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YAC3D,kCAAkC;YAClC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;YAClB,OAAM;QACR,CAAC;QACD,MAAM,KAAK,GAAG,IAAI,CAAC,UAAU,GAAG,SAAA,CAAC,EAAI,CAAC,IAAI,CAAC,aAAa,GAAG,CAAC,CAAC,CAAA,CAAA;QAC7D,IAAI,KAAK,IAAI,CAAC,EAAE,CAAC;YACf,IAAI,CAAC,WAAW,EAAE,CAAA;YAClB,OAAM;QACR,CAAC;QACD,UAAU,CAAC,GAAG,EAAE;YACd,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;gBACjB,IAAI,CAAC,WAAW,EAAE,CAAA;YACpB,CAAC;iBAAM,CAAC;gBACN,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;YACpB,CAAC;QACH,CAAC,EAAE,KAAK,CAAC,CAAA;IACX,CAAC;IAED,iBAAiB;IACjB,KAAK;QACH,IAAI,CAAC,OAAO,GAAG,IAAI,CAAA;QACnB,IAAI,CAAC,WAAW,EAAE,CAAA;IACpB,CAAC;IAED,IAAI;QACF,IAAI,CAAC,KAAK,EAAE,CAAA;IACd,CAAC;CACF","sourcesContent":["interface RetryProcessorArgs<ProcessorResult> {\n processor: () => Promise<ProcessorResult>\n onSuccess: (result: ProcessorResult) => void\n onFail: (error: Error) => void\n maxTryings?: number\n /**\n * Delay (in milliseconds) before the first retry, doubled at every further retry (exponential backoff).\n * Defaults to 0 (immediate retry).\n */\n retryDelay?: number\n}\n\nconst toError = (error: unknown): Error => (error instanceof Error ? error : new Error(String(error)))\n\nexport class RetryProcessor<ProcessorResult> {\n private readonly processor: () => Promise<ProcessorResult>\n private readonly onSuccess: (result: ProcessorResult) => void\n private readonly onFail: (error: Error) => void\n private readonly maxTryings: number\n private readonly retryDelay: number\n\n // State properties\n private currentTrying: number = 0\n private running: boolean = false\n\n constructor({ processor, onSuccess, onFail, maxTryings = 2, retryDelay = 0 }: RetryProcessorArgs<ProcessorResult>) {\n this.processor = processor\n this.onSuccess = onSuccess\n this.onFail = onFail\n this.maxTryings = maxTryings\n this.retryDelay = retryDelay\n }\n\n protected reset() {\n this.currentTrying = 0\n this.running = false\n }\n\n protected processNext(): void {\n this.currentTrying += 1\n\n // onSuccess runs outside the retried chain: if it throws, the processor must not run again\n this.processor().then(\n (result) => this.handleSuccess(result),\n (error) => this.handleProcessorError(toError(error))\n )\n }\n\n private handleSuccess(result: ProcessorResult): void {\n try {\n this.onSuccess(result)\n } catch (error) {\n this.onFail(toError(error))\n }\n }\n\n private handleProcessorError(error: Error): void {\n if (!this.running || this.currentTrying >= this.maxTryings) {\n // stopped or max attempts reached\n this.onFail(error)\n return\n }\n const delay = this.retryDelay * 2 ** (this.currentTrying - 1)\n if (delay <= 0) {\n this.processNext()\n return\n }\n setTimeout(() => {\n if (this.running) {\n this.processNext()\n } else {\n this.onFail(error)\n }\n }, delay)\n }\n\n // Public methods\n start(): void {\n this.running = true\n this.processNext()\n }\n\n stop(): void {\n this.reset()\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"retryProcessor.js","sourceRoot":"","sources":["../../src/utils/retryProcessor.ts"],"names":[],"mappings":";;;AAYA,MAAM,OAAO,GAAG,CAAC,KAAc,EAAS,EAAE,CAAC,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;AAEtG;IAWE,YAAY,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,GAAG,CAAC,EAAE,UAAU,GAAG,CAAC,EAAuC;QAJjH,mBAAmB;QACX,kBAAa,GAAW,CAAC,CAAA;QACzB,YAAO,GAAY,KAAK,CAAA;QAG9B,IAAI,CAAC,SAAS,GAAG,SAAS,CAAA;QAC1B,IAAI,CAAC,SAAS,GAAG,SAAS,CAAA;QAC1B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAA;QACpB,IAAI,CAAC,UAAU,GAAG,UAAU,CAAA;QAC5B,IAAI,CAAC,UAAU,GAAG,UAAU,CAAA;IAC9B,CAAC;IAES,KAAK;QACb,IAAI,CAAC,aAAa,GAAG,CAAC,CAAA;QACtB,IAAI,CAAC,OAAO,GAAG,KAAK,CAAA;IACtB,CAAC;IAES,WAAW;QACnB,IAAI,CAAC,aAAa,IAAI,CAAC,CAAA;QAEvB,2FAA2F;QAC3F,IAAI,CAAC,SAAS,EAAE,CAAC,IAAI,CACnB,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,EACtC,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,oBAAoB,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CACrD,CAAA;IACH,CAAC;IAEO,aAAa,CAAC,MAAuB;QAC3C,IAAI,CAAC;YACH,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAA;QACxB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAA;QAC7B,CAAC;IACH,CAAC;IAEO,oBAAoB,CAAC,KAAY;QACvC,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,aAAa,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YAC3D,kCAAkC;YAClC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;YAClB,OAAM;QACR,CAAC;QACD,MAAM,KAAK,GAAG,IAAI,CAAC,UAAU,GAAG,SAAA,CAAC,EAAI,CAAC,IAAI,CAAC,aAAa,GAAG,CAAC,CAAC,CAAA,CAAA;QAC7D,IAAI,KAAK,IAAI,CAAC,EAAE,CAAC;YACf,IAAI,CAAC,WAAW,EAAE,CAAA;YAClB,OAAM;QACR,CAAC;QACD,UAAU,CAAC,GAAG,EAAE;YACd,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;gBACjB,IAAI,CAAC,WAAW,EAAE,CAAA;YACpB,CAAC;iBAAM,CAAC;gBACN,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;YACpB,CAAC;QACH,CAAC,EAAE,KAAK,CAAC,CAAA;IACX,CAAC;IAED,iBAAiB;IACjB,KAAK;QACH,IAAI,CAAC,aAAa,GAAG,CAAC,CAAA;QACtB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAA;QACnB,IAAI,CAAC,WAAW,EAAE,CAAA;IACpB,CAAC;IAED,IAAI;QACF,IAAI,CAAC,KAAK,EAAE,CAAA;IACd,CAAC;CACF","sourcesContent":["interface RetryProcessorArgs<ProcessorResult> {\n processor: () => Promise<ProcessorResult>\n onSuccess: (result: ProcessorResult) => void\n onFail: (error: Error) => void\n maxTryings?: number\n /**\n * Delay (in milliseconds) before the first retry, doubled at every further retry (exponential backoff).\n * Defaults to 0 (immediate retry).\n */\n retryDelay?: number\n}\n\nconst toError = (error: unknown): Error => (error instanceof Error ? error : new Error(String(error)))\n\nexport class RetryProcessor<ProcessorResult> {\n private readonly processor: () => Promise<ProcessorResult>\n private readonly onSuccess: (result: ProcessorResult) => void\n private readonly onFail: (error: Error) => void\n private readonly maxTryings: number\n private readonly retryDelay: number\n\n // State properties\n private currentTrying: number = 0\n private running: boolean = false\n\n constructor({ processor, onSuccess, onFail, maxTryings = 2, retryDelay = 0 }: RetryProcessorArgs<ProcessorResult>) {\n this.processor = processor\n this.onSuccess = onSuccess\n this.onFail = onFail\n this.maxTryings = maxTryings\n this.retryDelay = retryDelay\n }\n\n protected reset() {\n this.currentTrying = 0\n this.running = false\n }\n\n protected processNext(): void {\n this.currentTrying += 1\n\n // onSuccess runs outside the retried chain: if it throws, the processor must not run again\n this.processor().then(\n (result) => this.handleSuccess(result),\n (error) => this.handleProcessorError(toError(error))\n )\n }\n\n private handleSuccess(result: ProcessorResult): void {\n try {\n this.onSuccess(result)\n } catch (error) {\n this.onFail(toError(error))\n }\n }\n\n private handleProcessorError(error: Error): void {\n if (!this.running || this.currentTrying >= this.maxTryings) {\n // stopped or max attempts reached\n this.onFail(error)\n return\n }\n const delay = this.retryDelay * 2 ** (this.currentTrying - 1)\n if (delay <= 0) {\n this.processNext()\n return\n }\n setTimeout(() => {\n if (this.running) {\n this.processNext()\n } else {\n this.onFail(error)\n }\n }, delay)\n }\n\n // Public methods\n start(): void {\n this.currentTrying = 0\n this.running = true\n this.processNext()\n }\n\n stop(): void {\n this.reset()\n }\n}\n"]}
|