@openforis/arena-core 2.18.10 → 2.18.11
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/recordNodesUpdater/recordNodeDependentsApplicableUpdater.js +5 -1
- package/dist/esm/record/recordNodesUpdater/recordNodeDependentsApplicableUpdater.js.map +1 -1
- package/dist/esm/record/recordNodesUpdater/recordUpdateResult.js +5 -1
- package/dist/esm/record/recordNodesUpdater/recordUpdateResult.js.map +1 -1
- package/dist/esm/record/recordValidator/attributeValidator.js +76 -27
- package/dist/esm/record/recordValidator/attributeValidator.js.map +1 -1
- package/dist/record/recordNodesUpdater/recordNodeDependentsApplicableUpdater.js +5 -1
- package/dist/record/recordNodesUpdater/recordNodeDependentsApplicableUpdater.js.map +1 -1
- package/dist/record/recordNodesUpdater/recordUpdateResult.d.ts +1 -0
- package/dist/record/recordNodesUpdater/recordUpdateResult.js +4 -0
- package/dist/record/recordNodesUpdater/recordUpdateResult.js.map +1 -1
- package/dist/record/recordValidator/attributeValidator.js +76 -27
- package/dist/record/recordValidator/attributeValidator.js.map +1 -1
- package/package.json +1 -1
|
@@ -36,6 +36,9 @@ const updateDescendantsApplicability = ({ updateResult, nodeCtxChild, applicable
|
|
|
36
36
|
const { survey, sideEffect = false, clearNonApplicableValues = false } = params;
|
|
37
37
|
// only values entered by the user are reported as cleared (they need a confirmation)
|
|
38
38
|
let userValuesCleared = false;
|
|
39
|
+
// nodes added to the record all together: adding them one by one would copy the record nodes at every node
|
|
40
|
+
// (when sideEffect is false)
|
|
41
|
+
const nodesUpdated = {};
|
|
39
42
|
Records.visitDescendantsAndSelf(nodeCtxChild, (nodeDescendant) => {
|
|
40
43
|
const nodeDescendantCleared = clearNonApplicableValues && !applicable && Nodes.isValueNotBlank(nodeDescendant);
|
|
41
44
|
// values set by expressions (default values, read only attributes) are applied again when applicable: not reported
|
|
@@ -46,13 +49,14 @@ const updateDescendantsApplicability = ({ updateResult, nodeCtxChild, applicable
|
|
|
46
49
|
const nodeDescendantUpdated = nodeDescendantCleared
|
|
47
50
|
? Nodes.assocValue(nodeDescendant, null, { sideEffect })
|
|
48
51
|
: nodeDescendant;
|
|
49
|
-
|
|
52
|
+
nodesUpdated[nodeDescendantUpdated.uuid] = nodeDescendantUpdated;
|
|
50
53
|
if (userValueCleared) {
|
|
51
54
|
updateResult.addClearedDefUuid(nodeDescendant.nodeDefUuid);
|
|
52
55
|
userValuesCleared = true;
|
|
53
56
|
}
|
|
54
57
|
return false;
|
|
55
58
|
})(updateResult.record);
|
|
59
|
+
updateResult.addNodes(nodesUpdated, recordUpdateOptions);
|
|
56
60
|
// if a multiple entity became not applicable and it's empty, delete it instead of just marking descendants as not applicable
|
|
57
61
|
if (!applicable && clearNonApplicableValues) {
|
|
58
62
|
const { uuid: nodeCtxChildUuid, nodeDefUuid: nodeCtxChildDefUuid } = nodeCtxChild;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"recordNodeDependentsApplicableUpdater.js","sourceRoot":"","sources":["../../../../src/record/recordNodesUpdater/recordNodeDependentsApplicableUpdater.ts"],"names":[],"mappings":"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","sourcesContent":["import { ArenaRecordNode, NodePointer, Nodes } from '../../node'\nimport { NodeDefEntity, NodeDefs } from '../../nodeDef'\nimport { Surveys } from '../../survey'\nimport { Survey, SurveyDependencyType } from '../../survey/survey'\nimport { ArenaRecord } from '../record'\nimport { Records, RecordUpdateOptions } from '../records'\nimport { calculateBooleanValueNext } from './recordNodeDependentsBooleanUpdater'\nimport { createOrDeleteEnumeratedEntities } from './recordNodeDependentsEnumeratedEntitiesUpdater'\nimport { syncEnumeratingItemsEntities } from './recordNodeDependentsEnumeratingItemsUpdater'\nimport { addNodeAndDescendants } from './recordNodesCreator'\nimport { removeNodes } from './recordNodesDeleter'\nimport { getDependentNodePointersByType } from './recordNodesDependentsUpdaterCommons'\nimport { RecordNodeDependentsUpdateParams } from './recordNodeDependentsUpdateParams'\nimport { RecordUpdateResult } from './recordUpdateResult'\n\nconst extractNodePointersToUpdate = (params: { survey: Survey; record: ArenaRecord; node: ArenaRecordNode }) => {\n const { survey, record, node } = params\n\n const nodePointersToUpdate = getDependentNodePointersByType({\n survey,\n record,\n node,\n dependencyType: SurveyDependencyType.applicable,\n includeSelfWhenSourceIsAttribute: true,\n includeNewEntityChildPointers: true,\n })\n return nodePointersToUpdate\n}\n\nconst calculateApplicableNext = (params: {\n params: RecordNodeDependentsUpdateParams\n updateResult: RecordUpdateResult\n nodePointer: NodePointer\n nodeCtx: ArenaRecordNode\n}): Promise<boolean | undefined> =>\n calculateBooleanValueNext({\n ...params,\n getExpressions: (nodePointer) => NodeDefs.getApplicable(nodePointer.nodeDef),\n getValuePrev: Nodes.isChildApplicable,\n })\n\nconst updateDescendantsApplicability = ({\n updateResult,\n nodeCtxChild,\n applicable,\n params,\n recordUpdateOptions,\n}: {\n updateResult: RecordUpdateResult\n nodeCtxChild: ArenaRecordNode\n applicable: boolean\n params: RecordNodeDependentsUpdateParams\n recordUpdateOptions: RecordUpdateOptions\n}): void => {\n const { survey, sideEffect = false, clearNonApplicableValues = false } = params\n\n // only values entered by the user are reported as cleared (they need a confirmation)\n let userValuesCleared = false\n\n Records.visitDescendantsAndSelf(nodeCtxChild, (nodeDescendant): boolean => {\n const nodeDescendantCleared = clearNonApplicableValues && !applicable && Nodes.isValueNotBlank(nodeDescendant)\n // values set by expressions (default values, read only attributes) are applied again when applicable: not reported\n // (evaluated before clearing the value: with sideEffect the node is modified in place)\n const nodeDescendantDef = Surveys.getNodeDefByUuid({ survey, uuid: nodeDescendant.nodeDefUuid })\n const userValueCleared =\n nodeDescendantCleared && !NodeDefs.isReadOnly(nodeDescendantDef) && Nodes.hasUserInputValue(nodeDescendant)\n // Clear value if becoming non-applicable and parameter is enabled\n const nodeDescendantUpdated = nodeDescendantCleared\n ? Nodes.assocValue(nodeDescendant, null, { sideEffect })\n : nodeDescendant\n updateResult.addNode(nodeDescendantUpdated, recordUpdateOptions)\n if (userValueCleared) {\n updateResult.addClearedDefUuid(nodeDescendant.nodeDefUuid)\n userValuesCleared = true\n }\n return false\n })(updateResult.record)\n\n // if a multiple entity became not applicable and it's empty, delete it instead of just marking descendants as not applicable\n if (!applicable && clearNonApplicableValues) {\n const { uuid: nodeCtxChildUuid, nodeDefUuid: nodeCtxChildDefUuid } = nodeCtxChild\n const nodeCtxChildDef = Surveys.getNodeDefByUuid({ survey, uuid: nodeCtxChildDefUuid })\n if (NodeDefs.isMultipleEntity(nodeCtxChildDef) && Records.isNodeEmpty(nodeCtxChild)(updateResult.record)) {\n const deleteResult = removeNodes([nodeCtxChildUuid], recordUpdateOptions)(updateResult.record)\n updateResult.merge(deleteResult)\n if (userValuesCleared) {\n updateResult.addClearedDefUuid(nodeCtxChildDefUuid)\n }\n }\n }\n}\n\nconst createMissingApplicableMultipleEntities = async ({\n params,\n updateResult,\n parentNode,\n nodeDef,\n}: {\n params: RecordNodeDependentsUpdateParams\n updateResult: RecordUpdateResult\n parentNode: ArenaRecordNode\n nodeDef: NodeDefEntity\n}): Promise<void> => {\n if (!NodeDefs.isAutoCreateMinCountItems(nodeDef)) return\n\n const minCount = Nodes.getChildrenMinCount({ parentNode, nodeDef })\n if (Number.isNaN(minCount) || minCount <= 0) return\n\n const existingChildren = Records.getChildren(parentNode, nodeDef.uuid)(updateResult.record)\n const missingCount = Math.max(0, minCount - existingChildren.length)\n\n for (let index = 0; index < missingCount; index++) {\n const createParams = { ...params, record: updateResult.record, parentNode, nodeDef }\n // create nodes sequentially: every iteration works on the record updated by the previous one\n const childUpdateResult = await addNodeAndDescendants(createParams) // NOSONAR\n updateResult.merge(childUpdateResult)\n }\n}\n\nconst updateNodePointerApplicability = async ({\n params,\n updateResult,\n nodePointer,\n recordUpdateOptions,\n}: {\n params: RecordNodeDependentsUpdateParams\n updateResult: RecordUpdateResult\n nodePointer: NodePointer\n recordUpdateOptions: RecordUpdateOptions\n}): Promise<void> => {\n const { nodeCtx: nodeCtxNodePointer, nodeDef: nodeDefNodePointer } = nodePointer\n\n const nodeCtxUuid = nodeCtxNodePointer.uuid\n const nodeDefUuid = nodeDefNodePointer.uuid\n\n // nodeCtx could have been updated in a previous iteration\n const nodeCtx = updateResult.getNodeByUuid(nodeCtxUuid) ?? nodeCtxNodePointer\n\n const applicablePrev = Nodes.isChildApplicable(nodeCtx, nodeDefUuid)\n const applicable = await calculateApplicableNext({\n params,\n updateResult,\n nodePointer,\n nodeCtx,\n })\n if (applicable === undefined) {\n return\n }\n\n if (applicable && NodeDefs.isMultipleEntity(nodeDefNodePointer)) {\n await createMissingApplicableMultipleEntities({\n params,\n updateResult,\n parentNode: nodeCtx,\n nodeDef: nodeDefNodePointer as NodeDefEntity,\n })\n }\n\n // 4. persist updated applicability if changed, and return updated nodes\n if (applicablePrev === applicable) {\n return\n }\n // Applicability changed\n\n // update node and add it to nodes updated\n const nodeCtxUpdated = Nodes.assocChildApplicability(nodeCtx, nodeDefUuid, applicable)\n updateResult.addNode(nodeCtxUpdated, recordUpdateOptions)\n\n let nodeCtxChildren = Records.getChildren(nodeCtx, nodeDefUuid)(updateResult.record)\n\n if (NodeDefs.isMultipleEntity(nodeDefNodePointer) && NodeDefs.isEnumerate(nodeDefNodePointer as NodeDefEntity)) {\n const entityDef = nodeDefNodePointer as NodeDefEntity\n if (NodeDefs.getEnumeratingItemsExpression(entityDef)) {\n await syncEnumeratingItemsEntities({\n ...params,\n parentNode: nodeCtxUpdated,\n entityDef,\n updateResult,\n })\n } else {\n await createOrDeleteEnumeratedEntities({\n ...params,\n parentNode: nodeCtxUpdated,\n entityDef,\n updateResult,\n })\n }\n nodeCtxChildren = Records.getChildren(nodeCtx, nodeDefUuid)(updateResult.record)\n }\n for (const nodeCtxChild of nodeCtxChildren) {\n // add nodeCtxChild and its descendants to nodesUpdated\n updateDescendantsApplicability({\n updateResult,\n nodeCtxChild,\n applicable,\n params,\n recordUpdateOptions,\n })\n }\n}\n\nexport const updateSelfAndDependentsApplicable = async (\n params: RecordNodeDependentsUpdateParams\n): Promise<RecordUpdateResult> => {\n const { survey, record, node, sideEffect = false } = params\n\n const updateResult = new RecordUpdateResult({ record })\n\n const recordUpdateOptions: RecordUpdateOptions = { sideEffect }\n\n // 1. fetch dependent nodes\n const nodePointersToUpdate = extractNodePointersToUpdate({ survey, record, node })\n\n // 2. update expr to node and dependent nodes\n // NOTE: don't do it in parallel, same nodeCtx metadata could be overwritten\n for (const nodePointer of nodePointersToUpdate) {\n await updateNodePointerApplicability({ params, updateResult, nodePointer, recordUpdateOptions }) // NOSONAR\n }\n return updateResult\n}\n"]}
|
|
1
|
+
{"version":3,"file":"recordNodeDependentsApplicableUpdater.js","sourceRoot":"","sources":["../../../../src/record/recordNodesUpdater/recordNodeDependentsApplicableUpdater.ts"],"names":[],"mappings":"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","sourcesContent":["import { ArenaRecordNode, NodePointer, Nodes, NodesMap } from '../../node'\nimport { NodeDefEntity, NodeDefs } from '../../nodeDef'\nimport { Surveys } from '../../survey'\nimport { Survey, SurveyDependencyType } from '../../survey/survey'\nimport { ArenaRecord } from '../record'\nimport { Records, RecordUpdateOptions } from '../records'\nimport { calculateBooleanValueNext } from './recordNodeDependentsBooleanUpdater'\nimport { createOrDeleteEnumeratedEntities } from './recordNodeDependentsEnumeratedEntitiesUpdater'\nimport { syncEnumeratingItemsEntities } from './recordNodeDependentsEnumeratingItemsUpdater'\nimport { addNodeAndDescendants } from './recordNodesCreator'\nimport { removeNodes } from './recordNodesDeleter'\nimport { getDependentNodePointersByType } from './recordNodesDependentsUpdaterCommons'\nimport { RecordNodeDependentsUpdateParams } from './recordNodeDependentsUpdateParams'\nimport { RecordUpdateResult } from './recordUpdateResult'\n\nconst extractNodePointersToUpdate = (params: { survey: Survey; record: ArenaRecord; node: ArenaRecordNode }) => {\n const { survey, record, node } = params\n\n const nodePointersToUpdate = getDependentNodePointersByType({\n survey,\n record,\n node,\n dependencyType: SurveyDependencyType.applicable,\n includeSelfWhenSourceIsAttribute: true,\n includeNewEntityChildPointers: true,\n })\n return nodePointersToUpdate\n}\n\nconst calculateApplicableNext = (params: {\n params: RecordNodeDependentsUpdateParams\n updateResult: RecordUpdateResult\n nodePointer: NodePointer\n nodeCtx: ArenaRecordNode\n}): Promise<boolean | undefined> =>\n calculateBooleanValueNext({\n ...params,\n getExpressions: (nodePointer) => NodeDefs.getApplicable(nodePointer.nodeDef),\n getValuePrev: Nodes.isChildApplicable,\n })\n\nconst updateDescendantsApplicability = ({\n updateResult,\n nodeCtxChild,\n applicable,\n params,\n recordUpdateOptions,\n}: {\n updateResult: RecordUpdateResult\n nodeCtxChild: ArenaRecordNode\n applicable: boolean\n params: RecordNodeDependentsUpdateParams\n recordUpdateOptions: RecordUpdateOptions\n}): void => {\n const { survey, sideEffect = false, clearNonApplicableValues = false } = params\n\n // only values entered by the user are reported as cleared (they need a confirmation)\n let userValuesCleared = false\n\n // nodes added to the record all together: adding them one by one would copy the record nodes at every node\n // (when sideEffect is false)\n const nodesUpdated: NodesMap = {}\n\n Records.visitDescendantsAndSelf(nodeCtxChild, (nodeDescendant): boolean => {\n const nodeDescendantCleared = clearNonApplicableValues && !applicable && Nodes.isValueNotBlank(nodeDescendant)\n // values set by expressions (default values, read only attributes) are applied again when applicable: not reported\n // (evaluated before clearing the value: with sideEffect the node is modified in place)\n const nodeDescendantDef = Surveys.getNodeDefByUuid({ survey, uuid: nodeDescendant.nodeDefUuid })\n const userValueCleared =\n nodeDescendantCleared && !NodeDefs.isReadOnly(nodeDescendantDef) && Nodes.hasUserInputValue(nodeDescendant)\n // Clear value if becoming non-applicable and parameter is enabled\n const nodeDescendantUpdated = nodeDescendantCleared\n ? Nodes.assocValue(nodeDescendant, null, { sideEffect })\n : nodeDescendant\n nodesUpdated[nodeDescendantUpdated.uuid] = nodeDescendantUpdated\n if (userValueCleared) {\n updateResult.addClearedDefUuid(nodeDescendant.nodeDefUuid)\n userValuesCleared = true\n }\n return false\n })(updateResult.record)\n\n updateResult.addNodes(nodesUpdated, recordUpdateOptions)\n\n // if a multiple entity became not applicable and it's empty, delete it instead of just marking descendants as not applicable\n if (!applicable && clearNonApplicableValues) {\n const { uuid: nodeCtxChildUuid, nodeDefUuid: nodeCtxChildDefUuid } = nodeCtxChild\n const nodeCtxChildDef = Surveys.getNodeDefByUuid({ survey, uuid: nodeCtxChildDefUuid })\n if (NodeDefs.isMultipleEntity(nodeCtxChildDef) && Records.isNodeEmpty(nodeCtxChild)(updateResult.record)) {\n const deleteResult = removeNodes([nodeCtxChildUuid], recordUpdateOptions)(updateResult.record)\n updateResult.merge(deleteResult)\n if (userValuesCleared) {\n updateResult.addClearedDefUuid(nodeCtxChildDefUuid)\n }\n }\n }\n}\n\nconst createMissingApplicableMultipleEntities = async ({\n params,\n updateResult,\n parentNode,\n nodeDef,\n}: {\n params: RecordNodeDependentsUpdateParams\n updateResult: RecordUpdateResult\n parentNode: ArenaRecordNode\n nodeDef: NodeDefEntity\n}): Promise<void> => {\n if (!NodeDefs.isAutoCreateMinCountItems(nodeDef)) return\n\n const minCount = Nodes.getChildrenMinCount({ parentNode, nodeDef })\n if (Number.isNaN(minCount) || minCount <= 0) return\n\n const existingChildren = Records.getChildren(parentNode, nodeDef.uuid)(updateResult.record)\n const missingCount = Math.max(0, minCount - existingChildren.length)\n\n for (let index = 0; index < missingCount; index++) {\n const createParams = { ...params, record: updateResult.record, parentNode, nodeDef }\n // create nodes sequentially: every iteration works on the record updated by the previous one\n const childUpdateResult = await addNodeAndDescendants(createParams) // NOSONAR\n updateResult.merge(childUpdateResult)\n }\n}\n\nconst updateNodePointerApplicability = async ({\n params,\n updateResult,\n nodePointer,\n recordUpdateOptions,\n}: {\n params: RecordNodeDependentsUpdateParams\n updateResult: RecordUpdateResult\n nodePointer: NodePointer\n recordUpdateOptions: RecordUpdateOptions\n}): Promise<void> => {\n const { nodeCtx: nodeCtxNodePointer, nodeDef: nodeDefNodePointer } = nodePointer\n\n const nodeCtxUuid = nodeCtxNodePointer.uuid\n const nodeDefUuid = nodeDefNodePointer.uuid\n\n // nodeCtx could have been updated in a previous iteration\n const nodeCtx = updateResult.getNodeByUuid(nodeCtxUuid) ?? nodeCtxNodePointer\n\n const applicablePrev = Nodes.isChildApplicable(nodeCtx, nodeDefUuid)\n const applicable = await calculateApplicableNext({\n params,\n updateResult,\n nodePointer,\n nodeCtx,\n })\n if (applicable === undefined) {\n return\n }\n\n if (applicable && NodeDefs.isMultipleEntity(nodeDefNodePointer)) {\n await createMissingApplicableMultipleEntities({\n params,\n updateResult,\n parentNode: nodeCtx,\n nodeDef: nodeDefNodePointer as NodeDefEntity,\n })\n }\n\n // 4. persist updated applicability if changed, and return updated nodes\n if (applicablePrev === applicable) {\n return\n }\n // Applicability changed\n\n // update node and add it to nodes updated\n const nodeCtxUpdated = Nodes.assocChildApplicability(nodeCtx, nodeDefUuid, applicable)\n updateResult.addNode(nodeCtxUpdated, recordUpdateOptions)\n\n let nodeCtxChildren = Records.getChildren(nodeCtx, nodeDefUuid)(updateResult.record)\n\n if (NodeDefs.isMultipleEntity(nodeDefNodePointer) && NodeDefs.isEnumerate(nodeDefNodePointer as NodeDefEntity)) {\n const entityDef = nodeDefNodePointer as NodeDefEntity\n if (NodeDefs.getEnumeratingItemsExpression(entityDef)) {\n await syncEnumeratingItemsEntities({\n ...params,\n parentNode: nodeCtxUpdated,\n entityDef,\n updateResult,\n })\n } else {\n await createOrDeleteEnumeratedEntities({\n ...params,\n parentNode: nodeCtxUpdated,\n entityDef,\n updateResult,\n })\n }\n nodeCtxChildren = Records.getChildren(nodeCtx, nodeDefUuid)(updateResult.record)\n }\n for (const nodeCtxChild of nodeCtxChildren) {\n // add nodeCtxChild and its descendants to nodesUpdated\n updateDescendantsApplicability({\n updateResult,\n nodeCtxChild,\n applicable,\n params,\n recordUpdateOptions,\n })\n }\n}\n\nexport const updateSelfAndDependentsApplicable = async (\n params: RecordNodeDependentsUpdateParams\n): Promise<RecordUpdateResult> => {\n const { survey, record, node, sideEffect = false } = params\n\n const updateResult = new RecordUpdateResult({ record })\n\n const recordUpdateOptions: RecordUpdateOptions = { sideEffect }\n\n // 1. fetch dependent nodes\n const nodePointersToUpdate = extractNodePointersToUpdate({ survey, record, node })\n\n // 2. update expr to node and dependent nodes\n // NOTE: don't do it in parallel, same nodeCtx metadata could be overwritten\n for (const nodePointer of nodePointersToUpdate) {\n await updateNodePointerApplicability({ params, updateResult, nodePointer, recordUpdateOptions }) // NOSONAR\n }\n return updateResult\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { addNode } from '../_records/recordUpdater';
|
|
1
|
+
import { addNode, addNodes } from '../_records/recordUpdater';
|
|
2
2
|
export class RecordUpdateResult {
|
|
3
3
|
constructor(params) {
|
|
4
4
|
var _a, _b, _c;
|
|
@@ -15,6 +15,10 @@ export class RecordUpdateResult {
|
|
|
15
15
|
this.nodes[node.uuid] = node;
|
|
16
16
|
this.record = addNode(node, options)(this.record);
|
|
17
17
|
}
|
|
18
|
+
addNodes(nodes, options) {
|
|
19
|
+
Object.assign(this.nodes, nodes);
|
|
20
|
+
this.record = addNodes(nodes, options)(this.record);
|
|
21
|
+
}
|
|
18
22
|
addClearedDefUuid(nodeDefUuid) {
|
|
19
23
|
this.clearedDefUuids.add(nodeDefUuid);
|
|
20
24
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"recordUpdateResult.js","sourceRoot":"","sources":["../../../../src/record/recordNodesUpdater/recordUpdateResult.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,OAAO,EAAE,MAAM,2BAA2B,CAAA;
|
|
1
|
+
{"version":3,"file":"recordUpdateResult.js","sourceRoot":"","sources":["../../../../src/record/recordNodesUpdater/recordUpdateResult.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAA;AAG7D,MAAM,OAAO,kBAAkB;IAO7B,YAAY,MAMX;;QACC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAA;QAC3B,IAAI,CAAC,KAAK,SAAG,MAAM,CAAC,KAAK,mCAAI,EAAE,CAAA;QAC/B,IAAI,CAAC,YAAY,SAAG,MAAM,CAAC,YAAY,mCAAI,EAAE,CAAA;QAC7C,IAAI,CAAC,eAAe,SAAG,MAAM,CAAC,eAAe,mCAAI,IAAI,GAAG,EAAU,CAAA;QAClE,IAAI,CAAC,UAAU,GAAG,MAAM,CAAC,UAAU,CAAA;IACrC,CAAC;IAED,aAAa,CAAC,IAAY;QACxB,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;IACzB,CAAC;IAED,OAAO,CAAC,IAAqB,EAAE,OAA6B;QAC1D,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAA;QAC5B,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;IACnD,CAAC;IAED,QAAQ,CAAC,KAAe,EAAE,OAA6B;QACrD,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,KAAK,CAAC,CAAA;QAChC,IAAI,CAAC,MAAM,GAAG,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;IACrD,CAAC;IAED,iBAAiB,CAAC,WAAmB;QACnC,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,WAAW,CAAC,CAAA;IACvC,CAAC;IAED;;;;;;;OAOG;IACH,KAAK,CAAC,kBAAsC;QAC1C,IAAI,CAAC,MAAM,GAAG,kBAAkB,CAAC,MAAM,CAAA;QACvC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,kBAAkB,CAAC,KAAK,CAAC,CAAA;QACnD,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,YAAY,EAAE,kBAAkB,CAAC,YAAY,CAAC,CAAA;QACjE,KAAK,MAAM,WAAW,IAAI,kBAAkB,CAAC,eAAe,EAAE,CAAC;YAC7D,IAAI,CAAC,iBAAiB,CAAC,WAAW,CAAC,CAAA;QACrC,CAAC;QACD,OAAO,IAAI,CAAA;IACb,CAAC;CACF","sourcesContent":["import { ArenaRecordNode, NodesMap } from '../../node'\nimport { Validation } from '../../validation'\nimport { ArenaRecord } from '../record'\nimport { addNode, addNodes } from '../_records/recordUpdater'\nimport { RecordUpdateOptions } from '../records'\n\nexport class RecordUpdateResult {\n record: ArenaRecord\n nodes: NodesMap\n nodesDeleted: NodesMap\n clearedDefUuids: Set<string>\n validation?: Validation\n\n constructor(params: {\n record: ArenaRecord\n nodes?: NodesMap\n nodesDeleted?: NodesMap\n clearedDefUuids?: Set<string>\n validation?: Validation\n }) {\n this.record = params.record\n this.nodes = params.nodes ?? {}\n this.nodesDeleted = params.nodesDeleted ?? {}\n this.clearedDefUuids = params.clearedDefUuids ?? new Set<string>()\n this.validation = params.validation\n }\n\n getNodeByUuid(uuid: string) {\n return this.nodes[uuid]\n }\n\n addNode(node: ArenaRecordNode, options?: RecordUpdateOptions) {\n this.nodes[node.uuid] = node\n this.record = addNode(node, options)(this.record)\n }\n\n addNodes(nodes: NodesMap, options?: RecordUpdateOptions) {\n Object.assign(this.nodes, nodes)\n this.record = addNodes(nodes, options)(this.record)\n }\n\n addClearedDefUuid(nodeDefUuid: string) {\n this.clearedDefUuids.add(nodeDefUuid)\n }\n\n /**\n * Merges this record update result with the specified one.\n * The record of this record update result will be the one of the specified record update result\n * and the nodes of the specified one will be added to the nodes of this one.\n *\n * @param {!RecordUpdateResult} recordUpdateResult - The record update result to merge with.\n * @returns {RecordUpdateResult} - The updated object.\n */\n merge(recordUpdateResult: RecordUpdateResult): this {\n this.record = recordUpdateResult.record\n Object.assign(this.nodes, recordUpdateResult.nodes)\n Object.assign(this.nodesDeleted, recordUpdateResult.nodesDeleted)\n for (const nodeDefUuid of recordUpdateResult.clearedDefUuids) {\n this.addClearedDefUuid(nodeDefUuid)\n }\n return this\n }\n}\n"]}
|
|
@@ -22,25 +22,24 @@ import { AttributeKeyValidator, createEntityKeysCache } from './attributeKeyVali
|
|
|
22
22
|
import { AttributeTypeValidator } from './attributeTypeValidator';
|
|
23
23
|
import { AttributeUniqueValidator } from './attributeUniqueValidator';
|
|
24
24
|
const expressionEvaluator = new RecordExpressionEvaluator();
|
|
25
|
-
const
|
|
25
|
+
const _getSiblingNodeKeysGroup = (params) => {
|
|
26
26
|
const { survey, record, attribute, siblingKeyNodesCache } = params;
|
|
27
27
|
const attributeDef = Surveys.getNodeDefByUuid({ survey, uuid: attribute.nodeDefUuid });
|
|
28
28
|
const ancestorMultipleEntityDef = Surveys.getNodeDefAncestorMultipleEntity({ survey, nodeDef: attributeDef });
|
|
29
|
-
if (!ancestorMultipleEntityDef)
|
|
30
|
-
return
|
|
31
|
-
}
|
|
29
|
+
if (!ancestorMultipleEntityDef)
|
|
30
|
+
return undefined;
|
|
32
31
|
const ancestorMultipleEntity = Records.getAncestor(attribute, ancestorMultipleEntityDef.uuid)(record);
|
|
33
32
|
if (!ancestorMultipleEntity)
|
|
34
|
-
return
|
|
33
|
+
return undefined;
|
|
35
34
|
const ancestorMultipleEntityParent = Records.getParent(ancestorMultipleEntity)(record);
|
|
36
35
|
if (!ancestorMultipleEntityParent)
|
|
37
|
-
return
|
|
36
|
+
return undefined;
|
|
38
37
|
const cacheKey = `${ancestorMultipleEntityParent.uuid}_${ancestorMultipleEntityDef.uuid}`;
|
|
39
|
-
let
|
|
40
|
-
if (!
|
|
38
|
+
let group = siblingKeyNodesCache.get(cacheKey);
|
|
39
|
+
if (!group) {
|
|
41
40
|
const keyDefs = Surveys.getNodeDefKeys({ survey, nodeDef: ancestorMultipleEntityDef });
|
|
42
41
|
const entities = Records.getEntitySiblings(ancestorMultipleEntity, true)(record);
|
|
43
|
-
items = [];
|
|
42
|
+
const items = [];
|
|
44
43
|
for (const entity of entities) {
|
|
45
44
|
const keyNodes = Records.getEntityKeyNodes(survey, entity, undefined, keyDefs)(record);
|
|
46
45
|
for (const keyNode of keyNodes) {
|
|
@@ -50,9 +49,10 @@ const _getSiblingNodeKeys = (params) => {
|
|
|
50
49
|
items.push({ entityUuid: entity.uuid, keyNode, hasErrors: !!nodeValidation && !nodeValidation.valid });
|
|
51
50
|
}
|
|
52
51
|
}
|
|
53
|
-
|
|
52
|
+
group = { items };
|
|
53
|
+
siblingKeyNodesCache.set(cacheKey, group);
|
|
54
54
|
}
|
|
55
|
-
return
|
|
55
|
+
return { entityUuid: ancestorMultipleEntity.uuid, group };
|
|
56
56
|
};
|
|
57
57
|
const _getValidationMessagesWithDefault = (params) => {
|
|
58
58
|
const { survey, expression, defaultMessage } = params;
|
|
@@ -147,7 +147,8 @@ const validateAttribute = (params) => __awaiter(void 0, void 0, void 0, function
|
|
|
147
147
|
return ValidationFactory.createInstance();
|
|
148
148
|
});
|
|
149
149
|
const findSiblingKeyNodesToValidate = (params) => {
|
|
150
|
-
|
|
150
|
+
var _a;
|
|
151
|
+
const { survey, nodeDef, nodeParent: parentNode, attribute, nodeUuidsToValidate } = params;
|
|
151
152
|
if (!NodeDefs.isKey(nodeDef) ||
|
|
152
153
|
!parentNode ||
|
|
153
154
|
(NodeDefs.isAutoIncrementalKey(nodeDef) && attribute.created && Nodes.isDefaultValueApplied(attribute))) {
|
|
@@ -157,24 +158,71 @@ const findSiblingKeyNodesToValidate = (params) => {
|
|
|
157
158
|
}
|
|
158
159
|
// when a key attribute is updated, a sibling key attribute with the same value
|
|
159
160
|
// or a sibling attribute with a validation error (duplicate key) should be re-validated
|
|
160
|
-
const
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
161
|
+
const siblingNodeKeysGroup = _getSiblingNodeKeysGroup(params);
|
|
162
|
+
if (!siblingNodeKeysGroup)
|
|
163
|
+
return [];
|
|
164
|
+
const { entityUuid, group } = siblingNodeKeysGroup;
|
|
165
|
+
// key nodes in the nodes to validate are validated anyway: no need to compare their value
|
|
166
|
+
(_a = group.itemsToCompare) !== null && _a !== void 0 ? _a : (group.itemsToCompare = group.items.filter((item) => !nodeUuidsToValidate.has(item.keyNode.uuid)));
|
|
167
|
+
const result = [];
|
|
168
|
+
for (const { entityUuid: itemEntityUuid, keyNode } of group.itemsToCompare) {
|
|
169
|
+
if (itemEntityUuid !== entityUuid &&
|
|
170
|
+
NodeValues.isValueEqual({ survey, nodeDef, parentNode, value: keyNode.value, valueSearch: attribute.value })) {
|
|
171
|
+
result.push(keyNode);
|
|
169
172
|
}
|
|
170
|
-
}
|
|
171
|
-
return
|
|
173
|
+
}
|
|
174
|
+
return result.concat(_findSiblingKeyNodesWithErrorsNotAdded({ group, entityUuid }));
|
|
175
|
+
};
|
|
176
|
+
// key nodes with errors (in entities other than the attribute's one) are added only once per group
|
|
177
|
+
const _findSiblingKeyNodesWithErrorsNotAdded = (params) => {
|
|
178
|
+
const { group, entityUuid } = params;
|
|
179
|
+
if (group.firstEntityUuid === undefined) {
|
|
180
|
+
group.firstEntityUuid = entityUuid;
|
|
181
|
+
return group.items.filter((item) => item.hasErrors && item.entityUuid !== entityUuid).map((item) => item.keyNode);
|
|
182
|
+
}
|
|
183
|
+
if (group.allItemsWithErrorsAdded || group.firstEntityUuid === entityUuid)
|
|
184
|
+
return [];
|
|
185
|
+
group.allItemsWithErrorsAdded = true;
|
|
186
|
+
const { firstEntityUuid } = group;
|
|
187
|
+
return group.items.filter((item) => item.hasErrors && item.entityUuid === firstEntityUuid).map((item) => item.keyNode);
|
|
188
|
+
};
|
|
189
|
+
const findUniqueAttributeSiblingsToValidate = (params) => {
|
|
190
|
+
const { record, node, nodeDef, nodeParent, attributeSiblingsAddedCache } = params;
|
|
191
|
+
if (!nodeParent)
|
|
192
|
+
return [];
|
|
193
|
+
const nodeParentParent = Records.getParent(nodeParent)(record);
|
|
194
|
+
if (!nodeParentParent)
|
|
195
|
+
return [];
|
|
196
|
+
// the siblings of the attribute are the attributes in the other parent entities:
|
|
197
|
+
// after visiting attributes in 2 different parent entities, all the attributes of the group have been added
|
|
198
|
+
const cacheKey = `${nodeParentParent.uuid}_${nodeParent.nodeDefUuid}_${nodeDef.uuid}`;
|
|
199
|
+
const cached = attributeSiblingsAddedCache.get(cacheKey);
|
|
200
|
+
if (!cached) {
|
|
201
|
+
attributeSiblingsAddedCache.set(cacheKey, { firstParentEntity: nodeParent });
|
|
202
|
+
return Records.getAttributeSiblings(node, nodeDef)(record);
|
|
203
|
+
}
|
|
204
|
+
if (cached.allSiblingsAdded || cached.firstParentEntity.uuid === nodeParent.uuid)
|
|
205
|
+
return [];
|
|
206
|
+
cached.allSiblingsAdded = true;
|
|
207
|
+
return Records.getChildren(cached.firstParentEntity, nodeDef.uuid)(record);
|
|
172
208
|
};
|
|
173
209
|
const findSortedNodesToValidate = (params) => {
|
|
174
210
|
var _a;
|
|
175
211
|
const { survey, record, nodesArray } = params;
|
|
176
212
|
const result = [];
|
|
213
|
+
const resultUuids = new Set();
|
|
214
|
+
const addToResult = (nodes) => {
|
|
215
|
+
for (const node of nodes) {
|
|
216
|
+
if (!resultUuids.has(node.uuid)) {
|
|
217
|
+
resultUuids.add(node.uuid);
|
|
218
|
+
result.push(node);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
// every attribute in nodesArray is validated (it's included in its own dependents)
|
|
223
|
+
const nodeUuidsToValidate = new Set(nodesArray.map((node) => node.uuid));
|
|
177
224
|
const siblingKeyNodesCache = new Map();
|
|
225
|
+
const attributeSiblingsAddedCache = new Map();
|
|
178
226
|
for (const node of nodesArray) {
|
|
179
227
|
const nodeDef = Surveys.getNodeDefByUuid({ survey, uuid: node.nodeDefUuid });
|
|
180
228
|
if (!NodeDefs.isAttribute(nodeDef)) {
|
|
@@ -186,11 +234,12 @@ const findSortedNodesToValidate = (params) => {
|
|
|
186
234
|
...Records.getDependentNodePointers(survey, node, SurveyDependencyType.required)(record),
|
|
187
235
|
];
|
|
188
236
|
const nodeParent = Records.getParent(node)(record);
|
|
189
|
-
|
|
237
|
+
addToResult(NodePointers.getNodesFromNodePointers({ record, nodePointers: nodePointersAttributeAndDependents }));
|
|
238
|
+
addToResult(findSiblingKeyNodesToValidate(Object.assign(Object.assign({}, params), { nodeDef,
|
|
239
|
+
nodeParent, attribute: node, siblingKeyNodesCache,
|
|
240
|
+
nodeUuidsToValidate })));
|
|
190
241
|
if ((_a = NodeDefs.getValidations(nodeDef)) === null || _a === void 0 ? void 0 : _a.unique) {
|
|
191
|
-
|
|
192
|
-
result.push(sibling);
|
|
193
|
-
}
|
|
242
|
+
addToResult(findUniqueAttributeSiblingsToValidate({ record, node, nodeDef, nodeParent, attributeSiblingsAddedCache }));
|
|
194
243
|
}
|
|
195
244
|
}
|
|
196
245
|
return result;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"attributeValidator.js","sourceRoot":"","sources":["../../../../src/record/recordValidator/attributeValidator.ts"],"names":[],"mappings":"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","sourcesContent":["import { User } from '../../auth'\nimport { Dictionary } from '../../common'\nimport { Labels } from '../../language'\nimport { ArenaRecordNode, NodeKeys, Nodes, NodeValues } from '../../node'\nimport { NodeDef, NodeDefExpression, NodeDefProps, NodeDefs, NodeDefType } from '../../nodeDef'\nimport { AreaOfInterestProvider } from '../../nodeDefExpressionEvaluator/areaOfInterestProvider'\nimport { CategoryItemProvider } from '../../nodeDefExpressionEvaluator/categoryItemProvider'\nimport { TaxonProvider } from '../../nodeDefExpressionEvaluator/taxonProvider'\nimport { Survey, Surveys } from '../../survey'\nimport { SurveyDependencyType } from '../../survey/survey'\nimport { Objects } from '../../utils'\nimport {\n ValidationFactory,\n ValidationResult,\n ValidationResultFactory,\n ValidationSeverity,\n Validator,\n} from '../../validation'\nimport { ValidationFields } from '../../validation/validation'\nimport { NodePointers } from '../nodePointers'\nimport { ArenaRecord } from '../record'\nimport { RecordExpressionEvaluator } from '../recordExpressionEvaluator'\nimport { evaluateRequiredIf } from '../recordNodesUpdater/recordNodeDependentsRequiredUpdater'\nimport { Records } from '../records'\nimport { RecordValidations } from '../recordValidations'\nimport { AttributeKeyValidator, createEntityKeysCache, EntityKeysCache } from './attributeKeyValidator'\nimport { AttributeTypeValidator } from './attributeTypeValidator'\nimport { AttributeUniqueValidator } from './attributeUniqueValidator'\n\nconst expressionEvaluator = new RecordExpressionEvaluator()\n\nexport interface RecordValidatorParams {\n user: User\n survey: Survey\n categoryItemProvider?: CategoryItemProvider\n taxonProvider?: TaxonProvider\n areaOfInterestProvider?: AreaOfInterestProvider\n record: ArenaRecord\n}\n\nexport interface AttributeValidatorParams extends RecordValidatorParams {\n attribute: ArenaRecordNode\n}\n\nexport interface AttributesValidatorParams extends RecordValidatorParams {\n nodes: Dictionary<ArenaRecordNode>\n}\n\nexport interface SortedAttributesValidatorParams extends RecordValidatorParams {\n nodesArray: ArenaRecordNode[]\n}\n\ntype SiblingKeyNodeItem = { entityUuid: string; keyNode: ArenaRecordNode; hasErrors: boolean }\n\n// sibling key nodes are cached by ancestor multiple entity parent and def uuid during the same validation call:\n// when many entities are added to the same parent entity (e.g. data import), computing them for every updated\n// key attribute would take a time that grows quadratically with the number of entities\ntype SiblingKeyNodesCache = Map<string, SiblingKeyNodeItem[]>\n\nconst _getSiblingNodeKeys = (\n params: AttributeValidatorParams & { siblingKeyNodesCache?: SiblingKeyNodesCache }\n): SiblingKeyNodeItem[] => {\n const { survey, record, attribute, siblingKeyNodesCache } = params\n\n const attributeDef = Surveys.getNodeDefByUuid({ survey, uuid: attribute.nodeDefUuid })\n const ancestorMultipleEntityDef = Surveys.getNodeDefAncestorMultipleEntity({ survey, nodeDef: attributeDef })\n if (!ancestorMultipleEntityDef) {\n return []\n }\n const ancestorMultipleEntity = Records.getAncestor(attribute, ancestorMultipleEntityDef.uuid)(record)\n if (!ancestorMultipleEntity) return []\n\n const ancestorMultipleEntityParent = Records.getParent(ancestorMultipleEntity)(record)\n if (!ancestorMultipleEntityParent) return []\n\n const cacheKey = `${ancestorMultipleEntityParent.uuid}_${ancestorMultipleEntityDef.uuid}`\n let items = siblingKeyNodesCache?.get(cacheKey)\n if (!items) {\n const keyDefs = Surveys.getNodeDefKeys({ survey, nodeDef: ancestorMultipleEntityDef })\n const entities = Records.getEntitySiblings(ancestorMultipleEntity, true)(record)\n items = []\n for (const entity of entities) {\n const keyNodes = Records.getEntityKeyNodes(survey, entity, undefined, keyDefs)(record)\n for (const keyNode of keyNodes) {\n if (!keyNode) continue\n const nodeValidation =\n record.validation && RecordValidations.getValidationNode({ nodeUuid: keyNode.uuid })(record.validation)\n items.push({ entityUuid: entity.uuid, keyNode, hasErrors: !!nodeValidation && !nodeValidation.valid })\n }\n }\n siblingKeyNodesCache?.set(cacheKey, items)\n }\n return items.filter((item) => item.entityUuid !== ancestorMultipleEntity.uuid)\n}\n\nconst _getValidationMessagesWithDefault = (params: {\n survey: Survey\n expression: NodeDefExpression\n defaultMessage?: string\n}): Labels => {\n const { survey, expression, defaultMessage } = params\n const messages: Labels = { ...expression.messages }\n\n const languages = Surveys.getLanguages(survey)\n const defaultMessageDefined = Objects.isNotEmpty(defaultMessage)\n\n for (const lang of languages) {\n const customMessage = messages[lang]\n if (Objects.isEmpty(customMessage) && defaultMessageDefined) {\n messages[lang] = defaultMessage\n }\n }\n\n return messages\n}\n\nconst _isValueRequired = async (\n params: AttributeValidatorParams & { nodeDef: NodeDef<NodeDefType, NodeDefProps> }\n): Promise<boolean> => {\n const { record, attribute, nodeDef } = params\n if (NodeDefs.isKey(nodeDef) || NodeDefs.isRequired(nodeDef)) return true\n if (!NodeDefs.isRequiredIfDefined(nodeDef)) return false\n const parentNode = Records.getParent(attribute)(record)\n if (!parentNode) return false\n // evaluated here (not read from the parent node meta flag) so validation doesn't depend on up to date node meta\n // (e.g. records created before the \"required if\" condition was defined)\n return evaluateRequiredIf({ ...params, nodeCtx: parentNode, nodeDef })\n}\n\nconst _validateRequired =\n (params: AttributeValidatorParams & { nodeDef: NodeDef<NodeDefType, NodeDefProps> }) =>\n async (_field: string, node: any): Promise<ValidationResult> => {\n const valid = !Nodes.isValueBlank(node) || !(await _isValueRequired(params))\n return valid\n ? ValidationResultFactory.createInstance()\n : ValidationResultFactory.createInstance({\n key: 'record.attribute.valueRequired',\n severity: ValidationSeverity.error,\n valid,\n })\n }\n\n/**\n * Evaluates the validation expressions.\n */\nconst _validateNodeValidations =\n (params: {\n user: User\n survey: Survey\n record: ArenaRecord\n nodeDef: NodeDef<NodeDefType, NodeDefProps>\n areaOfInterestProvider?: AreaOfInterestProvider\n }) =>\n async (_propName: string, node: ArenaRecordNode): Promise<ValidationResult> => {\n const { user, survey, record, nodeDef, areaOfInterestProvider } = params\n if (Nodes.isValueBlank(node)) return ValidationResultFactory.createInstance()\n\n const validations = NodeDefs.getValidations(nodeDef)\n if (!validations?.expressions?.length) return ValidationResultFactory.createInstance()\n\n const applicableExpressionsEval = await expressionEvaluator.evalApplicableExpressions({\n user,\n survey,\n record,\n areaOfInterestProvider,\n nodeCtx: node,\n expressions: validations.expressions,\n stopAtFirstFound: false,\n })\n\n let validationResult = ValidationResultFactory.createInstance()\n\n for (const { expression, value: valid } of applicableExpressionsEval) {\n if (!valid) {\n const messages = _getValidationMessagesWithDefault({\n survey,\n expression,\n defaultMessage: expression.expression,\n })\n\n validationResult = ValidationResultFactory.createInstance({\n valid: false,\n key: 'record.attribute.customValidation',\n severity: expression.severity,\n messages,\n })\n break\n }\n }\n\n return validationResult\n }\n\nconst validateAttribute = async (params: AttributeValidatorParams & { entityKeysCache?: EntityKeysCache }) => {\n const { survey, record, attribute } = params\n if (Records.isNodeApplicable(attribute)(record)) {\n const nodeDef = Surveys.getNodeDefByUuid({ survey, uuid: attribute.nodeDefUuid })\n const validatorParams = { ...params, nodeDef }\n return new Validator().validate(attribute, {\n [NodeKeys.value]: [\n _validateRequired(validatorParams),\n AttributeTypeValidator.validateValueType(validatorParams),\n _validateNodeValidations(validatorParams),\n AttributeKeyValidator.validateAttributeKey(validatorParams),\n AttributeUniqueValidator.validateAttributeUnique(validatorParams),\n ],\n })\n }\n return ValidationFactory.createInstance()\n}\n\nconst findSiblingKeyNodesToValidate = (\n params: AttributeValidatorParams & {\n nodeDef: NodeDef<NodeDefType, NodeDefProps>\n nodeParent?: ArenaRecordNode\n siblingKeyNodesCache?: SiblingKeyNodesCache\n }\n) => {\n const { survey, nodeDef, nodeParent: parentNode, attribute } = params\n if (\n !NodeDefs.isKey(nodeDef) ||\n !parentNode ||\n (NodeDefs.isAutoIncrementalKey(nodeDef) && attribute.created && Nodes.isDefaultValueApplied(attribute))\n ) {\n // when a new node is created and the key is autoincremental, its value will be unique\n // and the validation of sibling key attributes is not necessary\n return []\n }\n // when a key attribute is updated, a sibling key attribute with the same value\n // or a sibling attribute with a validation error (duplicate key) should be re-validated\n const siblingNodeKeys = _getSiblingNodeKeys(params)\n const siblingNodeKeysWithSameValue: ArenaRecordNode[] = []\n const siblingNodeKeysWithErrors: ArenaRecordNode[] = []\n siblingNodeKeys.forEach(({ keyNode, hasErrors }) => {\n if (NodeValues.isValueEqual({ survey, nodeDef, parentNode, value: keyNode.value, valueSearch: attribute.value })) {\n siblingNodeKeysWithSameValue.push(keyNode)\n }\n if (hasErrors) {\n siblingNodeKeysWithErrors.push(keyNode)\n }\n })\n return [...siblingNodeKeysWithSameValue, ...siblingNodeKeysWithErrors]\n}\n\nconst findSortedNodesToValidate = (params: SortedAttributesValidatorParams): ArenaRecordNode[] => {\n const { survey, record, nodesArray } = params\n\n const result = []\n const siblingKeyNodesCache: SiblingKeyNodesCache = new Map()\n for (const node of nodesArray) {\n const nodeDef = Surveys.getNodeDefByUuid({ survey, uuid: node.nodeDefUuid })\n if (!NodeDefs.isAttribute(nodeDef)) {\n continue\n }\n // Get dependents and attribute itself\n const nodePointersAttributeAndDependents = [\n ...Records.getDependentNodePointers(survey, node, SurveyDependencyType.validations, true)(record),\n ...Records.getDependentNodePointers(survey, node, SurveyDependencyType.required)(record),\n ]\n\n const nodeParent = Records.getParent(node)(record)\n\n result.push(\n ...NodePointers.getNodesFromNodePointers({ record, nodePointers: nodePointersAttributeAndDependents }),\n ...findSiblingKeyNodesToValidate({ ...params, nodeDef, nodeParent, attribute: node, siblingKeyNodesCache })\n )\n\n if (NodeDefs.getValidations(nodeDef)?.unique) {\n for (const sibling of Records.getAttributeSiblings(node, nodeDef)(record)) {\n result.push(sibling)\n }\n }\n }\n\n return result\n}\n\nconst validateSelfAndDependentSortedAttributes = async (\n params: SortedAttributesValidatorParams\n): Promise<ValidationFields> => {\n const nodesToValidate: ArenaRecordNode[] = findSortedNodesToValidate(params)\n const validationsByNodeUuid: ValidationFields = {}\n // the record doesn't change during the validation: entity keys can be cached\n const entityKeysCache = createEntityKeysCache()\n\n for (const nodeToValidate of nodesToValidate) {\n const nodeUuid = nodeToValidate.uuid\n // Validate only attributes not deleted and not validated already\n if (!nodeToValidate.deleted && !validationsByNodeUuid[nodeUuid]) {\n // sequential: avoid concurrent item/taxon lookups on large node sets\n const attributeValidatorParams = { ...params, attribute: nodeToValidate, entityKeysCache }\n validationsByNodeUuid[nodeUuid] = await validateAttribute(attributeValidatorParams) // NOSONAR\n }\n }\n return validationsByNodeUuid\n}\n\nexport const AttributeValidator = {\n validateSelfAndDependentSortedAttributes,\n}\n"]}
|
|
1
|
+
{"version":3,"file":"attributeValidator.js","sourceRoot":"","sources":["../../../../src/record/recordValidator/attributeValidator.ts"],"names":[],"mappings":";;;;;;;;;AAGA,OAAO,EAAmB,QAAQ,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AACzE,OAAO,EAA4C,QAAQ,EAAe,MAAM,eAAe,CAAA;AAI/F,OAAO,EAAU,OAAO,EAAE,MAAM,cAAc,CAAA;AAC9C,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAA;AAC1D,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAA;AACrC,OAAO,EACL,iBAAiB,EAEjB,uBAAuB,EACvB,kBAAkB,EAClB,SAAS,GACV,MAAM,kBAAkB,CAAA;AAEzB,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAE9C,OAAO,EAAE,yBAAyB,EAAE,MAAM,8BAA8B,CAAA;AACxE,OAAO,EAAE,kBAAkB,EAAE,MAAM,2DAA2D,CAAA;AAC9F,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAA;AACpC,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAA;AACxD,OAAO,EAAE,qBAAqB,EAAE,qBAAqB,EAAmB,MAAM,yBAAyB,CAAA;AACvG,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAA;AACjE,OAAO,EAAE,wBAAwB,EAAE,MAAM,4BAA4B,CAAA;AAErE,MAAM,mBAAmB,GAAG,IAAI,yBAAyB,EAAE,CAAA;AAwC3D,MAAM,wBAAwB,GAAG,CAC/B,MAAiF,EAChB,EAAE;IACnE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,oBAAoB,EAAE,GAAG,MAAM,CAAA;IAElE,MAAM,YAAY,GAAG,OAAO,CAAC,gBAAgB,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,CAAC,WAAW,EAAE,CAAC,CAAA;IACtF,MAAM,yBAAyB,GAAG,OAAO,CAAC,gCAAgC,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,EAAE,CAAC,CAAA;IAC7G,IAAI,CAAC,yBAAyB;QAAE,OAAO,SAAS,CAAA;IAEhD,MAAM,sBAAsB,GAAG,OAAO,CAAC,WAAW,CAAC,SAAS,EAAE,yBAAyB,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAA;IACrG,IAAI,CAAC,sBAAsB;QAAE,OAAO,SAAS,CAAA;IAE7C,MAAM,4BAA4B,GAAG,OAAO,CAAC,SAAS,CAAC,sBAAsB,CAAC,CAAC,MAAM,CAAC,CAAA;IACtF,IAAI,CAAC,4BAA4B;QAAE,OAAO,SAAS,CAAA;IAEnD,MAAM,QAAQ,GAAG,GAAG,4BAA4B,CAAC,IAAI,IAAI,yBAAyB,CAAC,IAAI,EAAE,CAAA;IACzF,IAAI,KAAK,GAAG,oBAAoB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;IAC9C,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,OAAO,GAAG,OAAO,CAAC,cAAc,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,yBAAyB,EAAE,CAAC,CAAA;QACtF,MAAM,QAAQ,GAAG,OAAO,CAAC,iBAAiB,CAAC,sBAAsB,EAAE,IAAI,CAAC,CAAC,MAAM,CAAC,CAAA;QAChF,MAAM,KAAK,GAAyB,EAAE,CAAA;QACtC,KAAK,MAAM,MAAM,IAAI,QAAQ,EAAE,CAAC;YAC9B,MAAM,QAAQ,GAAG,OAAO,CAAC,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC,MAAM,CAAC,CAAA;YACtF,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;gBAC/B,IAAI,CAAC,OAAO;oBAAE,SAAQ;gBACtB,MAAM,cAAc,GAClB,MAAM,CAAC,UAAU,IAAI,iBAAiB,CAAC,iBAAiB,CAAC,EAAE,QAAQ,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,CAAA;gBACzG,KAAK,CAAC,IAAI,CAAC,EAAE,UAAU,EAAE,MAAM,CAAC,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC,CAAC,cAAc,IAAI,CAAC,cAAc,CAAC,KAAK,EAAE,CAAC,CAAA;YACxG,CAAC;QACH,CAAC;QACD,KAAK,GAAG,EAAE,KAAK,EAAE,CAAA;QACjB,oBAAoB,CAAC,GAAG,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAA;IAC3C,CAAC;IACD,OAAO,EAAE,UAAU,EAAE,sBAAsB,CAAC,IAAI,EAAE,KAAK,EAAE,CAAA;AAC3D,CAAC,CAAA;AAED,MAAM,iCAAiC,GAAG,CAAC,MAI1C,EAAU,EAAE;IACX,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,cAAc,EAAE,GAAG,MAAM,CAAA;IACrD,MAAM,QAAQ,qBAAgB,UAAU,CAAC,QAAQ,CAAE,CAAA;IAEnD,MAAM,SAAS,GAAG,OAAO,CAAC,YAAY,CAAC,MAAM,CAAC,CAAA;IAC9C,MAAM,qBAAqB,GAAG,OAAO,CAAC,UAAU,CAAC,cAAc,CAAC,CAAA;IAEhE,KAAK,MAAM,IAAI,IAAI,SAAS,EAAE,CAAC;QAC7B,MAAM,aAAa,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAA;QACpC,IAAI,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC,IAAI,qBAAqB,EAAE,CAAC;YAC5D,QAAQ,CAAC,IAAI,CAAC,GAAG,cAAc,CAAA;QACjC,CAAC;IACH,CAAC;IAED,OAAO,QAAQ,CAAA;AACjB,CAAC,CAAA;AAED,MAAM,gBAAgB,GAAG,CACvB,MAAkF,EAChE,EAAE;IACpB,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,GAAG,MAAM,CAAA;IAC7C,IAAI,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,QAAQ,CAAC,UAAU,CAAC,OAAO,CAAC;QAAE,OAAO,IAAI,CAAA;IACxE,IAAI,CAAC,QAAQ,CAAC,mBAAmB,CAAC,OAAO,CAAC;QAAE,OAAO,KAAK,CAAA;IACxD,MAAM,UAAU,GAAG,OAAO,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,CAAA;IACvD,IAAI,CAAC,UAAU;QAAE,OAAO,KAAK,CAAA;IAC7B,gHAAgH;IAChH,wEAAwE;IACxE,OAAO,kBAAkB,iCAAM,MAAM,KAAE,OAAO,EAAE,UAAU,EAAE,OAAO,IAAG,CAAA;AACxE,CAAC,CAAA,CAAA;AAED,MAAM,iBAAiB,GACrB,CAAC,MAAkF,EAAE,EAAE,CACvF,CAAO,MAAc,EAAE,IAAS,EAA6B,EAAE;IAC7D,MAAM,KAAK,GAAG,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,gBAAgB,CAAC,MAAM,CAAC,CAAC,CAAA;IAC5E,OAAO,KAAK;QACV,CAAC,CAAC,uBAAuB,CAAC,cAAc,EAAE;QAC1C,CAAC,CAAC,uBAAuB,CAAC,cAAc,CAAC;YACrC,GAAG,EAAE,gCAAgC;YACrC,QAAQ,EAAE,kBAAkB,CAAC,KAAK;YAClC,KAAK;SACN,CAAC,CAAA;AACR,CAAC,CAAA,CAAA;AAEH;;GAEG;AACH,MAAM,wBAAwB,GAC5B,CAAC,MAMA,EAAE,EAAE,CACL,CAAO,SAAiB,EAAE,IAAqB,EAA6B,EAAE;;IAC5E,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,sBAAsB,EAAE,GAAG,MAAM,CAAA;IACxE,IAAI,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC;QAAE,OAAO,uBAAuB,CAAC,cAAc,EAAE,CAAA;IAE7E,MAAM,WAAW,GAAG,QAAQ,CAAC,cAAc,CAAC,OAAO,CAAC,CAAA;IACpD,IAAI,EAAC,MAAA,WAAW,aAAX,WAAW,uBAAX,WAAW,CAAE,WAAW,0CAAE,MAAM,CAAA;QAAE,OAAO,uBAAuB,CAAC,cAAc,EAAE,CAAA;IAEtF,MAAM,yBAAyB,GAAG,MAAM,mBAAmB,CAAC,yBAAyB,CAAC;QACpF,IAAI;QACJ,MAAM;QACN,MAAM;QACN,sBAAsB;QACtB,OAAO,EAAE,IAAI;QACb,WAAW,EAAE,WAAW,CAAC,WAAW;QACpC,gBAAgB,EAAE,KAAK;KACxB,CAAC,CAAA;IAEF,IAAI,gBAAgB,GAAG,uBAAuB,CAAC,cAAc,EAAE,CAAA;IAE/D,KAAK,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,yBAAyB,EAAE,CAAC;QACrE,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,QAAQ,GAAG,iCAAiC,CAAC;gBACjD,MAAM;gBACN,UAAU;gBACV,cAAc,EAAE,UAAU,CAAC,UAAU;aACtC,CAAC,CAAA;YAEF,gBAAgB,GAAG,uBAAuB,CAAC,cAAc,CAAC;gBACxD,KAAK,EAAE,KAAK;gBACZ,GAAG,EAAE,mCAAmC;gBACxC,QAAQ,EAAE,UAAU,CAAC,QAAQ;gBAC7B,QAAQ;aACT,CAAC,CAAA;YACF,MAAK;QACP,CAAC;IACH,CAAC;IAED,OAAO,gBAAgB,CAAA;AACzB,CAAC,CAAA,CAAA;AAEH,MAAM,iBAAiB,GAAG,CAAO,MAAwE,EAAE,EAAE;IAC3G,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,CAAA;IAC5C,IAAI,OAAO,CAAC,gBAAgB,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC;QAChD,MAAM,OAAO,GAAG,OAAO,CAAC,gBAAgB,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,CAAC,WAAW,EAAE,CAAC,CAAA;QACjF,MAAM,eAAe,mCAAQ,MAAM,KAAE,OAAO,GAAE,CAAA;QAC9C,OAAO,IAAI,SAAS,EAAE,CAAC,QAAQ,CAAC,SAAS,EAAE;YACzC,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE;gBAChB,iBAAiB,CAAC,eAAe,CAAC;gBAClC,sBAAsB,CAAC,iBAAiB,CAAC,eAAe,CAAC;gBACzD,wBAAwB,CAAC,eAAe,CAAC;gBACzC,qBAAqB,CAAC,oBAAoB,CAAC,eAAe,CAAC;gBAC3D,wBAAwB,CAAC,uBAAuB,CAAC,eAAe,CAAC;aAClE;SACF,CAAC,CAAA;IACJ,CAAC;IACD,OAAO,iBAAiB,CAAC,cAAc,EAAE,CAAA;AAC3C,CAAC,CAAA,CAAA;AAED,MAAM,6BAA6B,GAAG,CACpC,MAKC,EACkB,EAAE;;IACrB,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,SAAS,EAAE,mBAAmB,EAAE,GAAG,MAAM,CAAA;IAC1F,IACE,CAAC,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC;QACxB,CAAC,UAAU;QACX,CAAC,QAAQ,CAAC,oBAAoB,CAAC,OAAO,CAAC,IAAI,SAAS,CAAC,OAAO,IAAI,KAAK,CAAC,qBAAqB,CAAC,SAAS,CAAC,CAAC,EACvG,CAAC;QACD,sFAAsF;QACtF,gEAAgE;QAChE,OAAO,EAAE,CAAA;IACX,CAAC;IACD,+EAA+E;IAC/E,wFAAwF;IACxF,MAAM,oBAAoB,GAAG,wBAAwB,CAAC,MAAM,CAAC,CAAA;IAC7D,IAAI,CAAC,oBAAoB;QAAE,OAAO,EAAE,CAAA;IAEpC,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,GAAG,oBAAoB,CAAA;IAClD,0FAA0F;IAC1F,MAAA,KAAK,CAAC,cAAc,oCAApB,KAAK,CAAC,cAAc,GAAK,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,mBAAmB,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,EAAA;IAElG,MAAM,MAAM,GAAsB,EAAE,CAAA;IACpC,KAAK,MAAM,EAAE,UAAU,EAAE,cAAc,EAAE,OAAO,EAAE,IAAI,KAAK,CAAC,cAAc,EAAE,CAAC;QAC3E,IACE,cAAc,KAAK,UAAU;YAC7B,UAAU,CAAC,YAAY,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,WAAW,EAAE,SAAS,CAAC,KAAK,EAAE,CAAC,EAC5G,CAAC;YACD,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;QACtB,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC,MAAM,CAAC,sCAAsC,CAAC,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,CAAC,CAAA;AACrF,CAAC,CAAA;AAED,mGAAmG;AACnG,MAAM,sCAAsC,GAAG,CAAC,MAG/C,EAAqB,EAAE;IACtB,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,GAAG,MAAM,CAAA;IACpC,IAAI,KAAK,CAAC,eAAe,KAAK,SAAS,EAAE,CAAC;QACxC,KAAK,CAAC,eAAe,GAAG,UAAU,CAAA;QAClC,OAAO,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,UAAU,KAAK,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;IACnH,CAAC;IACD,IAAI,KAAK,CAAC,uBAAuB,IAAI,KAAK,CAAC,eAAe,KAAK,UAAU;QAAE,OAAO,EAAE,CAAA;IACpF,KAAK,CAAC,uBAAuB,GAAG,IAAI,CAAA;IACpC,MAAM,EAAE,eAAe,EAAE,GAAG,KAAK,CAAA;IACjC,OAAO,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,UAAU,KAAK,eAAe,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;AACxH,CAAC,CAAA;AAMD,MAAM,qCAAqC,GAAG,CAAC,MAM9C,EAAqB,EAAE;IACtB,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,2BAA2B,EAAE,GAAG,MAAM,CAAA;IACjF,IAAI,CAAC,UAAU;QAAE,OAAO,EAAE,CAAA;IAC1B,MAAM,gBAAgB,GAAG,OAAO,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,CAAA;IAC9D,IAAI,CAAC,gBAAgB;QAAE,OAAO,EAAE,CAAA;IAChC,iFAAiF;IACjF,4GAA4G;IAC5G,MAAM,QAAQ,GAAG,GAAG,gBAAgB,CAAC,IAAI,IAAI,UAAU,CAAC,WAAW,IAAI,OAAO,CAAC,IAAI,EAAE,CAAA;IACrF,MAAM,MAAM,GAAG,2BAA2B,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;IACxD,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,2BAA2B,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE,iBAAiB,EAAE,UAAU,EAAE,CAAC,CAAA;QAC5E,OAAO,OAAO,CAAC,oBAAoB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,MAAM,CAAC,CAAA;IAC5D,CAAC;IACD,IAAI,MAAM,CAAC,gBAAgB,IAAI,MAAM,CAAC,iBAAiB,CAAC,IAAI,KAAK,UAAU,CAAC,IAAI;QAAE,OAAO,EAAE,CAAA;IAC3F,MAAM,CAAC,gBAAgB,GAAG,IAAI,CAAA;IAC9B,OAAO,OAAO,CAAC,WAAW,CAAC,MAAM,CAAC,iBAAiB,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAA;AAC5E,CAAC,CAAA;AAED,MAAM,yBAAyB,GAAG,CAAC,MAAuC,EAAqB,EAAE;;IAC/F,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,GAAG,MAAM,CAAA;IAE7C,MAAM,MAAM,GAAsB,EAAE,CAAA;IACpC,MAAM,WAAW,GAAG,IAAI,GAAG,EAAU,CAAA;IACrC,MAAM,WAAW,GAAG,CAAC,KAAwB,EAAE,EAAE;QAC/C,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;gBAChC,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;gBAC1B,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;YACnB,CAAC;QACH,CAAC;IACH,CAAC,CAAA;IACD,mFAAmF;IACnF,MAAM,mBAAmB,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAA;IACxE,MAAM,oBAAoB,GAAyB,IAAI,GAAG,EAAE,CAAA;IAC5D,MAAM,2BAA2B,GAAgC,IAAI,GAAG,EAAE,CAAA;IAC1E,KAAK,MAAM,IAAI,IAAI,UAAU,EAAE,CAAC;QAC9B,MAAM,OAAO,GAAG,OAAO,CAAC,gBAAgB,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC,CAAA;QAC5E,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,OAAO,CAAC,EAAE,CAAC;YACnC,SAAQ;QACV,CAAC;QACD,sCAAsC;QACtC,MAAM,kCAAkC,GAAG;YACzC,GAAG,OAAO,CAAC,wBAAwB,CAAC,MAAM,EAAE,IAAI,EAAE,oBAAoB,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC,MAAM,CAAC;YACjG,GAAG,OAAO,CAAC,wBAAwB,CAAC,MAAM,EAAE,IAAI,EAAE,oBAAoB,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC;SACzF,CAAA;QAED,MAAM,UAAU,GAAG,OAAO,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAA;QAElD,WAAW,CAAC,YAAY,CAAC,wBAAwB,CAAC,EAAE,MAAM,EAAE,YAAY,EAAE,kCAAkC,EAAE,CAAC,CAAC,CAAA;QAChH,WAAW,CACT,6BAA6B,iCACxB,MAAM,KACT,OAAO;YACP,UAAU,EACV,SAAS,EAAE,IAAI,EACf,oBAAoB;YACpB,mBAAmB,IACnB,CACH,CAAA;QAED,IAAI,MAAA,QAAQ,CAAC,cAAc,CAAC,OAAO,CAAC,0CAAE,MAAM,EAAE,CAAC;YAC7C,WAAW,CACT,qCAAqC,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,2BAA2B,EAAE,CAAC,CAC1G,CAAA;QACH,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAA;AACf,CAAC,CAAA;AAED,MAAM,wCAAwC,GAAG,CAC/C,MAAuC,EACZ,EAAE;IAC7B,MAAM,eAAe,GAAsB,yBAAyB,CAAC,MAAM,CAAC,CAAA;IAC5E,MAAM,qBAAqB,GAAqB,EAAE,CAAA;IAClD,6EAA6E;IAC7E,MAAM,eAAe,GAAG,qBAAqB,EAAE,CAAA;IAE/C,KAAK,MAAM,cAAc,IAAI,eAAe,EAAE,CAAC;QAC7C,MAAM,QAAQ,GAAG,cAAc,CAAC,IAAI,CAAA;QACpC,iEAAiE;QACjE,IAAI,CAAC,cAAc,CAAC,OAAO,IAAI,CAAC,qBAAqB,CAAC,QAAQ,CAAC,EAAE,CAAC;YAChE,qEAAqE;YACrE,MAAM,wBAAwB,mCAAQ,MAAM,KAAE,SAAS,EAAE,cAAc,EAAE,eAAe,GAAE,CAAA;YAC1F,qBAAqB,CAAC,QAAQ,CAAC,GAAG,MAAM,iBAAiB,CAAC,wBAAwB,CAAC,CAAA,CAAC,UAAU;QAChG,CAAC;IACH,CAAC;IACD,OAAO,qBAAqB,CAAA;AAC9B,CAAC,CAAA,CAAA;AAED,MAAM,CAAC,MAAM,kBAAkB,GAAG;IAChC,wCAAwC;CACzC,CAAA","sourcesContent":["import { User } from '../../auth'\nimport { Dictionary } from '../../common'\nimport { Labels } from '../../language'\nimport { ArenaRecordNode, NodeKeys, Nodes, NodeValues } from '../../node'\nimport { NodeDef, NodeDefExpression, NodeDefProps, NodeDefs, NodeDefType } from '../../nodeDef'\nimport { AreaOfInterestProvider } from '../../nodeDefExpressionEvaluator/areaOfInterestProvider'\nimport { CategoryItemProvider } from '../../nodeDefExpressionEvaluator/categoryItemProvider'\nimport { TaxonProvider } from '../../nodeDefExpressionEvaluator/taxonProvider'\nimport { Survey, Surveys } from '../../survey'\nimport { SurveyDependencyType } from '../../survey/survey'\nimport { Objects } from '../../utils'\nimport {\n ValidationFactory,\n ValidationResult,\n ValidationResultFactory,\n ValidationSeverity,\n Validator,\n} from '../../validation'\nimport { ValidationFields } from '../../validation/validation'\nimport { NodePointers } from '../nodePointers'\nimport { ArenaRecord } from '../record'\nimport { RecordExpressionEvaluator } from '../recordExpressionEvaluator'\nimport { evaluateRequiredIf } from '../recordNodesUpdater/recordNodeDependentsRequiredUpdater'\nimport { Records } from '../records'\nimport { RecordValidations } from '../recordValidations'\nimport { AttributeKeyValidator, createEntityKeysCache, EntityKeysCache } from './attributeKeyValidator'\nimport { AttributeTypeValidator } from './attributeTypeValidator'\nimport { AttributeUniqueValidator } from './attributeUniqueValidator'\n\nconst expressionEvaluator = new RecordExpressionEvaluator()\n\nexport interface RecordValidatorParams {\n user: User\n survey: Survey\n categoryItemProvider?: CategoryItemProvider\n taxonProvider?: TaxonProvider\n areaOfInterestProvider?: AreaOfInterestProvider\n record: ArenaRecord\n}\n\nexport interface AttributeValidatorParams extends RecordValidatorParams {\n attribute: ArenaRecordNode\n}\n\nexport interface AttributesValidatorParams extends RecordValidatorParams {\n nodes: Dictionary<ArenaRecordNode>\n}\n\nexport interface SortedAttributesValidatorParams extends RecordValidatorParams {\n nodesArray: ArenaRecordNode[]\n}\n\ntype SiblingKeyNodeItem = { entityUuid: string; keyNode: ArenaRecordNode; hasErrors: boolean }\n\ntype SiblingKeyNodesGroup = {\n items: SiblingKeyNodeItem[]\n // items whose key node is not validated anyway (not in the nodes to validate): only these are compared by value\n itemsToCompare?: SiblingKeyNodeItem[]\n // entity of the first key attribute validated in this group: its key nodes with errors are not added\n // until a key attribute of another entity is validated\n firstEntityUuid?: string\n allItemsWithErrorsAdded?: boolean\n}\n\n// sibling key nodes are cached by ancestor multiple entity parent and def uuid during the same validation call:\n// when many entities are added to the same parent entity (e.g. data import), computing them for every updated\n// key attribute would take a time that grows quadratically with the number of entities\ntype SiblingKeyNodesCache = Map<string, SiblingKeyNodesGroup>\n\nconst _getSiblingNodeKeysGroup = (\n params: AttributeValidatorParams & { siblingKeyNodesCache: SiblingKeyNodesCache }\n): { entityUuid: string; group: SiblingKeyNodesGroup } | undefined => {\n const { survey, record, attribute, siblingKeyNodesCache } = params\n\n const attributeDef = Surveys.getNodeDefByUuid({ survey, uuid: attribute.nodeDefUuid })\n const ancestorMultipleEntityDef = Surveys.getNodeDefAncestorMultipleEntity({ survey, nodeDef: attributeDef })\n if (!ancestorMultipleEntityDef) return undefined\n\n const ancestorMultipleEntity = Records.getAncestor(attribute, ancestorMultipleEntityDef.uuid)(record)\n if (!ancestorMultipleEntity) return undefined\n\n const ancestorMultipleEntityParent = Records.getParent(ancestorMultipleEntity)(record)\n if (!ancestorMultipleEntityParent) return undefined\n\n const cacheKey = `${ancestorMultipleEntityParent.uuid}_${ancestorMultipleEntityDef.uuid}`\n let group = siblingKeyNodesCache.get(cacheKey)\n if (!group) {\n const keyDefs = Surveys.getNodeDefKeys({ survey, nodeDef: ancestorMultipleEntityDef })\n const entities = Records.getEntitySiblings(ancestorMultipleEntity, true)(record)\n const items: SiblingKeyNodeItem[] = []\n for (const entity of entities) {\n const keyNodes = Records.getEntityKeyNodes(survey, entity, undefined, keyDefs)(record)\n for (const keyNode of keyNodes) {\n if (!keyNode) continue\n const nodeValidation =\n record.validation && RecordValidations.getValidationNode({ nodeUuid: keyNode.uuid })(record.validation)\n items.push({ entityUuid: entity.uuid, keyNode, hasErrors: !!nodeValidation && !nodeValidation.valid })\n }\n }\n group = { items }\n siblingKeyNodesCache.set(cacheKey, group)\n }\n return { entityUuid: ancestorMultipleEntity.uuid, group }\n}\n\nconst _getValidationMessagesWithDefault = (params: {\n survey: Survey\n expression: NodeDefExpression\n defaultMessage?: string\n}): Labels => {\n const { survey, expression, defaultMessage } = params\n const messages: Labels = { ...expression.messages }\n\n const languages = Surveys.getLanguages(survey)\n const defaultMessageDefined = Objects.isNotEmpty(defaultMessage)\n\n for (const lang of languages) {\n const customMessage = messages[lang]\n if (Objects.isEmpty(customMessage) && defaultMessageDefined) {\n messages[lang] = defaultMessage\n }\n }\n\n return messages\n}\n\nconst _isValueRequired = async (\n params: AttributeValidatorParams & { nodeDef: NodeDef<NodeDefType, NodeDefProps> }\n): Promise<boolean> => {\n const { record, attribute, nodeDef } = params\n if (NodeDefs.isKey(nodeDef) || NodeDefs.isRequired(nodeDef)) return true\n if (!NodeDefs.isRequiredIfDefined(nodeDef)) return false\n const parentNode = Records.getParent(attribute)(record)\n if (!parentNode) return false\n // evaluated here (not read from the parent node meta flag) so validation doesn't depend on up to date node meta\n // (e.g. records created before the \"required if\" condition was defined)\n return evaluateRequiredIf({ ...params, nodeCtx: parentNode, nodeDef })\n}\n\nconst _validateRequired =\n (params: AttributeValidatorParams & { nodeDef: NodeDef<NodeDefType, NodeDefProps> }) =>\n async (_field: string, node: any): Promise<ValidationResult> => {\n const valid = !Nodes.isValueBlank(node) || !(await _isValueRequired(params))\n return valid\n ? ValidationResultFactory.createInstance()\n : ValidationResultFactory.createInstance({\n key: 'record.attribute.valueRequired',\n severity: ValidationSeverity.error,\n valid,\n })\n }\n\n/**\n * Evaluates the validation expressions.\n */\nconst _validateNodeValidations =\n (params: {\n user: User\n survey: Survey\n record: ArenaRecord\n nodeDef: NodeDef<NodeDefType, NodeDefProps>\n areaOfInterestProvider?: AreaOfInterestProvider\n }) =>\n async (_propName: string, node: ArenaRecordNode): Promise<ValidationResult> => {\n const { user, survey, record, nodeDef, areaOfInterestProvider } = params\n if (Nodes.isValueBlank(node)) return ValidationResultFactory.createInstance()\n\n const validations = NodeDefs.getValidations(nodeDef)\n if (!validations?.expressions?.length) return ValidationResultFactory.createInstance()\n\n const applicableExpressionsEval = await expressionEvaluator.evalApplicableExpressions({\n user,\n survey,\n record,\n areaOfInterestProvider,\n nodeCtx: node,\n expressions: validations.expressions,\n stopAtFirstFound: false,\n })\n\n let validationResult = ValidationResultFactory.createInstance()\n\n for (const { expression, value: valid } of applicableExpressionsEval) {\n if (!valid) {\n const messages = _getValidationMessagesWithDefault({\n survey,\n expression,\n defaultMessage: expression.expression,\n })\n\n validationResult = ValidationResultFactory.createInstance({\n valid: false,\n key: 'record.attribute.customValidation',\n severity: expression.severity,\n messages,\n })\n break\n }\n }\n\n return validationResult\n }\n\nconst validateAttribute = async (params: AttributeValidatorParams & { entityKeysCache?: EntityKeysCache }) => {\n const { survey, record, attribute } = params\n if (Records.isNodeApplicable(attribute)(record)) {\n const nodeDef = Surveys.getNodeDefByUuid({ survey, uuid: attribute.nodeDefUuid })\n const validatorParams = { ...params, nodeDef }\n return new Validator().validate(attribute, {\n [NodeKeys.value]: [\n _validateRequired(validatorParams),\n AttributeTypeValidator.validateValueType(validatorParams),\n _validateNodeValidations(validatorParams),\n AttributeKeyValidator.validateAttributeKey(validatorParams),\n AttributeUniqueValidator.validateAttributeUnique(validatorParams),\n ],\n })\n }\n return ValidationFactory.createInstance()\n}\n\nconst findSiblingKeyNodesToValidate = (\n params: AttributeValidatorParams & {\n nodeDef: NodeDef<NodeDefType, NodeDefProps>\n nodeParent?: ArenaRecordNode\n siblingKeyNodesCache: SiblingKeyNodesCache\n nodeUuidsToValidate: Set<string>\n }\n): ArenaRecordNode[] => {\n const { survey, nodeDef, nodeParent: parentNode, attribute, nodeUuidsToValidate } = params\n if (\n !NodeDefs.isKey(nodeDef) ||\n !parentNode ||\n (NodeDefs.isAutoIncrementalKey(nodeDef) && attribute.created && Nodes.isDefaultValueApplied(attribute))\n ) {\n // when a new node is created and the key is autoincremental, its value will be unique\n // and the validation of sibling key attributes is not necessary\n return []\n }\n // when a key attribute is updated, a sibling key attribute with the same value\n // or a sibling attribute with a validation error (duplicate key) should be re-validated\n const siblingNodeKeysGroup = _getSiblingNodeKeysGroup(params)\n if (!siblingNodeKeysGroup) return []\n\n const { entityUuid, group } = siblingNodeKeysGroup\n // key nodes in the nodes to validate are validated anyway: no need to compare their value\n group.itemsToCompare ??= group.items.filter((item) => !nodeUuidsToValidate.has(item.keyNode.uuid))\n\n const result: ArenaRecordNode[] = []\n for (const { entityUuid: itemEntityUuid, keyNode } of group.itemsToCompare) {\n if (\n itemEntityUuid !== entityUuid &&\n NodeValues.isValueEqual({ survey, nodeDef, parentNode, value: keyNode.value, valueSearch: attribute.value })\n ) {\n result.push(keyNode)\n }\n }\n return result.concat(_findSiblingKeyNodesWithErrorsNotAdded({ group, entityUuid }))\n}\n\n// key nodes with errors (in entities other than the attribute's one) are added only once per group\nconst _findSiblingKeyNodesWithErrorsNotAdded = (params: {\n group: SiblingKeyNodesGroup\n entityUuid: string\n}): ArenaRecordNode[] => {\n const { group, entityUuid } = params\n if (group.firstEntityUuid === undefined) {\n group.firstEntityUuid = entityUuid\n return group.items.filter((item) => item.hasErrors && item.entityUuid !== entityUuid).map((item) => item.keyNode)\n }\n if (group.allItemsWithErrorsAdded || group.firstEntityUuid === entityUuid) return []\n group.allItemsWithErrorsAdded = true\n const { firstEntityUuid } = group\n return group.items.filter((item) => item.hasErrors && item.entityUuid === firstEntityUuid).map((item) => item.keyNode)\n}\n\n// attribute siblings (same attribute def in the sibling entities) added to the nodes to validate,\n// by parent entity def and parent of the parent entity\ntype AttributeSiblingsAddedCache = Map<string, { firstParentEntity: ArenaRecordNode; allSiblingsAdded?: boolean }>\n\nconst findUniqueAttributeSiblingsToValidate = (params: {\n record: ArenaRecord\n node: ArenaRecordNode\n nodeDef: NodeDef<NodeDefType, NodeDefProps>\n nodeParent?: ArenaRecordNode\n attributeSiblingsAddedCache: AttributeSiblingsAddedCache\n}): ArenaRecordNode[] => {\n const { record, node, nodeDef, nodeParent, attributeSiblingsAddedCache } = params\n if (!nodeParent) return []\n const nodeParentParent = Records.getParent(nodeParent)(record)\n if (!nodeParentParent) return []\n // the siblings of the attribute are the attributes in the other parent entities:\n // after visiting attributes in 2 different parent entities, all the attributes of the group have been added\n const cacheKey = `${nodeParentParent.uuid}_${nodeParent.nodeDefUuid}_${nodeDef.uuid}`\n const cached = attributeSiblingsAddedCache.get(cacheKey)\n if (!cached) {\n attributeSiblingsAddedCache.set(cacheKey, { firstParentEntity: nodeParent })\n return Records.getAttributeSiblings(node, nodeDef)(record)\n }\n if (cached.allSiblingsAdded || cached.firstParentEntity.uuid === nodeParent.uuid) return []\n cached.allSiblingsAdded = true\n return Records.getChildren(cached.firstParentEntity, nodeDef.uuid)(record)\n}\n\nconst findSortedNodesToValidate = (params: SortedAttributesValidatorParams): ArenaRecordNode[] => {\n const { survey, record, nodesArray } = params\n\n const result: ArenaRecordNode[] = []\n const resultUuids = new Set<string>()\n const addToResult = (nodes: ArenaRecordNode[]) => {\n for (const node of nodes) {\n if (!resultUuids.has(node.uuid)) {\n resultUuids.add(node.uuid)\n result.push(node)\n }\n }\n }\n // every attribute in nodesArray is validated (it's included in its own dependents)\n const nodeUuidsToValidate = new Set(nodesArray.map((node) => node.uuid))\n const siblingKeyNodesCache: SiblingKeyNodesCache = new Map()\n const attributeSiblingsAddedCache: AttributeSiblingsAddedCache = new Map()\n for (const node of nodesArray) {\n const nodeDef = Surveys.getNodeDefByUuid({ survey, uuid: node.nodeDefUuid })\n if (!NodeDefs.isAttribute(nodeDef)) {\n continue\n }\n // Get dependents and attribute itself\n const nodePointersAttributeAndDependents = [\n ...Records.getDependentNodePointers(survey, node, SurveyDependencyType.validations, true)(record),\n ...Records.getDependentNodePointers(survey, node, SurveyDependencyType.required)(record),\n ]\n\n const nodeParent = Records.getParent(node)(record)\n\n addToResult(NodePointers.getNodesFromNodePointers({ record, nodePointers: nodePointersAttributeAndDependents }))\n addToResult(\n findSiblingKeyNodesToValidate({\n ...params,\n nodeDef,\n nodeParent,\n attribute: node,\n siblingKeyNodesCache,\n nodeUuidsToValidate,\n })\n )\n\n if (NodeDefs.getValidations(nodeDef)?.unique) {\n addToResult(\n findUniqueAttributeSiblingsToValidate({ record, node, nodeDef, nodeParent, attributeSiblingsAddedCache })\n )\n }\n }\n\n return result\n}\n\nconst validateSelfAndDependentSortedAttributes = async (\n params: SortedAttributesValidatorParams\n): Promise<ValidationFields> => {\n const nodesToValidate: ArenaRecordNode[] = findSortedNodesToValidate(params)\n const validationsByNodeUuid: ValidationFields = {}\n // the record doesn't change during the validation: entity keys can be cached\n const entityKeysCache = createEntityKeysCache()\n\n for (const nodeToValidate of nodesToValidate) {\n const nodeUuid = nodeToValidate.uuid\n // Validate only attributes not deleted and not validated already\n if (!nodeToValidate.deleted && !validationsByNodeUuid[nodeUuid]) {\n // sequential: avoid concurrent item/taxon lookups on large node sets\n const attributeValidatorParams = { ...params, attribute: nodeToValidate, entityKeysCache }\n validationsByNodeUuid[nodeUuid] = await validateAttribute(attributeValidatorParams) // NOSONAR\n }\n }\n return validationsByNodeUuid\n}\n\nexport const AttributeValidator = {\n validateSelfAndDependentSortedAttributes,\n}\n"]}
|
|
@@ -39,6 +39,9 @@ const updateDescendantsApplicability = ({ updateResult, nodeCtxChild, applicable
|
|
|
39
39
|
const { survey, sideEffect = false, clearNonApplicableValues = false } = params;
|
|
40
40
|
// only values entered by the user are reported as cleared (they need a confirmation)
|
|
41
41
|
let userValuesCleared = false;
|
|
42
|
+
// nodes added to the record all together: adding them one by one would copy the record nodes at every node
|
|
43
|
+
// (when sideEffect is false)
|
|
44
|
+
const nodesUpdated = {};
|
|
42
45
|
records_1.Records.visitDescendantsAndSelf(nodeCtxChild, (nodeDescendant) => {
|
|
43
46
|
const nodeDescendantCleared = clearNonApplicableValues && !applicable && node_1.Nodes.isValueNotBlank(nodeDescendant);
|
|
44
47
|
// values set by expressions (default values, read only attributes) are applied again when applicable: not reported
|
|
@@ -49,13 +52,14 @@ const updateDescendantsApplicability = ({ updateResult, nodeCtxChild, applicable
|
|
|
49
52
|
const nodeDescendantUpdated = nodeDescendantCleared
|
|
50
53
|
? node_1.Nodes.assocValue(nodeDescendant, null, { sideEffect })
|
|
51
54
|
: nodeDescendant;
|
|
52
|
-
|
|
55
|
+
nodesUpdated[nodeDescendantUpdated.uuid] = nodeDescendantUpdated;
|
|
53
56
|
if (userValueCleared) {
|
|
54
57
|
updateResult.addClearedDefUuid(nodeDescendant.nodeDefUuid);
|
|
55
58
|
userValuesCleared = true;
|
|
56
59
|
}
|
|
57
60
|
return false;
|
|
58
61
|
})(updateResult.record);
|
|
62
|
+
updateResult.addNodes(nodesUpdated, recordUpdateOptions);
|
|
59
63
|
// if a multiple entity became not applicable and it's empty, delete it instead of just marking descendants as not applicable
|
|
60
64
|
if (!applicable && clearNonApplicableValues) {
|
|
61
65
|
const { uuid: nodeCtxChildUuid, nodeDefUuid: nodeCtxChildDefUuid } = nodeCtxChild;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"recordNodeDependentsApplicableUpdater.js","sourceRoot":"","sources":["../../../src/record/recordNodesUpdater/recordNodeDependentsApplicableUpdater.ts"],"names":[],"mappings":"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","sourcesContent":["import { ArenaRecordNode, NodePointer, Nodes } from '../../node'\nimport { NodeDefEntity, NodeDefs } from '../../nodeDef'\nimport { Surveys } from '../../survey'\nimport { Survey, SurveyDependencyType } from '../../survey/survey'\nimport { ArenaRecord } from '../record'\nimport { Records, RecordUpdateOptions } from '../records'\nimport { calculateBooleanValueNext } from './recordNodeDependentsBooleanUpdater'\nimport { createOrDeleteEnumeratedEntities } from './recordNodeDependentsEnumeratedEntitiesUpdater'\nimport { syncEnumeratingItemsEntities } from './recordNodeDependentsEnumeratingItemsUpdater'\nimport { addNodeAndDescendants } from './recordNodesCreator'\nimport { removeNodes } from './recordNodesDeleter'\nimport { getDependentNodePointersByType } from './recordNodesDependentsUpdaterCommons'\nimport { RecordNodeDependentsUpdateParams } from './recordNodeDependentsUpdateParams'\nimport { RecordUpdateResult } from './recordUpdateResult'\n\nconst extractNodePointersToUpdate = (params: { survey: Survey; record: ArenaRecord; node: ArenaRecordNode }) => {\n const { survey, record, node } = params\n\n const nodePointersToUpdate = getDependentNodePointersByType({\n survey,\n record,\n node,\n dependencyType: SurveyDependencyType.applicable,\n includeSelfWhenSourceIsAttribute: true,\n includeNewEntityChildPointers: true,\n })\n return nodePointersToUpdate\n}\n\nconst calculateApplicableNext = (params: {\n params: RecordNodeDependentsUpdateParams\n updateResult: RecordUpdateResult\n nodePointer: NodePointer\n nodeCtx: ArenaRecordNode\n}): Promise<boolean | undefined> =>\n calculateBooleanValueNext({\n ...params,\n getExpressions: (nodePointer) => NodeDefs.getApplicable(nodePointer.nodeDef),\n getValuePrev: Nodes.isChildApplicable,\n })\n\nconst updateDescendantsApplicability = ({\n updateResult,\n nodeCtxChild,\n applicable,\n params,\n recordUpdateOptions,\n}: {\n updateResult: RecordUpdateResult\n nodeCtxChild: ArenaRecordNode\n applicable: boolean\n params: RecordNodeDependentsUpdateParams\n recordUpdateOptions: RecordUpdateOptions\n}): void => {\n const { survey, sideEffect = false, clearNonApplicableValues = false } = params\n\n // only values entered by the user are reported as cleared (they need a confirmation)\n let userValuesCleared = false\n\n Records.visitDescendantsAndSelf(nodeCtxChild, (nodeDescendant): boolean => {\n const nodeDescendantCleared = clearNonApplicableValues && !applicable && Nodes.isValueNotBlank(nodeDescendant)\n // values set by expressions (default values, read only attributes) are applied again when applicable: not reported\n // (evaluated before clearing the value: with sideEffect the node is modified in place)\n const nodeDescendantDef = Surveys.getNodeDefByUuid({ survey, uuid: nodeDescendant.nodeDefUuid })\n const userValueCleared =\n nodeDescendantCleared && !NodeDefs.isReadOnly(nodeDescendantDef) && Nodes.hasUserInputValue(nodeDescendant)\n // Clear value if becoming non-applicable and parameter is enabled\n const nodeDescendantUpdated = nodeDescendantCleared\n ? Nodes.assocValue(nodeDescendant, null, { sideEffect })\n : nodeDescendant\n updateResult.addNode(nodeDescendantUpdated, recordUpdateOptions)\n if (userValueCleared) {\n updateResult.addClearedDefUuid(nodeDescendant.nodeDefUuid)\n userValuesCleared = true\n }\n return false\n })(updateResult.record)\n\n // if a multiple entity became not applicable and it's empty, delete it instead of just marking descendants as not applicable\n if (!applicable && clearNonApplicableValues) {\n const { uuid: nodeCtxChildUuid, nodeDefUuid: nodeCtxChildDefUuid } = nodeCtxChild\n const nodeCtxChildDef = Surveys.getNodeDefByUuid({ survey, uuid: nodeCtxChildDefUuid })\n if (NodeDefs.isMultipleEntity(nodeCtxChildDef) && Records.isNodeEmpty(nodeCtxChild)(updateResult.record)) {\n const deleteResult = removeNodes([nodeCtxChildUuid], recordUpdateOptions)(updateResult.record)\n updateResult.merge(deleteResult)\n if (userValuesCleared) {\n updateResult.addClearedDefUuid(nodeCtxChildDefUuid)\n }\n }\n }\n}\n\nconst createMissingApplicableMultipleEntities = async ({\n params,\n updateResult,\n parentNode,\n nodeDef,\n}: {\n params: RecordNodeDependentsUpdateParams\n updateResult: RecordUpdateResult\n parentNode: ArenaRecordNode\n nodeDef: NodeDefEntity\n}): Promise<void> => {\n if (!NodeDefs.isAutoCreateMinCountItems(nodeDef)) return\n\n const minCount = Nodes.getChildrenMinCount({ parentNode, nodeDef })\n if (Number.isNaN(minCount) || minCount <= 0) return\n\n const existingChildren = Records.getChildren(parentNode, nodeDef.uuid)(updateResult.record)\n const missingCount = Math.max(0, minCount - existingChildren.length)\n\n for (let index = 0; index < missingCount; index++) {\n const createParams = { ...params, record: updateResult.record, parentNode, nodeDef }\n // create nodes sequentially: every iteration works on the record updated by the previous one\n const childUpdateResult = await addNodeAndDescendants(createParams) // NOSONAR\n updateResult.merge(childUpdateResult)\n }\n}\n\nconst updateNodePointerApplicability = async ({\n params,\n updateResult,\n nodePointer,\n recordUpdateOptions,\n}: {\n params: RecordNodeDependentsUpdateParams\n updateResult: RecordUpdateResult\n nodePointer: NodePointer\n recordUpdateOptions: RecordUpdateOptions\n}): Promise<void> => {\n const { nodeCtx: nodeCtxNodePointer, nodeDef: nodeDefNodePointer } = nodePointer\n\n const nodeCtxUuid = nodeCtxNodePointer.uuid\n const nodeDefUuid = nodeDefNodePointer.uuid\n\n // nodeCtx could have been updated in a previous iteration\n const nodeCtx = updateResult.getNodeByUuid(nodeCtxUuid) ?? nodeCtxNodePointer\n\n const applicablePrev = Nodes.isChildApplicable(nodeCtx, nodeDefUuid)\n const applicable = await calculateApplicableNext({\n params,\n updateResult,\n nodePointer,\n nodeCtx,\n })\n if (applicable === undefined) {\n return\n }\n\n if (applicable && NodeDefs.isMultipleEntity(nodeDefNodePointer)) {\n await createMissingApplicableMultipleEntities({\n params,\n updateResult,\n parentNode: nodeCtx,\n nodeDef: nodeDefNodePointer as NodeDefEntity,\n })\n }\n\n // 4. persist updated applicability if changed, and return updated nodes\n if (applicablePrev === applicable) {\n return\n }\n // Applicability changed\n\n // update node and add it to nodes updated\n const nodeCtxUpdated = Nodes.assocChildApplicability(nodeCtx, nodeDefUuid, applicable)\n updateResult.addNode(nodeCtxUpdated, recordUpdateOptions)\n\n let nodeCtxChildren = Records.getChildren(nodeCtx, nodeDefUuid)(updateResult.record)\n\n if (NodeDefs.isMultipleEntity(nodeDefNodePointer) && NodeDefs.isEnumerate(nodeDefNodePointer as NodeDefEntity)) {\n const entityDef = nodeDefNodePointer as NodeDefEntity\n if (NodeDefs.getEnumeratingItemsExpression(entityDef)) {\n await syncEnumeratingItemsEntities({\n ...params,\n parentNode: nodeCtxUpdated,\n entityDef,\n updateResult,\n })\n } else {\n await createOrDeleteEnumeratedEntities({\n ...params,\n parentNode: nodeCtxUpdated,\n entityDef,\n updateResult,\n })\n }\n nodeCtxChildren = Records.getChildren(nodeCtx, nodeDefUuid)(updateResult.record)\n }\n for (const nodeCtxChild of nodeCtxChildren) {\n // add nodeCtxChild and its descendants to nodesUpdated\n updateDescendantsApplicability({\n updateResult,\n nodeCtxChild,\n applicable,\n params,\n recordUpdateOptions,\n })\n }\n}\n\nexport const updateSelfAndDependentsApplicable = async (\n params: RecordNodeDependentsUpdateParams\n): Promise<RecordUpdateResult> => {\n const { survey, record, node, sideEffect = false } = params\n\n const updateResult = new RecordUpdateResult({ record })\n\n const recordUpdateOptions: RecordUpdateOptions = { sideEffect }\n\n // 1. fetch dependent nodes\n const nodePointersToUpdate = extractNodePointersToUpdate({ survey, record, node })\n\n // 2. update expr to node and dependent nodes\n // NOTE: don't do it in parallel, same nodeCtx metadata could be overwritten\n for (const nodePointer of nodePointersToUpdate) {\n await updateNodePointerApplicability({ params, updateResult, nodePointer, recordUpdateOptions }) // NOSONAR\n }\n return updateResult\n}\n"]}
|
|
1
|
+
{"version":3,"file":"recordNodeDependentsApplicableUpdater.js","sourceRoot":"","sources":["../../../src/record/recordNodesUpdater/recordNodeDependentsApplicableUpdater.ts"],"names":[],"mappings":"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","sourcesContent":["import { ArenaRecordNode, NodePointer, Nodes, NodesMap } from '../../node'\nimport { NodeDefEntity, NodeDefs } from '../../nodeDef'\nimport { Surveys } from '../../survey'\nimport { Survey, SurveyDependencyType } from '../../survey/survey'\nimport { ArenaRecord } from '../record'\nimport { Records, RecordUpdateOptions } from '../records'\nimport { calculateBooleanValueNext } from './recordNodeDependentsBooleanUpdater'\nimport { createOrDeleteEnumeratedEntities } from './recordNodeDependentsEnumeratedEntitiesUpdater'\nimport { syncEnumeratingItemsEntities } from './recordNodeDependentsEnumeratingItemsUpdater'\nimport { addNodeAndDescendants } from './recordNodesCreator'\nimport { removeNodes } from './recordNodesDeleter'\nimport { getDependentNodePointersByType } from './recordNodesDependentsUpdaterCommons'\nimport { RecordNodeDependentsUpdateParams } from './recordNodeDependentsUpdateParams'\nimport { RecordUpdateResult } from './recordUpdateResult'\n\nconst extractNodePointersToUpdate = (params: { survey: Survey; record: ArenaRecord; node: ArenaRecordNode }) => {\n const { survey, record, node } = params\n\n const nodePointersToUpdate = getDependentNodePointersByType({\n survey,\n record,\n node,\n dependencyType: SurveyDependencyType.applicable,\n includeSelfWhenSourceIsAttribute: true,\n includeNewEntityChildPointers: true,\n })\n return nodePointersToUpdate\n}\n\nconst calculateApplicableNext = (params: {\n params: RecordNodeDependentsUpdateParams\n updateResult: RecordUpdateResult\n nodePointer: NodePointer\n nodeCtx: ArenaRecordNode\n}): Promise<boolean | undefined> =>\n calculateBooleanValueNext({\n ...params,\n getExpressions: (nodePointer) => NodeDefs.getApplicable(nodePointer.nodeDef),\n getValuePrev: Nodes.isChildApplicable,\n })\n\nconst updateDescendantsApplicability = ({\n updateResult,\n nodeCtxChild,\n applicable,\n params,\n recordUpdateOptions,\n}: {\n updateResult: RecordUpdateResult\n nodeCtxChild: ArenaRecordNode\n applicable: boolean\n params: RecordNodeDependentsUpdateParams\n recordUpdateOptions: RecordUpdateOptions\n}): void => {\n const { survey, sideEffect = false, clearNonApplicableValues = false } = params\n\n // only values entered by the user are reported as cleared (they need a confirmation)\n let userValuesCleared = false\n\n // nodes added to the record all together: adding them one by one would copy the record nodes at every node\n // (when sideEffect is false)\n const nodesUpdated: NodesMap = {}\n\n Records.visitDescendantsAndSelf(nodeCtxChild, (nodeDescendant): boolean => {\n const nodeDescendantCleared = clearNonApplicableValues && !applicable && Nodes.isValueNotBlank(nodeDescendant)\n // values set by expressions (default values, read only attributes) are applied again when applicable: not reported\n // (evaluated before clearing the value: with sideEffect the node is modified in place)\n const nodeDescendantDef = Surveys.getNodeDefByUuid({ survey, uuid: nodeDescendant.nodeDefUuid })\n const userValueCleared =\n nodeDescendantCleared && !NodeDefs.isReadOnly(nodeDescendantDef) && Nodes.hasUserInputValue(nodeDescendant)\n // Clear value if becoming non-applicable and parameter is enabled\n const nodeDescendantUpdated = nodeDescendantCleared\n ? Nodes.assocValue(nodeDescendant, null, { sideEffect })\n : nodeDescendant\n nodesUpdated[nodeDescendantUpdated.uuid] = nodeDescendantUpdated\n if (userValueCleared) {\n updateResult.addClearedDefUuid(nodeDescendant.nodeDefUuid)\n userValuesCleared = true\n }\n return false\n })(updateResult.record)\n\n updateResult.addNodes(nodesUpdated, recordUpdateOptions)\n\n // if a multiple entity became not applicable and it's empty, delete it instead of just marking descendants as not applicable\n if (!applicable && clearNonApplicableValues) {\n const { uuid: nodeCtxChildUuid, nodeDefUuid: nodeCtxChildDefUuid } = nodeCtxChild\n const nodeCtxChildDef = Surveys.getNodeDefByUuid({ survey, uuid: nodeCtxChildDefUuid })\n if (NodeDefs.isMultipleEntity(nodeCtxChildDef) && Records.isNodeEmpty(nodeCtxChild)(updateResult.record)) {\n const deleteResult = removeNodes([nodeCtxChildUuid], recordUpdateOptions)(updateResult.record)\n updateResult.merge(deleteResult)\n if (userValuesCleared) {\n updateResult.addClearedDefUuid(nodeCtxChildDefUuid)\n }\n }\n }\n}\n\nconst createMissingApplicableMultipleEntities = async ({\n params,\n updateResult,\n parentNode,\n nodeDef,\n}: {\n params: RecordNodeDependentsUpdateParams\n updateResult: RecordUpdateResult\n parentNode: ArenaRecordNode\n nodeDef: NodeDefEntity\n}): Promise<void> => {\n if (!NodeDefs.isAutoCreateMinCountItems(nodeDef)) return\n\n const minCount = Nodes.getChildrenMinCount({ parentNode, nodeDef })\n if (Number.isNaN(minCount) || minCount <= 0) return\n\n const existingChildren = Records.getChildren(parentNode, nodeDef.uuid)(updateResult.record)\n const missingCount = Math.max(0, minCount - existingChildren.length)\n\n for (let index = 0; index < missingCount; index++) {\n const createParams = { ...params, record: updateResult.record, parentNode, nodeDef }\n // create nodes sequentially: every iteration works on the record updated by the previous one\n const childUpdateResult = await addNodeAndDescendants(createParams) // NOSONAR\n updateResult.merge(childUpdateResult)\n }\n}\n\nconst updateNodePointerApplicability = async ({\n params,\n updateResult,\n nodePointer,\n recordUpdateOptions,\n}: {\n params: RecordNodeDependentsUpdateParams\n updateResult: RecordUpdateResult\n nodePointer: NodePointer\n recordUpdateOptions: RecordUpdateOptions\n}): Promise<void> => {\n const { nodeCtx: nodeCtxNodePointer, nodeDef: nodeDefNodePointer } = nodePointer\n\n const nodeCtxUuid = nodeCtxNodePointer.uuid\n const nodeDefUuid = nodeDefNodePointer.uuid\n\n // nodeCtx could have been updated in a previous iteration\n const nodeCtx = updateResult.getNodeByUuid(nodeCtxUuid) ?? nodeCtxNodePointer\n\n const applicablePrev = Nodes.isChildApplicable(nodeCtx, nodeDefUuid)\n const applicable = await calculateApplicableNext({\n params,\n updateResult,\n nodePointer,\n nodeCtx,\n })\n if (applicable === undefined) {\n return\n }\n\n if (applicable && NodeDefs.isMultipleEntity(nodeDefNodePointer)) {\n await createMissingApplicableMultipleEntities({\n params,\n updateResult,\n parentNode: nodeCtx,\n nodeDef: nodeDefNodePointer as NodeDefEntity,\n })\n }\n\n // 4. persist updated applicability if changed, and return updated nodes\n if (applicablePrev === applicable) {\n return\n }\n // Applicability changed\n\n // update node and add it to nodes updated\n const nodeCtxUpdated = Nodes.assocChildApplicability(nodeCtx, nodeDefUuid, applicable)\n updateResult.addNode(nodeCtxUpdated, recordUpdateOptions)\n\n let nodeCtxChildren = Records.getChildren(nodeCtx, nodeDefUuid)(updateResult.record)\n\n if (NodeDefs.isMultipleEntity(nodeDefNodePointer) && NodeDefs.isEnumerate(nodeDefNodePointer as NodeDefEntity)) {\n const entityDef = nodeDefNodePointer as NodeDefEntity\n if (NodeDefs.getEnumeratingItemsExpression(entityDef)) {\n await syncEnumeratingItemsEntities({\n ...params,\n parentNode: nodeCtxUpdated,\n entityDef,\n updateResult,\n })\n } else {\n await createOrDeleteEnumeratedEntities({\n ...params,\n parentNode: nodeCtxUpdated,\n entityDef,\n updateResult,\n })\n }\n nodeCtxChildren = Records.getChildren(nodeCtx, nodeDefUuid)(updateResult.record)\n }\n for (const nodeCtxChild of nodeCtxChildren) {\n // add nodeCtxChild and its descendants to nodesUpdated\n updateDescendantsApplicability({\n updateResult,\n nodeCtxChild,\n applicable,\n params,\n recordUpdateOptions,\n })\n }\n}\n\nexport const updateSelfAndDependentsApplicable = async (\n params: RecordNodeDependentsUpdateParams\n): Promise<RecordUpdateResult> => {\n const { survey, record, node, sideEffect = false } = params\n\n const updateResult = new RecordUpdateResult({ record })\n\n const recordUpdateOptions: RecordUpdateOptions = { sideEffect }\n\n // 1. fetch dependent nodes\n const nodePointersToUpdate = extractNodePointersToUpdate({ survey, record, node })\n\n // 2. update expr to node and dependent nodes\n // NOTE: don't do it in parallel, same nodeCtx metadata could be overwritten\n for (const nodePointer of nodePointersToUpdate) {\n await updateNodePointerApplicability({ params, updateResult, nodePointer, recordUpdateOptions }) // NOSONAR\n }\n return updateResult\n}\n"]}
|
|
@@ -17,6 +17,7 @@ export declare class RecordUpdateResult {
|
|
|
17
17
|
});
|
|
18
18
|
getNodeByUuid(uuid: string): ArenaRecordNode;
|
|
19
19
|
addNode(node: ArenaRecordNode, options?: RecordUpdateOptions): void;
|
|
20
|
+
addNodes(nodes: NodesMap, options?: RecordUpdateOptions): void;
|
|
20
21
|
addClearedDefUuid(nodeDefUuid: string): void;
|
|
21
22
|
/**
|
|
22
23
|
* Merges this record update result with the specified one.
|
|
@@ -18,6 +18,10 @@ class RecordUpdateResult {
|
|
|
18
18
|
this.nodes[node.uuid] = node;
|
|
19
19
|
this.record = (0, recordUpdater_1.addNode)(node, options)(this.record);
|
|
20
20
|
}
|
|
21
|
+
addNodes(nodes, options) {
|
|
22
|
+
Object.assign(this.nodes, nodes);
|
|
23
|
+
this.record = (0, recordUpdater_1.addNodes)(nodes, options)(this.record);
|
|
24
|
+
}
|
|
21
25
|
addClearedDefUuid(nodeDefUuid) {
|
|
22
26
|
this.clearedDefUuids.add(nodeDefUuid);
|
|
23
27
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"recordUpdateResult.js","sourceRoot":"","sources":["../../../src/record/recordNodesUpdater/recordUpdateResult.ts"],"names":[],"mappings":";;;AAGA,
|
|
1
|
+
{"version":3,"file":"recordUpdateResult.js","sourceRoot":"","sources":["../../../src/record/recordNodesUpdater/recordUpdateResult.ts"],"names":[],"mappings":";;;AAGA,6DAA6D;AAG7D;IAOE,YAAY,MAMX;;QACC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAA;QAC3B,IAAI,CAAC,KAAK,SAAG,MAAM,CAAC,KAAK,mCAAI,EAAE,CAAA;QAC/B,IAAI,CAAC,YAAY,SAAG,MAAM,CAAC,YAAY,mCAAI,EAAE,CAAA;QAC7C,IAAI,CAAC,eAAe,SAAG,MAAM,CAAC,eAAe,mCAAI,IAAI,GAAG,EAAU,CAAA;QAClE,IAAI,CAAC,UAAU,GAAG,MAAM,CAAC,UAAU,CAAA;IACrC,CAAC;IAED,aAAa,CAAC,IAAY;QACxB,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;IACzB,CAAC;IAED,OAAO,CAAC,IAAqB,EAAE,OAA6B;QAC1D,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAA;QAC5B,IAAI,CAAC,MAAM,GAAG,IAAA,uBAAO,EAAC,IAAI,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;IACnD,CAAC;IAED,QAAQ,CAAC,KAAe,EAAE,OAA6B;QACrD,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,KAAK,CAAC,CAAA;QAChC,IAAI,CAAC,MAAM,GAAG,IAAA,wBAAQ,EAAC,KAAK,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;IACrD,CAAC;IAED,iBAAiB,CAAC,WAAmB;QACnC,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,WAAW,CAAC,CAAA;IACvC,CAAC;IAED;;;;;;;OAOG;IACH,KAAK,CAAC,kBAAsC;QAC1C,IAAI,CAAC,MAAM,GAAG,kBAAkB,CAAC,MAAM,CAAA;QACvC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,kBAAkB,CAAC,KAAK,CAAC,CAAA;QACnD,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,YAAY,EAAE,kBAAkB,CAAC,YAAY,CAAC,CAAA;QACjE,KAAK,MAAM,WAAW,IAAI,kBAAkB,CAAC,eAAe,EAAE,CAAC;YAC7D,IAAI,CAAC,iBAAiB,CAAC,WAAW,CAAC,CAAA;QACrC,CAAC;QACD,OAAO,IAAI,CAAA;IACb,CAAC;CACF","sourcesContent":["import { ArenaRecordNode, NodesMap } from '../../node'\nimport { Validation } from '../../validation'\nimport { ArenaRecord } from '../record'\nimport { addNode, addNodes } from '../_records/recordUpdater'\nimport { RecordUpdateOptions } from '../records'\n\nexport class RecordUpdateResult {\n record: ArenaRecord\n nodes: NodesMap\n nodesDeleted: NodesMap\n clearedDefUuids: Set<string>\n validation?: Validation\n\n constructor(params: {\n record: ArenaRecord\n nodes?: NodesMap\n nodesDeleted?: NodesMap\n clearedDefUuids?: Set<string>\n validation?: Validation\n }) {\n this.record = params.record\n this.nodes = params.nodes ?? {}\n this.nodesDeleted = params.nodesDeleted ?? {}\n this.clearedDefUuids = params.clearedDefUuids ?? new Set<string>()\n this.validation = params.validation\n }\n\n getNodeByUuid(uuid: string) {\n return this.nodes[uuid]\n }\n\n addNode(node: ArenaRecordNode, options?: RecordUpdateOptions) {\n this.nodes[node.uuid] = node\n this.record = addNode(node, options)(this.record)\n }\n\n addNodes(nodes: NodesMap, options?: RecordUpdateOptions) {\n Object.assign(this.nodes, nodes)\n this.record = addNodes(nodes, options)(this.record)\n }\n\n addClearedDefUuid(nodeDefUuid: string) {\n this.clearedDefUuids.add(nodeDefUuid)\n }\n\n /**\n * Merges this record update result with the specified one.\n * The record of this record update result will be the one of the specified record update result\n * and the nodes of the specified one will be added to the nodes of this one.\n *\n * @param {!RecordUpdateResult} recordUpdateResult - The record update result to merge with.\n * @returns {RecordUpdateResult} - The updated object.\n */\n merge(recordUpdateResult: RecordUpdateResult): this {\n this.record = recordUpdateResult.record\n Object.assign(this.nodes, recordUpdateResult.nodes)\n Object.assign(this.nodesDeleted, recordUpdateResult.nodesDeleted)\n for (const nodeDefUuid of recordUpdateResult.clearedDefUuids) {\n this.addClearedDefUuid(nodeDefUuid)\n }\n return this\n }\n}\n"]}
|
|
@@ -25,25 +25,24 @@ const attributeKeyValidator_1 = require("./attributeKeyValidator");
|
|
|
25
25
|
const attributeTypeValidator_1 = require("./attributeTypeValidator");
|
|
26
26
|
const attributeUniqueValidator_1 = require("./attributeUniqueValidator");
|
|
27
27
|
const expressionEvaluator = new recordExpressionEvaluator_1.RecordExpressionEvaluator();
|
|
28
|
-
const
|
|
28
|
+
const _getSiblingNodeKeysGroup = (params) => {
|
|
29
29
|
const { survey, record, attribute, siblingKeyNodesCache } = params;
|
|
30
30
|
const attributeDef = survey_1.Surveys.getNodeDefByUuid({ survey, uuid: attribute.nodeDefUuid });
|
|
31
31
|
const ancestorMultipleEntityDef = survey_1.Surveys.getNodeDefAncestorMultipleEntity({ survey, nodeDef: attributeDef });
|
|
32
|
-
if (!ancestorMultipleEntityDef)
|
|
33
|
-
return
|
|
34
|
-
}
|
|
32
|
+
if (!ancestorMultipleEntityDef)
|
|
33
|
+
return undefined;
|
|
35
34
|
const ancestorMultipleEntity = records_1.Records.getAncestor(attribute, ancestorMultipleEntityDef.uuid)(record);
|
|
36
35
|
if (!ancestorMultipleEntity)
|
|
37
|
-
return
|
|
36
|
+
return undefined;
|
|
38
37
|
const ancestorMultipleEntityParent = records_1.Records.getParent(ancestorMultipleEntity)(record);
|
|
39
38
|
if (!ancestorMultipleEntityParent)
|
|
40
|
-
return
|
|
39
|
+
return undefined;
|
|
41
40
|
const cacheKey = `${ancestorMultipleEntityParent.uuid}_${ancestorMultipleEntityDef.uuid}`;
|
|
42
|
-
let
|
|
43
|
-
if (!
|
|
41
|
+
let group = siblingKeyNodesCache.get(cacheKey);
|
|
42
|
+
if (!group) {
|
|
44
43
|
const keyDefs = survey_1.Surveys.getNodeDefKeys({ survey, nodeDef: ancestorMultipleEntityDef });
|
|
45
44
|
const entities = records_1.Records.getEntitySiblings(ancestorMultipleEntity, true)(record);
|
|
46
|
-
items = [];
|
|
45
|
+
const items = [];
|
|
47
46
|
for (const entity of entities) {
|
|
48
47
|
const keyNodes = records_1.Records.getEntityKeyNodes(survey, entity, undefined, keyDefs)(record);
|
|
49
48
|
for (const keyNode of keyNodes) {
|
|
@@ -53,9 +52,10 @@ const _getSiblingNodeKeys = (params) => {
|
|
|
53
52
|
items.push({ entityUuid: entity.uuid, keyNode, hasErrors: !!nodeValidation && !nodeValidation.valid });
|
|
54
53
|
}
|
|
55
54
|
}
|
|
56
|
-
|
|
55
|
+
group = { items };
|
|
56
|
+
siblingKeyNodesCache.set(cacheKey, group);
|
|
57
57
|
}
|
|
58
|
-
return
|
|
58
|
+
return { entityUuid: ancestorMultipleEntity.uuid, group };
|
|
59
59
|
};
|
|
60
60
|
const _getValidationMessagesWithDefault = (params) => {
|
|
61
61
|
const { survey, expression, defaultMessage } = params;
|
|
@@ -150,7 +150,8 @@ const validateAttribute = (params) => __awaiter(void 0, void 0, void 0, function
|
|
|
150
150
|
return validation_1.ValidationFactory.createInstance();
|
|
151
151
|
});
|
|
152
152
|
const findSiblingKeyNodesToValidate = (params) => {
|
|
153
|
-
|
|
153
|
+
var _a;
|
|
154
|
+
const { survey, nodeDef, nodeParent: parentNode, attribute, nodeUuidsToValidate } = params;
|
|
154
155
|
if (!nodeDef_1.NodeDefs.isKey(nodeDef) ||
|
|
155
156
|
!parentNode ||
|
|
156
157
|
(nodeDef_1.NodeDefs.isAutoIncrementalKey(nodeDef) && attribute.created && node_1.Nodes.isDefaultValueApplied(attribute))) {
|
|
@@ -160,24 +161,71 @@ const findSiblingKeyNodesToValidate = (params) => {
|
|
|
160
161
|
}
|
|
161
162
|
// when a key attribute is updated, a sibling key attribute with the same value
|
|
162
163
|
// or a sibling attribute with a validation error (duplicate key) should be re-validated
|
|
163
|
-
const
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
164
|
+
const siblingNodeKeysGroup = _getSiblingNodeKeysGroup(params);
|
|
165
|
+
if (!siblingNodeKeysGroup)
|
|
166
|
+
return [];
|
|
167
|
+
const { entityUuid, group } = siblingNodeKeysGroup;
|
|
168
|
+
// key nodes in the nodes to validate are validated anyway: no need to compare their value
|
|
169
|
+
(_a = group.itemsToCompare) !== null && _a !== void 0 ? _a : (group.itemsToCompare = group.items.filter((item) => !nodeUuidsToValidate.has(item.keyNode.uuid)));
|
|
170
|
+
const result = [];
|
|
171
|
+
for (const { entityUuid: itemEntityUuid, keyNode } of group.itemsToCompare) {
|
|
172
|
+
if (itemEntityUuid !== entityUuid &&
|
|
173
|
+
node_1.NodeValues.isValueEqual({ survey, nodeDef, parentNode, value: keyNode.value, valueSearch: attribute.value })) {
|
|
174
|
+
result.push(keyNode);
|
|
172
175
|
}
|
|
173
|
-
}
|
|
174
|
-
return
|
|
176
|
+
}
|
|
177
|
+
return result.concat(_findSiblingKeyNodesWithErrorsNotAdded({ group, entityUuid }));
|
|
178
|
+
};
|
|
179
|
+
// key nodes with errors (in entities other than the attribute's one) are added only once per group
|
|
180
|
+
const _findSiblingKeyNodesWithErrorsNotAdded = (params) => {
|
|
181
|
+
const { group, entityUuid } = params;
|
|
182
|
+
if (group.firstEntityUuid === undefined) {
|
|
183
|
+
group.firstEntityUuid = entityUuid;
|
|
184
|
+
return group.items.filter((item) => item.hasErrors && item.entityUuid !== entityUuid).map((item) => item.keyNode);
|
|
185
|
+
}
|
|
186
|
+
if (group.allItemsWithErrorsAdded || group.firstEntityUuid === entityUuid)
|
|
187
|
+
return [];
|
|
188
|
+
group.allItemsWithErrorsAdded = true;
|
|
189
|
+
const { firstEntityUuid } = group;
|
|
190
|
+
return group.items.filter((item) => item.hasErrors && item.entityUuid === firstEntityUuid).map((item) => item.keyNode);
|
|
191
|
+
};
|
|
192
|
+
const findUniqueAttributeSiblingsToValidate = (params) => {
|
|
193
|
+
const { record, node, nodeDef, nodeParent, attributeSiblingsAddedCache } = params;
|
|
194
|
+
if (!nodeParent)
|
|
195
|
+
return [];
|
|
196
|
+
const nodeParentParent = records_1.Records.getParent(nodeParent)(record);
|
|
197
|
+
if (!nodeParentParent)
|
|
198
|
+
return [];
|
|
199
|
+
// the siblings of the attribute are the attributes in the other parent entities:
|
|
200
|
+
// after visiting attributes in 2 different parent entities, all the attributes of the group have been added
|
|
201
|
+
const cacheKey = `${nodeParentParent.uuid}_${nodeParent.nodeDefUuid}_${nodeDef.uuid}`;
|
|
202
|
+
const cached = attributeSiblingsAddedCache.get(cacheKey);
|
|
203
|
+
if (!cached) {
|
|
204
|
+
attributeSiblingsAddedCache.set(cacheKey, { firstParentEntity: nodeParent });
|
|
205
|
+
return records_1.Records.getAttributeSiblings(node, nodeDef)(record);
|
|
206
|
+
}
|
|
207
|
+
if (cached.allSiblingsAdded || cached.firstParentEntity.uuid === nodeParent.uuid)
|
|
208
|
+
return [];
|
|
209
|
+
cached.allSiblingsAdded = true;
|
|
210
|
+
return records_1.Records.getChildren(cached.firstParentEntity, nodeDef.uuid)(record);
|
|
175
211
|
};
|
|
176
212
|
const findSortedNodesToValidate = (params) => {
|
|
177
213
|
var _a;
|
|
178
214
|
const { survey, record, nodesArray } = params;
|
|
179
215
|
const result = [];
|
|
216
|
+
const resultUuids = new Set();
|
|
217
|
+
const addToResult = (nodes) => {
|
|
218
|
+
for (const node of nodes) {
|
|
219
|
+
if (!resultUuids.has(node.uuid)) {
|
|
220
|
+
resultUuids.add(node.uuid);
|
|
221
|
+
result.push(node);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
// every attribute in nodesArray is validated (it's included in its own dependents)
|
|
226
|
+
const nodeUuidsToValidate = new Set(nodesArray.map((node) => node.uuid));
|
|
180
227
|
const siblingKeyNodesCache = new Map();
|
|
228
|
+
const attributeSiblingsAddedCache = new Map();
|
|
181
229
|
for (const node of nodesArray) {
|
|
182
230
|
const nodeDef = survey_1.Surveys.getNodeDefByUuid({ survey, uuid: node.nodeDefUuid });
|
|
183
231
|
if (!nodeDef_1.NodeDefs.isAttribute(nodeDef)) {
|
|
@@ -189,11 +237,12 @@ const findSortedNodesToValidate = (params) => {
|
|
|
189
237
|
...records_1.Records.getDependentNodePointers(survey, node, survey_2.SurveyDependencyType.required)(record),
|
|
190
238
|
];
|
|
191
239
|
const nodeParent = records_1.Records.getParent(node)(record);
|
|
192
|
-
|
|
240
|
+
addToResult(nodePointers_1.NodePointers.getNodesFromNodePointers({ record, nodePointers: nodePointersAttributeAndDependents }));
|
|
241
|
+
addToResult(findSiblingKeyNodesToValidate(Object.assign(Object.assign({}, params), { nodeDef,
|
|
242
|
+
nodeParent, attribute: node, siblingKeyNodesCache,
|
|
243
|
+
nodeUuidsToValidate })));
|
|
193
244
|
if ((_a = nodeDef_1.NodeDefs.getValidations(nodeDef)) === null || _a === void 0 ? void 0 : _a.unique) {
|
|
194
|
-
|
|
195
|
-
result.push(sibling);
|
|
196
|
-
}
|
|
245
|
+
addToResult(findUniqueAttributeSiblingsToValidate({ record, node, nodeDef, nodeParent, attributeSiblingsAddedCache }));
|
|
197
246
|
}
|
|
198
247
|
}
|
|
199
248
|
return result;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"attributeValidator.js","sourceRoot":"","sources":["../../../src/record/recordValidator/attributeValidator.ts"],"names":[],"mappings":"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","sourcesContent":["import { User } from '../../auth'\nimport { Dictionary } from '../../common'\nimport { Labels } from '../../language'\nimport { ArenaRecordNode, NodeKeys, Nodes, NodeValues } from '../../node'\nimport { NodeDef, NodeDefExpression, NodeDefProps, NodeDefs, NodeDefType } from '../../nodeDef'\nimport { AreaOfInterestProvider } from '../../nodeDefExpressionEvaluator/areaOfInterestProvider'\nimport { CategoryItemProvider } from '../../nodeDefExpressionEvaluator/categoryItemProvider'\nimport { TaxonProvider } from '../../nodeDefExpressionEvaluator/taxonProvider'\nimport { Survey, Surveys } from '../../survey'\nimport { SurveyDependencyType } from '../../survey/survey'\nimport { Objects } from '../../utils'\nimport {\n ValidationFactory,\n ValidationResult,\n ValidationResultFactory,\n ValidationSeverity,\n Validator,\n} from '../../validation'\nimport { ValidationFields } from '../../validation/validation'\nimport { NodePointers } from '../nodePointers'\nimport { ArenaRecord } from '../record'\nimport { RecordExpressionEvaluator } from '../recordExpressionEvaluator'\nimport { evaluateRequiredIf } from '../recordNodesUpdater/recordNodeDependentsRequiredUpdater'\nimport { Records } from '../records'\nimport { RecordValidations } from '../recordValidations'\nimport { AttributeKeyValidator, createEntityKeysCache, EntityKeysCache } from './attributeKeyValidator'\nimport { AttributeTypeValidator } from './attributeTypeValidator'\nimport { AttributeUniqueValidator } from './attributeUniqueValidator'\n\nconst expressionEvaluator = new RecordExpressionEvaluator()\n\nexport interface RecordValidatorParams {\n user: User\n survey: Survey\n categoryItemProvider?: CategoryItemProvider\n taxonProvider?: TaxonProvider\n areaOfInterestProvider?: AreaOfInterestProvider\n record: ArenaRecord\n}\n\nexport interface AttributeValidatorParams extends RecordValidatorParams {\n attribute: ArenaRecordNode\n}\n\nexport interface AttributesValidatorParams extends RecordValidatorParams {\n nodes: Dictionary<ArenaRecordNode>\n}\n\nexport interface SortedAttributesValidatorParams extends RecordValidatorParams {\n nodesArray: ArenaRecordNode[]\n}\n\ntype SiblingKeyNodeItem = { entityUuid: string; keyNode: ArenaRecordNode; hasErrors: boolean }\n\n// sibling key nodes are cached by ancestor multiple entity parent and def uuid during the same validation call:\n// when many entities are added to the same parent entity (e.g. data import), computing them for every updated\n// key attribute would take a time that grows quadratically with the number of entities\ntype SiblingKeyNodesCache = Map<string, SiblingKeyNodeItem[]>\n\nconst _getSiblingNodeKeys = (\n params: AttributeValidatorParams & { siblingKeyNodesCache?: SiblingKeyNodesCache }\n): SiblingKeyNodeItem[] => {\n const { survey, record, attribute, siblingKeyNodesCache } = params\n\n const attributeDef = Surveys.getNodeDefByUuid({ survey, uuid: attribute.nodeDefUuid })\n const ancestorMultipleEntityDef = Surveys.getNodeDefAncestorMultipleEntity({ survey, nodeDef: attributeDef })\n if (!ancestorMultipleEntityDef) {\n return []\n }\n const ancestorMultipleEntity = Records.getAncestor(attribute, ancestorMultipleEntityDef.uuid)(record)\n if (!ancestorMultipleEntity) return []\n\n const ancestorMultipleEntityParent = Records.getParent(ancestorMultipleEntity)(record)\n if (!ancestorMultipleEntityParent) return []\n\n const cacheKey = `${ancestorMultipleEntityParent.uuid}_${ancestorMultipleEntityDef.uuid}`\n let items = siblingKeyNodesCache?.get(cacheKey)\n if (!items) {\n const keyDefs = Surveys.getNodeDefKeys({ survey, nodeDef: ancestorMultipleEntityDef })\n const entities = Records.getEntitySiblings(ancestorMultipleEntity, true)(record)\n items = []\n for (const entity of entities) {\n const keyNodes = Records.getEntityKeyNodes(survey, entity, undefined, keyDefs)(record)\n for (const keyNode of keyNodes) {\n if (!keyNode) continue\n const nodeValidation =\n record.validation && RecordValidations.getValidationNode({ nodeUuid: keyNode.uuid })(record.validation)\n items.push({ entityUuid: entity.uuid, keyNode, hasErrors: !!nodeValidation && !nodeValidation.valid })\n }\n }\n siblingKeyNodesCache?.set(cacheKey, items)\n }\n return items.filter((item) => item.entityUuid !== ancestorMultipleEntity.uuid)\n}\n\nconst _getValidationMessagesWithDefault = (params: {\n survey: Survey\n expression: NodeDefExpression\n defaultMessage?: string\n}): Labels => {\n const { survey, expression, defaultMessage } = params\n const messages: Labels = { ...expression.messages }\n\n const languages = Surveys.getLanguages(survey)\n const defaultMessageDefined = Objects.isNotEmpty(defaultMessage)\n\n for (const lang of languages) {\n const customMessage = messages[lang]\n if (Objects.isEmpty(customMessage) && defaultMessageDefined) {\n messages[lang] = defaultMessage\n }\n }\n\n return messages\n}\n\nconst _isValueRequired = async (\n params: AttributeValidatorParams & { nodeDef: NodeDef<NodeDefType, NodeDefProps> }\n): Promise<boolean> => {\n const { record, attribute, nodeDef } = params\n if (NodeDefs.isKey(nodeDef) || NodeDefs.isRequired(nodeDef)) return true\n if (!NodeDefs.isRequiredIfDefined(nodeDef)) return false\n const parentNode = Records.getParent(attribute)(record)\n if (!parentNode) return false\n // evaluated here (not read from the parent node meta flag) so validation doesn't depend on up to date node meta\n // (e.g. records created before the \"required if\" condition was defined)\n return evaluateRequiredIf({ ...params, nodeCtx: parentNode, nodeDef })\n}\n\nconst _validateRequired =\n (params: AttributeValidatorParams & { nodeDef: NodeDef<NodeDefType, NodeDefProps> }) =>\n async (_field: string, node: any): Promise<ValidationResult> => {\n const valid = !Nodes.isValueBlank(node) || !(await _isValueRequired(params))\n return valid\n ? ValidationResultFactory.createInstance()\n : ValidationResultFactory.createInstance({\n key: 'record.attribute.valueRequired',\n severity: ValidationSeverity.error,\n valid,\n })\n }\n\n/**\n * Evaluates the validation expressions.\n */\nconst _validateNodeValidations =\n (params: {\n user: User\n survey: Survey\n record: ArenaRecord\n nodeDef: NodeDef<NodeDefType, NodeDefProps>\n areaOfInterestProvider?: AreaOfInterestProvider\n }) =>\n async (_propName: string, node: ArenaRecordNode): Promise<ValidationResult> => {\n const { user, survey, record, nodeDef, areaOfInterestProvider } = params\n if (Nodes.isValueBlank(node)) return ValidationResultFactory.createInstance()\n\n const validations = NodeDefs.getValidations(nodeDef)\n if (!validations?.expressions?.length) return ValidationResultFactory.createInstance()\n\n const applicableExpressionsEval = await expressionEvaluator.evalApplicableExpressions({\n user,\n survey,\n record,\n areaOfInterestProvider,\n nodeCtx: node,\n expressions: validations.expressions,\n stopAtFirstFound: false,\n })\n\n let validationResult = ValidationResultFactory.createInstance()\n\n for (const { expression, value: valid } of applicableExpressionsEval) {\n if (!valid) {\n const messages = _getValidationMessagesWithDefault({\n survey,\n expression,\n defaultMessage: expression.expression,\n })\n\n validationResult = ValidationResultFactory.createInstance({\n valid: false,\n key: 'record.attribute.customValidation',\n severity: expression.severity,\n messages,\n })\n break\n }\n }\n\n return validationResult\n }\n\nconst validateAttribute = async (params: AttributeValidatorParams & { entityKeysCache?: EntityKeysCache }) => {\n const { survey, record, attribute } = params\n if (Records.isNodeApplicable(attribute)(record)) {\n const nodeDef = Surveys.getNodeDefByUuid({ survey, uuid: attribute.nodeDefUuid })\n const validatorParams = { ...params, nodeDef }\n return new Validator().validate(attribute, {\n [NodeKeys.value]: [\n _validateRequired(validatorParams),\n AttributeTypeValidator.validateValueType(validatorParams),\n _validateNodeValidations(validatorParams),\n AttributeKeyValidator.validateAttributeKey(validatorParams),\n AttributeUniqueValidator.validateAttributeUnique(validatorParams),\n ],\n })\n }\n return ValidationFactory.createInstance()\n}\n\nconst findSiblingKeyNodesToValidate = (\n params: AttributeValidatorParams & {\n nodeDef: NodeDef<NodeDefType, NodeDefProps>\n nodeParent?: ArenaRecordNode\n siblingKeyNodesCache?: SiblingKeyNodesCache\n }\n) => {\n const { survey, nodeDef, nodeParent: parentNode, attribute } = params\n if (\n !NodeDefs.isKey(nodeDef) ||\n !parentNode ||\n (NodeDefs.isAutoIncrementalKey(nodeDef) && attribute.created && Nodes.isDefaultValueApplied(attribute))\n ) {\n // when a new node is created and the key is autoincremental, its value will be unique\n // and the validation of sibling key attributes is not necessary\n return []\n }\n // when a key attribute is updated, a sibling key attribute with the same value\n // or a sibling attribute with a validation error (duplicate key) should be re-validated\n const siblingNodeKeys = _getSiblingNodeKeys(params)\n const siblingNodeKeysWithSameValue: ArenaRecordNode[] = []\n const siblingNodeKeysWithErrors: ArenaRecordNode[] = []\n siblingNodeKeys.forEach(({ keyNode, hasErrors }) => {\n if (NodeValues.isValueEqual({ survey, nodeDef, parentNode, value: keyNode.value, valueSearch: attribute.value })) {\n siblingNodeKeysWithSameValue.push(keyNode)\n }\n if (hasErrors) {\n siblingNodeKeysWithErrors.push(keyNode)\n }\n })\n return [...siblingNodeKeysWithSameValue, ...siblingNodeKeysWithErrors]\n}\n\nconst findSortedNodesToValidate = (params: SortedAttributesValidatorParams): ArenaRecordNode[] => {\n const { survey, record, nodesArray } = params\n\n const result = []\n const siblingKeyNodesCache: SiblingKeyNodesCache = new Map()\n for (const node of nodesArray) {\n const nodeDef = Surveys.getNodeDefByUuid({ survey, uuid: node.nodeDefUuid })\n if (!NodeDefs.isAttribute(nodeDef)) {\n continue\n }\n // Get dependents and attribute itself\n const nodePointersAttributeAndDependents = [\n ...Records.getDependentNodePointers(survey, node, SurveyDependencyType.validations, true)(record),\n ...Records.getDependentNodePointers(survey, node, SurveyDependencyType.required)(record),\n ]\n\n const nodeParent = Records.getParent(node)(record)\n\n result.push(\n ...NodePointers.getNodesFromNodePointers({ record, nodePointers: nodePointersAttributeAndDependents }),\n ...findSiblingKeyNodesToValidate({ ...params, nodeDef, nodeParent, attribute: node, siblingKeyNodesCache })\n )\n\n if (NodeDefs.getValidations(nodeDef)?.unique) {\n for (const sibling of Records.getAttributeSiblings(node, nodeDef)(record)) {\n result.push(sibling)\n }\n }\n }\n\n return result\n}\n\nconst validateSelfAndDependentSortedAttributes = async (\n params: SortedAttributesValidatorParams\n): Promise<ValidationFields> => {\n const nodesToValidate: ArenaRecordNode[] = findSortedNodesToValidate(params)\n const validationsByNodeUuid: ValidationFields = {}\n // the record doesn't change during the validation: entity keys can be cached\n const entityKeysCache = createEntityKeysCache()\n\n for (const nodeToValidate of nodesToValidate) {\n const nodeUuid = nodeToValidate.uuid\n // Validate only attributes not deleted and not validated already\n if (!nodeToValidate.deleted && !validationsByNodeUuid[nodeUuid]) {\n // sequential: avoid concurrent item/taxon lookups on large node sets\n const attributeValidatorParams = { ...params, attribute: nodeToValidate, entityKeysCache }\n validationsByNodeUuid[nodeUuid] = await validateAttribute(attributeValidatorParams) // NOSONAR\n }\n }\n return validationsByNodeUuid\n}\n\nexport const AttributeValidator = {\n validateSelfAndDependentSortedAttributes,\n}\n"]}
|
|
1
|
+
{"version":3,"file":"attributeValidator.js","sourceRoot":"","sources":["../../../src/record/recordValidator/attributeValidator.ts"],"names":[],"mappings":"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","sourcesContent":["import { User } from '../../auth'\nimport { Dictionary } from '../../common'\nimport { Labels } from '../../language'\nimport { ArenaRecordNode, NodeKeys, Nodes, NodeValues } from '../../node'\nimport { NodeDef, NodeDefExpression, NodeDefProps, NodeDefs, NodeDefType } from '../../nodeDef'\nimport { AreaOfInterestProvider } from '../../nodeDefExpressionEvaluator/areaOfInterestProvider'\nimport { CategoryItemProvider } from '../../nodeDefExpressionEvaluator/categoryItemProvider'\nimport { TaxonProvider } from '../../nodeDefExpressionEvaluator/taxonProvider'\nimport { Survey, Surveys } from '../../survey'\nimport { SurveyDependencyType } from '../../survey/survey'\nimport { Objects } from '../../utils'\nimport {\n ValidationFactory,\n ValidationResult,\n ValidationResultFactory,\n ValidationSeverity,\n Validator,\n} from '../../validation'\nimport { ValidationFields } from '../../validation/validation'\nimport { NodePointers } from '../nodePointers'\nimport { ArenaRecord } from '../record'\nimport { RecordExpressionEvaluator } from '../recordExpressionEvaluator'\nimport { evaluateRequiredIf } from '../recordNodesUpdater/recordNodeDependentsRequiredUpdater'\nimport { Records } from '../records'\nimport { RecordValidations } from '../recordValidations'\nimport { AttributeKeyValidator, createEntityKeysCache, EntityKeysCache } from './attributeKeyValidator'\nimport { AttributeTypeValidator } from './attributeTypeValidator'\nimport { AttributeUniqueValidator } from './attributeUniqueValidator'\n\nconst expressionEvaluator = new RecordExpressionEvaluator()\n\nexport interface RecordValidatorParams {\n user: User\n survey: Survey\n categoryItemProvider?: CategoryItemProvider\n taxonProvider?: TaxonProvider\n areaOfInterestProvider?: AreaOfInterestProvider\n record: ArenaRecord\n}\n\nexport interface AttributeValidatorParams extends RecordValidatorParams {\n attribute: ArenaRecordNode\n}\n\nexport interface AttributesValidatorParams extends RecordValidatorParams {\n nodes: Dictionary<ArenaRecordNode>\n}\n\nexport interface SortedAttributesValidatorParams extends RecordValidatorParams {\n nodesArray: ArenaRecordNode[]\n}\n\ntype SiblingKeyNodeItem = { entityUuid: string; keyNode: ArenaRecordNode; hasErrors: boolean }\n\ntype SiblingKeyNodesGroup = {\n items: SiblingKeyNodeItem[]\n // items whose key node is not validated anyway (not in the nodes to validate): only these are compared by value\n itemsToCompare?: SiblingKeyNodeItem[]\n // entity of the first key attribute validated in this group: its key nodes with errors are not added\n // until a key attribute of another entity is validated\n firstEntityUuid?: string\n allItemsWithErrorsAdded?: boolean\n}\n\n// sibling key nodes are cached by ancestor multiple entity parent and def uuid during the same validation call:\n// when many entities are added to the same parent entity (e.g. data import), computing them for every updated\n// key attribute would take a time that grows quadratically with the number of entities\ntype SiblingKeyNodesCache = Map<string, SiblingKeyNodesGroup>\n\nconst _getSiblingNodeKeysGroup = (\n params: AttributeValidatorParams & { siblingKeyNodesCache: SiblingKeyNodesCache }\n): { entityUuid: string; group: SiblingKeyNodesGroup } | undefined => {\n const { survey, record, attribute, siblingKeyNodesCache } = params\n\n const attributeDef = Surveys.getNodeDefByUuid({ survey, uuid: attribute.nodeDefUuid })\n const ancestorMultipleEntityDef = Surveys.getNodeDefAncestorMultipleEntity({ survey, nodeDef: attributeDef })\n if (!ancestorMultipleEntityDef) return undefined\n\n const ancestorMultipleEntity = Records.getAncestor(attribute, ancestorMultipleEntityDef.uuid)(record)\n if (!ancestorMultipleEntity) return undefined\n\n const ancestorMultipleEntityParent = Records.getParent(ancestorMultipleEntity)(record)\n if (!ancestorMultipleEntityParent) return undefined\n\n const cacheKey = `${ancestorMultipleEntityParent.uuid}_${ancestorMultipleEntityDef.uuid}`\n let group = siblingKeyNodesCache.get(cacheKey)\n if (!group) {\n const keyDefs = Surveys.getNodeDefKeys({ survey, nodeDef: ancestorMultipleEntityDef })\n const entities = Records.getEntitySiblings(ancestorMultipleEntity, true)(record)\n const items: SiblingKeyNodeItem[] = []\n for (const entity of entities) {\n const keyNodes = Records.getEntityKeyNodes(survey, entity, undefined, keyDefs)(record)\n for (const keyNode of keyNodes) {\n if (!keyNode) continue\n const nodeValidation =\n record.validation && RecordValidations.getValidationNode({ nodeUuid: keyNode.uuid })(record.validation)\n items.push({ entityUuid: entity.uuid, keyNode, hasErrors: !!nodeValidation && !nodeValidation.valid })\n }\n }\n group = { items }\n siblingKeyNodesCache.set(cacheKey, group)\n }\n return { entityUuid: ancestorMultipleEntity.uuid, group }\n}\n\nconst _getValidationMessagesWithDefault = (params: {\n survey: Survey\n expression: NodeDefExpression\n defaultMessage?: string\n}): Labels => {\n const { survey, expression, defaultMessage } = params\n const messages: Labels = { ...expression.messages }\n\n const languages = Surveys.getLanguages(survey)\n const defaultMessageDefined = Objects.isNotEmpty(defaultMessage)\n\n for (const lang of languages) {\n const customMessage = messages[lang]\n if (Objects.isEmpty(customMessage) && defaultMessageDefined) {\n messages[lang] = defaultMessage\n }\n }\n\n return messages\n}\n\nconst _isValueRequired = async (\n params: AttributeValidatorParams & { nodeDef: NodeDef<NodeDefType, NodeDefProps> }\n): Promise<boolean> => {\n const { record, attribute, nodeDef } = params\n if (NodeDefs.isKey(nodeDef) || NodeDefs.isRequired(nodeDef)) return true\n if (!NodeDefs.isRequiredIfDefined(nodeDef)) return false\n const parentNode = Records.getParent(attribute)(record)\n if (!parentNode) return false\n // evaluated here (not read from the parent node meta flag) so validation doesn't depend on up to date node meta\n // (e.g. records created before the \"required if\" condition was defined)\n return evaluateRequiredIf({ ...params, nodeCtx: parentNode, nodeDef })\n}\n\nconst _validateRequired =\n (params: AttributeValidatorParams & { nodeDef: NodeDef<NodeDefType, NodeDefProps> }) =>\n async (_field: string, node: any): Promise<ValidationResult> => {\n const valid = !Nodes.isValueBlank(node) || !(await _isValueRequired(params))\n return valid\n ? ValidationResultFactory.createInstance()\n : ValidationResultFactory.createInstance({\n key: 'record.attribute.valueRequired',\n severity: ValidationSeverity.error,\n valid,\n })\n }\n\n/**\n * Evaluates the validation expressions.\n */\nconst _validateNodeValidations =\n (params: {\n user: User\n survey: Survey\n record: ArenaRecord\n nodeDef: NodeDef<NodeDefType, NodeDefProps>\n areaOfInterestProvider?: AreaOfInterestProvider\n }) =>\n async (_propName: string, node: ArenaRecordNode): Promise<ValidationResult> => {\n const { user, survey, record, nodeDef, areaOfInterestProvider } = params\n if (Nodes.isValueBlank(node)) return ValidationResultFactory.createInstance()\n\n const validations = NodeDefs.getValidations(nodeDef)\n if (!validations?.expressions?.length) return ValidationResultFactory.createInstance()\n\n const applicableExpressionsEval = await expressionEvaluator.evalApplicableExpressions({\n user,\n survey,\n record,\n areaOfInterestProvider,\n nodeCtx: node,\n expressions: validations.expressions,\n stopAtFirstFound: false,\n })\n\n let validationResult = ValidationResultFactory.createInstance()\n\n for (const { expression, value: valid } of applicableExpressionsEval) {\n if (!valid) {\n const messages = _getValidationMessagesWithDefault({\n survey,\n expression,\n defaultMessage: expression.expression,\n })\n\n validationResult = ValidationResultFactory.createInstance({\n valid: false,\n key: 'record.attribute.customValidation',\n severity: expression.severity,\n messages,\n })\n break\n }\n }\n\n return validationResult\n }\n\nconst validateAttribute = async (params: AttributeValidatorParams & { entityKeysCache?: EntityKeysCache }) => {\n const { survey, record, attribute } = params\n if (Records.isNodeApplicable(attribute)(record)) {\n const nodeDef = Surveys.getNodeDefByUuid({ survey, uuid: attribute.nodeDefUuid })\n const validatorParams = { ...params, nodeDef }\n return new Validator().validate(attribute, {\n [NodeKeys.value]: [\n _validateRequired(validatorParams),\n AttributeTypeValidator.validateValueType(validatorParams),\n _validateNodeValidations(validatorParams),\n AttributeKeyValidator.validateAttributeKey(validatorParams),\n AttributeUniqueValidator.validateAttributeUnique(validatorParams),\n ],\n })\n }\n return ValidationFactory.createInstance()\n}\n\nconst findSiblingKeyNodesToValidate = (\n params: AttributeValidatorParams & {\n nodeDef: NodeDef<NodeDefType, NodeDefProps>\n nodeParent?: ArenaRecordNode\n siblingKeyNodesCache: SiblingKeyNodesCache\n nodeUuidsToValidate: Set<string>\n }\n): ArenaRecordNode[] => {\n const { survey, nodeDef, nodeParent: parentNode, attribute, nodeUuidsToValidate } = params\n if (\n !NodeDefs.isKey(nodeDef) ||\n !parentNode ||\n (NodeDefs.isAutoIncrementalKey(nodeDef) && attribute.created && Nodes.isDefaultValueApplied(attribute))\n ) {\n // when a new node is created and the key is autoincremental, its value will be unique\n // and the validation of sibling key attributes is not necessary\n return []\n }\n // when a key attribute is updated, a sibling key attribute with the same value\n // or a sibling attribute with a validation error (duplicate key) should be re-validated\n const siblingNodeKeysGroup = _getSiblingNodeKeysGroup(params)\n if (!siblingNodeKeysGroup) return []\n\n const { entityUuid, group } = siblingNodeKeysGroup\n // key nodes in the nodes to validate are validated anyway: no need to compare their value\n group.itemsToCompare ??= group.items.filter((item) => !nodeUuidsToValidate.has(item.keyNode.uuid))\n\n const result: ArenaRecordNode[] = []\n for (const { entityUuid: itemEntityUuid, keyNode } of group.itemsToCompare) {\n if (\n itemEntityUuid !== entityUuid &&\n NodeValues.isValueEqual({ survey, nodeDef, parentNode, value: keyNode.value, valueSearch: attribute.value })\n ) {\n result.push(keyNode)\n }\n }\n return result.concat(_findSiblingKeyNodesWithErrorsNotAdded({ group, entityUuid }))\n}\n\n// key nodes with errors (in entities other than the attribute's one) are added only once per group\nconst _findSiblingKeyNodesWithErrorsNotAdded = (params: {\n group: SiblingKeyNodesGroup\n entityUuid: string\n}): ArenaRecordNode[] => {\n const { group, entityUuid } = params\n if (group.firstEntityUuid === undefined) {\n group.firstEntityUuid = entityUuid\n return group.items.filter((item) => item.hasErrors && item.entityUuid !== entityUuid).map((item) => item.keyNode)\n }\n if (group.allItemsWithErrorsAdded || group.firstEntityUuid === entityUuid) return []\n group.allItemsWithErrorsAdded = true\n const { firstEntityUuid } = group\n return group.items.filter((item) => item.hasErrors && item.entityUuid === firstEntityUuid).map((item) => item.keyNode)\n}\n\n// attribute siblings (same attribute def in the sibling entities) added to the nodes to validate,\n// by parent entity def and parent of the parent entity\ntype AttributeSiblingsAddedCache = Map<string, { firstParentEntity: ArenaRecordNode; allSiblingsAdded?: boolean }>\n\nconst findUniqueAttributeSiblingsToValidate = (params: {\n record: ArenaRecord\n node: ArenaRecordNode\n nodeDef: NodeDef<NodeDefType, NodeDefProps>\n nodeParent?: ArenaRecordNode\n attributeSiblingsAddedCache: AttributeSiblingsAddedCache\n}): ArenaRecordNode[] => {\n const { record, node, nodeDef, nodeParent, attributeSiblingsAddedCache } = params\n if (!nodeParent) return []\n const nodeParentParent = Records.getParent(nodeParent)(record)\n if (!nodeParentParent) return []\n // the siblings of the attribute are the attributes in the other parent entities:\n // after visiting attributes in 2 different parent entities, all the attributes of the group have been added\n const cacheKey = `${nodeParentParent.uuid}_${nodeParent.nodeDefUuid}_${nodeDef.uuid}`\n const cached = attributeSiblingsAddedCache.get(cacheKey)\n if (!cached) {\n attributeSiblingsAddedCache.set(cacheKey, { firstParentEntity: nodeParent })\n return Records.getAttributeSiblings(node, nodeDef)(record)\n }\n if (cached.allSiblingsAdded || cached.firstParentEntity.uuid === nodeParent.uuid) return []\n cached.allSiblingsAdded = true\n return Records.getChildren(cached.firstParentEntity, nodeDef.uuid)(record)\n}\n\nconst findSortedNodesToValidate = (params: SortedAttributesValidatorParams): ArenaRecordNode[] => {\n const { survey, record, nodesArray } = params\n\n const result: ArenaRecordNode[] = []\n const resultUuids = new Set<string>()\n const addToResult = (nodes: ArenaRecordNode[]) => {\n for (const node of nodes) {\n if (!resultUuids.has(node.uuid)) {\n resultUuids.add(node.uuid)\n result.push(node)\n }\n }\n }\n // every attribute in nodesArray is validated (it's included in its own dependents)\n const nodeUuidsToValidate = new Set(nodesArray.map((node) => node.uuid))\n const siblingKeyNodesCache: SiblingKeyNodesCache = new Map()\n const attributeSiblingsAddedCache: AttributeSiblingsAddedCache = new Map()\n for (const node of nodesArray) {\n const nodeDef = Surveys.getNodeDefByUuid({ survey, uuid: node.nodeDefUuid })\n if (!NodeDefs.isAttribute(nodeDef)) {\n continue\n }\n // Get dependents and attribute itself\n const nodePointersAttributeAndDependents = [\n ...Records.getDependentNodePointers(survey, node, SurveyDependencyType.validations, true)(record),\n ...Records.getDependentNodePointers(survey, node, SurveyDependencyType.required)(record),\n ]\n\n const nodeParent = Records.getParent(node)(record)\n\n addToResult(NodePointers.getNodesFromNodePointers({ record, nodePointers: nodePointersAttributeAndDependents }))\n addToResult(\n findSiblingKeyNodesToValidate({\n ...params,\n nodeDef,\n nodeParent,\n attribute: node,\n siblingKeyNodesCache,\n nodeUuidsToValidate,\n })\n )\n\n if (NodeDefs.getValidations(nodeDef)?.unique) {\n addToResult(\n findUniqueAttributeSiblingsToValidate({ record, node, nodeDef, nodeParent, attributeSiblingsAddedCache })\n )\n }\n }\n\n return result\n}\n\nconst validateSelfAndDependentSortedAttributes = async (\n params: SortedAttributesValidatorParams\n): Promise<ValidationFields> => {\n const nodesToValidate: ArenaRecordNode[] = findSortedNodesToValidate(params)\n const validationsByNodeUuid: ValidationFields = {}\n // the record doesn't change during the validation: entity keys can be cached\n const entityKeysCache = createEntityKeysCache()\n\n for (const nodeToValidate of nodesToValidate) {\n const nodeUuid = nodeToValidate.uuid\n // Validate only attributes not deleted and not validated already\n if (!nodeToValidate.deleted && !validationsByNodeUuid[nodeUuid]) {\n // sequential: avoid concurrent item/taxon lookups on large node sets\n const attributeValidatorParams = { ...params, attribute: nodeToValidate, entityKeysCache }\n validationsByNodeUuid[nodeUuid] = await validateAttribute(attributeValidatorParams) // NOSONAR\n }\n }\n return validationsByNodeUuid\n}\n\nexport const AttributeValidator = {\n validateSelfAndDependentSortedAttributes,\n}\n"]}
|