@openforis/arena-core 2.16.4 → 2.16.5

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.
@@ -8,6 +8,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  });
9
9
  };
10
10
  import { SystemError, SystemErrorKeys } from '../error';
11
+ import { NodeDefs } from '../nodeDef';
11
12
  import { SurveyDependencyType, Surveys } from '../survey';
12
13
  import { Dates, Objects } from '../utils';
13
14
  import { Validations } from '../validation/validations';
@@ -40,7 +41,7 @@ const getDependentValidationNodePointers = (params) => {
40
41
  return Array.from(uniquePointersByKey.values());
41
42
  };
42
43
  const _onRecordNodesCreateOrUpdate = (params) => __awaiter(void 0, void 0, void 0, function* () {
43
- const { user, survey, dateModified = Dates.nowFormattedForStorage() } = params;
44
+ const { user, survey, categoryItemProvider, taxonProvider, areaOfInterestProvider, additionalNodesToValidate, dateModified = Dates.nowFormattedForStorage(), } = params;
44
45
  const updateResult = yield RecordNodesUpdater.updateNodesDependents(params);
45
46
  const { nodes: updatedNodes, record: updatedRecord } = updateResult;
46
47
  const dependentValidationNodePointers = getDependentValidationNodePointers({
@@ -52,10 +53,13 @@ const _onRecordNodesCreateOrUpdate = (params) => __awaiter(void 0, void 0, void
52
53
  record: updatedRecord,
53
54
  nodePointers: dependentValidationNodePointers,
54
55
  });
55
- const nodesToValidate = Object.assign(Object.assign({}, updatedNodes), dependentValidationNodes);
56
+ const nodesToValidate = Object.assign(Object.assign(Object.assign({}, updatedNodes), dependentValidationNodes), additionalNodesToValidate);
56
57
  const validationUpdatedNodes = yield RecordValidator.validateNodes({
57
58
  user,
58
59
  survey,
60
+ categoryItemProvider,
61
+ taxonProvider,
62
+ areaOfInterestProvider,
59
63
  record: updatedRecord,
60
64
  nodes: nodesToValidate,
61
65
  });
@@ -91,11 +95,49 @@ const updateAttributeValue = (params) => __awaiter(void 0, void 0, void 0, funct
91
95
  const _record = Records.addNode(attributeUpdated, { sideEffect })(record);
92
96
  return _onRecordNodesCreateOrUpdate(Object.assign(Object.assign({}, params), { record: _record, nodes: nodesUpdated }));
93
97
  });
98
+ /**
99
+ * Returns the key and unique attributes of the siblings of the specified entities:
100
+ * they could have been duplicates of the ones being deleted, so they must be validated again.
101
+ */
102
+ const getEntityKeyOrUniqueAttributes = (params) => {
103
+ const { survey, record, entity, uniqueAttributeDefs } = params;
104
+ return [
105
+ ...Records.getEntityKeyNodes(survey, entity)(record),
106
+ ...uniqueAttributeDefs.flatMap((uniqueDef) => Records.getChildren(entity, uniqueDef.uuid)(record)),
107
+ ].filter(Boolean);
108
+ };
109
+ const getSiblingKeyOrUniqueAttributes = (params) => {
110
+ const { survey, record, nodeUuids } = params;
111
+ const nodeUuidsSet = new Set(nodeUuids);
112
+ const result = {};
113
+ for (const nodeUuid of nodeUuids) {
114
+ const node = Records.getNodeByUuid(nodeUuid)(record);
115
+ const parentNode = node ? Records.getParent(node)(record) : undefined;
116
+ const nodeDef = node ? Surveys.getNodeDefByUuid({ survey, uuid: node.nodeDefUuid }) : undefined;
117
+ if (!parentNode || !nodeDef || !NodeDefs.isEntity(nodeDef))
118
+ continue;
119
+ const uniqueAttributeDefs = Surveys.getNodeDefChildren({ survey, nodeDef }).filter((childDef) => { var _a; return (_a = NodeDefs.getValidations(childDef)) === null || _a === void 0 ? void 0 : _a.unique; });
120
+ const siblings = Records.getChildren(parentNode, nodeDef.uuid)(record).filter((s) => !nodeUuidsSet.has(s.uuid));
121
+ for (const sibling of siblings) {
122
+ for (const attribute of getEntityKeyOrUniqueAttributes({
123
+ survey,
124
+ record,
125
+ entity: sibling,
126
+ uniqueAttributeDefs,
127
+ })) {
128
+ result[attribute.uuid] = attribute;
129
+ }
130
+ }
131
+ }
132
+ return result;
133
+ };
94
134
  const deleteNodes = (params) => __awaiter(void 0, void 0, void 0, function* () {
95
- const { nodeUuids, record: _record, sideEffect = false } = params;
135
+ const { survey, nodeUuids, record: _record, sideEffect = false } = params;
136
+ // found before removing the nodes: the deleted nodes are no longer reachable from their siblings afterwards
137
+ const siblingKeyOrUniqueAttributes = getSiblingKeyOrUniqueAttributes({ survey, record: _record, nodeUuids });
96
138
  const updateResult = Records.removeNodes(nodeUuids, { sideEffect })(_record);
97
139
  const { record, nodesDeleted } = updateResult;
98
- const result = yield _onRecordNodesCreateOrUpdate(Object.assign(Object.assign({}, params), { record, nodes: nodesDeleted }));
140
+ const result = yield _onRecordNodesCreateOrUpdate(Object.assign(Object.assign({}, params), { record, nodes: nodesDeleted, additionalNodesToValidate: siblingKeyOrUniqueAttributes }));
99
141
  result.nodesDeleted = nodesDeleted;
100
142
  return result;
101
143
  });
@@ -1 +1 @@
1
- {"version":3,"file":"recordUpdater.js","sourceRoot":"","sources":["../../../src/record/recordUpdater.ts"],"names":[],"mappings":"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","sourcesContent":["import { Dictionary } from '../common'\nimport { SystemError, SystemErrorKeys } from '../error'\nimport { ArenaRecordNode, NodePointer, NodesMap } from '../node'\nimport { Survey, SurveyDependencyType, Surveys } from '../survey'\nimport { Dates, Objects } from '../utils'\nimport { Validations } from '../validation/validations'\nimport { NodePointers } from './nodePointers'\nimport { ArenaRecord } from './record'\nimport { RecordNodesUpdater, RecordUpdateResult } from './recordNodesUpdater'\nimport { NodeCreateParams, NodesUpdateParams } from './recordNodesUpdater/recordNodesCreator'\nimport { Records } from './records'\nimport { RecordValidator } from './recordValidator'\n\nconst validationDependencyTypes = [\n SurveyDependencyType.validations,\n SurveyDependencyType.required,\n SurveyDependencyType.minCount,\n SurveyDependencyType.maxCount,\n]\n\nconst getDependentValidationNodePointers = (params: {\n survey: Survey\n record: ArenaRecord\n nodes: NodesMap\n}): NodePointer[] => {\n const { survey, record, nodes } = params\n const uniquePointersByKey = new Map<string, NodePointer>()\n for (const node of Object.values(nodes)) {\n for (const dependencyType of validationDependencyTypes) {\n const nodePointers = Records.getDependentNodePointers(survey, node, dependencyType)(record)\n for (const pointer of nodePointers) {\n const nodeCtxUuid = pointer.nodeCtx.uuid\n const nodeDefUuid = pointer.nodeDef.uuid\n const key = `${nodeCtxUuid ?? ''}:${nodeDefUuid ?? ''}`\n if (!uniquePointersByKey.has(key)) {\n uniquePointersByKey.set(key, pointer)\n }\n }\n }\n }\n return Array.from(uniquePointersByKey.values())\n}\n\nconst _onRecordNodesCreateOrUpdate = async (\n params: NodesUpdateParams & { nodes: Dictionary<ArenaRecordNode> }\n): Promise<RecordUpdateResult> => {\n const { user, survey, dateModified = Dates.nowFormattedForStorage() } = params\n\n const updateResult = await RecordNodesUpdater.updateNodesDependents(params)\n const { nodes: updatedNodes, record: updatedRecord } = updateResult\n\n const dependentValidationNodePointers = getDependentValidationNodePointers({\n survey,\n record: updatedRecord,\n nodes: updatedNodes,\n })\n\n const dependentValidationNodes = NodePointers.getNodesMapFromNodePointers({\n record: updatedRecord,\n nodePointers: dependentValidationNodePointers,\n })\n\n const nodesToValidate = { ...updatedNodes, ...dependentValidationNodes }\n\n const validationUpdatedNodes = await RecordValidator.validateNodes({\n user,\n survey,\n record: updatedRecord,\n nodes: nodesToValidate,\n })\n\n const validation = Validations.mergeValidations(validationUpdatedNodes)(Validations.getValidation(updatedRecord))\n updatedRecord.validation = validation\n updatedRecord.dateModified = dateModified\n\n updateResult.validation = validation\n\n return updateResult\n}\n\nconst createDescendants = async (params: NodeCreateParams): Promise<RecordUpdateResult> => {\n const { record: updatedRecord, nodes: createdNodes } = await RecordNodesUpdater.addDescendants(params)\n\n return _onRecordNodesCreateOrUpdate({ ...params, record: updatedRecord, nodes: createdNodes })\n}\n\nconst createNodeAndDescendants = async (params: NodeCreateParams): Promise<RecordUpdateResult> => {\n const { record: updatedRecord, nodes: createdNodes } = await RecordNodesUpdater.addNodeAndDescendants(params)\n\n return _onRecordNodesCreateOrUpdate({ ...params, record: updatedRecord, nodes: createdNodes })\n}\n\nconst createRootEntity = async (\n params: NodesUpdateParams & {\n createMultipleEntities?: boolean\n }\n): Promise<RecordUpdateResult> => {\n const { survey, createMultipleEntities = true } = params\n\n return createNodeAndDescendants({ ...params, nodeDef: Surveys.getNodeDefRoot({ survey }), createMultipleEntities })\n}\n\nconst updateAttributeValue = async (\n params: NodesUpdateParams & {\n attributeUuid: string\n value: any\n }\n): Promise<RecordUpdateResult> => {\n const { attributeUuid, record, value, dateModified = Dates.nowFormattedForStorage(), sideEffect = false } = params\n const attribute = Records.getNodeByUuid(attributeUuid)(record)\n if (!attribute) throw new SystemError(SystemErrorKeys.record.nodeNotFound, { uuid: attributeUuid })\n\n let attributeUpdated = sideEffect ? attribute : { ...attribute }\n attributeUpdated.value = value\n attributeUpdated.dateModified = dateModified\n // reset defaultValueApplied flag\n attributeUpdated = Objects.dissocPath({ obj: attributeUpdated, path: ['meta', 'defaultValueApplied'], sideEffect })\n\n const nodesUpdated = { [attributeUuid]: attributeUpdated }\n\n const _record = Records.addNode(attributeUpdated, { sideEffect })(record)\n\n return _onRecordNodesCreateOrUpdate({ ...params, record: _record, nodes: nodesUpdated })\n}\n\nconst deleteNodes = async (\n params: NodesUpdateParams & {\n nodeUuids: string[]\n }\n): Promise<RecordUpdateResult> => {\n const { nodeUuids, record: _record, sideEffect = false } = params\n\n const updateResult = Records.removeNodes(nodeUuids, { sideEffect })(_record)\n const { record, nodesDeleted } = updateResult\n\n const result = await _onRecordNodesCreateOrUpdate({ ...params, record, nodes: nodesDeleted })\n result.nodesDeleted = nodesDeleted\n return result\n}\n\nconst deleteNode = async (params: NodesUpdateParams & { nodeUuid: string }): Promise<RecordUpdateResult> => {\n const { nodeUuid } = params\n return deleteNodes({ ...params, nodeUuids: [nodeUuid] })\n}\n\nexport const RecordUpdater = {\n /** Creates the descendants of a node and updates dependents (applicability, default values, calculations...) and validation */\n createDescendants,\n /** Creates a node and its descendants and updates dependents (applicability, default values, calculations...) and validation */\n createNodeAndDescendants,\n createRootEntity,\n updateAttributeValue,\n /** Deletes the node and its descendants and updates dependents (applicability, default values, calculations...) and validation */\n deleteNode,\n /** Deletes the nodes and their descendants and updates dependents (applicability, default values, calculations...) and validation */\n deleteNodes,\n}\n"]}
1
+ {"version":3,"file":"recordUpdater.js","sourceRoot":"","sources":["../../../src/record/recordUpdater.ts"],"names":[],"mappings":"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","sourcesContent":["import { Dictionary } from '../common'\nimport { SystemError, SystemErrorKeys } from '../error'\nimport { ArenaRecordNode, NodePointer, NodesMap } from '../node'\nimport { NodeDef, NodeDefs } from '../nodeDef'\nimport { Survey, SurveyDependencyType, Surveys } from '../survey'\nimport { Dates, Objects } from '../utils'\nimport { Validations } from '../validation/validations'\nimport { NodePointers } from './nodePointers'\nimport { ArenaRecord } from './record'\nimport { RecordNodesUpdater, RecordUpdateResult } from './recordNodesUpdater'\nimport { NodeCreateParams, NodesUpdateParams } from './recordNodesUpdater/recordNodesCreator'\nimport { Records } from './records'\nimport { RecordValidator } from './recordValidator'\n\nconst validationDependencyTypes = [\n SurveyDependencyType.validations,\n SurveyDependencyType.required,\n SurveyDependencyType.minCount,\n SurveyDependencyType.maxCount,\n]\n\nconst getDependentValidationNodePointers = (params: {\n survey: Survey\n record: ArenaRecord\n nodes: NodesMap\n}): NodePointer[] => {\n const { survey, record, nodes } = params\n const uniquePointersByKey = new Map<string, NodePointer>()\n for (const node of Object.values(nodes)) {\n for (const dependencyType of validationDependencyTypes) {\n const nodePointers = Records.getDependentNodePointers(survey, node, dependencyType)(record)\n for (const pointer of nodePointers) {\n const nodeCtxUuid = pointer.nodeCtx.uuid\n const nodeDefUuid = pointer.nodeDef.uuid\n const key = `${nodeCtxUuid ?? ''}:${nodeDefUuid ?? ''}`\n if (!uniquePointersByKey.has(key)) {\n uniquePointersByKey.set(key, pointer)\n }\n }\n }\n }\n return Array.from(uniquePointersByKey.values())\n}\n\nconst _onRecordNodesCreateOrUpdate = async (\n params: NodesUpdateParams & { nodes: Dictionary<ArenaRecordNode>; additionalNodesToValidate?: NodesMap }\n): Promise<RecordUpdateResult> => {\n const {\n user,\n survey,\n categoryItemProvider,\n taxonProvider,\n areaOfInterestProvider,\n additionalNodesToValidate,\n dateModified = Dates.nowFormattedForStorage(),\n } = params\n\n const updateResult = await RecordNodesUpdater.updateNodesDependents(params)\n const { nodes: updatedNodes, record: updatedRecord } = updateResult\n\n const dependentValidationNodePointers = getDependentValidationNodePointers({\n survey,\n record: updatedRecord,\n nodes: updatedNodes,\n })\n\n const dependentValidationNodes = NodePointers.getNodesMapFromNodePointers({\n record: updatedRecord,\n nodePointers: dependentValidationNodePointers,\n })\n\n const nodesToValidate = { ...updatedNodes, ...dependentValidationNodes, ...additionalNodesToValidate }\n\n const validationUpdatedNodes = await RecordValidator.validateNodes({\n user,\n survey,\n categoryItemProvider,\n taxonProvider,\n areaOfInterestProvider,\n record: updatedRecord,\n nodes: nodesToValidate,\n })\n\n const validation = Validations.mergeValidations(validationUpdatedNodes)(Validations.getValidation(updatedRecord))\n updatedRecord.validation = validation\n updatedRecord.dateModified = dateModified\n\n updateResult.validation = validation\n\n return updateResult\n}\n\nconst createDescendants = async (params: NodeCreateParams): Promise<RecordUpdateResult> => {\n const { record: updatedRecord, nodes: createdNodes } = await RecordNodesUpdater.addDescendants(params)\n\n return _onRecordNodesCreateOrUpdate({ ...params, record: updatedRecord, nodes: createdNodes })\n}\n\nconst createNodeAndDescendants = async (params: NodeCreateParams): Promise<RecordUpdateResult> => {\n const { record: updatedRecord, nodes: createdNodes } = await RecordNodesUpdater.addNodeAndDescendants(params)\n\n return _onRecordNodesCreateOrUpdate({ ...params, record: updatedRecord, nodes: createdNodes })\n}\n\nconst createRootEntity = async (\n params: NodesUpdateParams & {\n createMultipleEntities?: boolean\n }\n): Promise<RecordUpdateResult> => {\n const { survey, createMultipleEntities = true } = params\n\n return createNodeAndDescendants({ ...params, nodeDef: Surveys.getNodeDefRoot({ survey }), createMultipleEntities })\n}\n\nconst updateAttributeValue = async (\n params: NodesUpdateParams & {\n attributeUuid: string\n value: any\n }\n): Promise<RecordUpdateResult> => {\n const { attributeUuid, record, value, dateModified = Dates.nowFormattedForStorage(), sideEffect = false } = params\n const attribute = Records.getNodeByUuid(attributeUuid)(record)\n if (!attribute) throw new SystemError(SystemErrorKeys.record.nodeNotFound, { uuid: attributeUuid })\n\n let attributeUpdated = sideEffect ? attribute : { ...attribute }\n attributeUpdated.value = value\n attributeUpdated.dateModified = dateModified\n // reset defaultValueApplied flag\n attributeUpdated = Objects.dissocPath({ obj: attributeUpdated, path: ['meta', 'defaultValueApplied'], sideEffect })\n\n const nodesUpdated = { [attributeUuid]: attributeUpdated }\n\n const _record = Records.addNode(attributeUpdated, { sideEffect })(record)\n\n return _onRecordNodesCreateOrUpdate({ ...params, record: _record, nodes: nodesUpdated })\n}\n\n/**\n * Returns the key and unique attributes of the siblings of the specified entities:\n * they could have been duplicates of the ones being deleted, so they must be validated again.\n */\nconst getEntityKeyOrUniqueAttributes = (params: {\n survey: Survey\n record: ArenaRecord\n entity: ArenaRecordNode\n uniqueAttributeDefs: NodeDef<any>[]\n}): ArenaRecordNode[] => {\n const { survey, record, entity, uniqueAttributeDefs } = params\n return [\n ...Records.getEntityKeyNodes(survey, entity)(record),\n ...uniqueAttributeDefs.flatMap((uniqueDef) => Records.getChildren(entity, uniqueDef.uuid)(record)),\n ].filter(Boolean)\n}\n\nconst getSiblingKeyOrUniqueAttributes = (params: {\n survey: Survey\n record: ArenaRecord\n nodeUuids: string[]\n}): NodesMap => {\n const { survey, record, nodeUuids } = params\n const nodeUuidsSet = new Set(nodeUuids)\n const result: NodesMap = {}\n for (const nodeUuid of nodeUuids) {\n const node = Records.getNodeByUuid(nodeUuid)(record)\n const parentNode = node ? Records.getParent(node)(record) : undefined\n const nodeDef = node ? Surveys.getNodeDefByUuid({ survey, uuid: node.nodeDefUuid }) : undefined\n if (!parentNode || !nodeDef || !NodeDefs.isEntity(nodeDef)) continue\n\n const uniqueAttributeDefs = Surveys.getNodeDefChildren({ survey, nodeDef }).filter(\n (childDef) => NodeDefs.getValidations(childDef)?.unique\n )\n const siblings = Records.getChildren(parentNode, nodeDef.uuid)(record).filter((s) => !nodeUuidsSet.has(s.uuid))\n for (const sibling of siblings) {\n for (const attribute of getEntityKeyOrUniqueAttributes({\n survey,\n record,\n entity: sibling,\n uniqueAttributeDefs,\n })) {\n result[attribute.uuid] = attribute\n }\n }\n }\n return result\n}\n\nconst deleteNodes = async (\n params: NodesUpdateParams & {\n nodeUuids: string[]\n }\n): Promise<RecordUpdateResult> => {\n const { survey, nodeUuids, record: _record, sideEffect = false } = params\n\n // found before removing the nodes: the deleted nodes are no longer reachable from their siblings afterwards\n const siblingKeyOrUniqueAttributes = getSiblingKeyOrUniqueAttributes({ survey, record: _record, nodeUuids })\n\n const updateResult = Records.removeNodes(nodeUuids, { sideEffect })(_record)\n const { record, nodesDeleted } = updateResult\n\n const result = await _onRecordNodesCreateOrUpdate({\n ...params,\n record,\n nodes: nodesDeleted,\n additionalNodesToValidate: siblingKeyOrUniqueAttributes,\n })\n result.nodesDeleted = nodesDeleted\n return result\n}\n\nconst deleteNode = async (params: NodesUpdateParams & { nodeUuid: string }): Promise<RecordUpdateResult> => {\n const { nodeUuid } = params\n return deleteNodes({ ...params, nodeUuids: [nodeUuid] })\n}\n\nexport const RecordUpdater = {\n /** Creates the descendants of a node and updates dependents (applicability, default values, calculations...) and validation */\n createDescendants,\n /** Creates a node and its descendants and updates dependents (applicability, default values, calculations...) and validation */\n createNodeAndDescendants,\n createRootEntity,\n updateAttributeValue,\n /** Deletes the node and its descendants and updates dependents (applicability, default values, calculations...) and validation */\n deleteNode,\n /** Deletes the nodes and their descendants and updates dependents (applicability, default values, calculations...) and validation */\n deleteNodes,\n}\n"]}
@@ -40,7 +40,8 @@ const _isEntityDuplicate = (params) => {
40
40
  return false;
41
41
  }
42
42
  const keyValuesByDefUuid = _getKeyValuesByDefUuid({ survey, record, entity, keyDefs, cache });
43
- return (!Objects.isEmpty(keyValuesByDefUuid) &&
43
+ // entities with all key values empty are never duplicates (they are reported as missing values)
44
+ return (!Object.values(keyValuesByDefUuid).every(Objects.isEmpty) &&
44
45
  siblingEntities.some((sibilingEntity) => {
45
46
  const siblingKeyValuesByDefUuid = _getKeyValuesByDefUuid({
46
47
  survey,
@@ -1 +1 @@
1
- {"version":3,"file":"attributeKeyValidator.js","sourceRoot":"","sources":["../../../../src/record/recordValidator/attributeKeyValidator.ts"],"names":[],"mappings":"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","sourcesContent":["import { ArenaRecord } from '../record'\nimport { Records } from '../records'\nimport { NodeDef, NodeDefs, NodeDefProps, NodeDefType } from '../../nodeDef'\nimport { ArenaRecordNode, Nodes, NodeValues } from '../../node'\nimport { ValidationResult, ValidationResultFactory, ValidationSeverity } from '../../validation'\nimport { Survey, Surveys } from '../../survey'\n\nimport { Dictionary } from '../../common'\nimport { Objects } from '../../utils'\n\n/**\n * Cache of entity key values (and key defs by entity def) used during a single validation run (the record doesn't change):\n * without it, validating the keys of S sibling entities would compute the key values of every sibling S times.\n */\nexport type EntityKeysCache = {\n keyDefsByEntityDefUuid: Map<string, NodeDef<NodeDefType, NodeDefProps>[]>\n keyValuesByEntityUuid: Map<string, Dictionary<any>>\n}\n\nexport const createEntityKeysCache = (): EntityKeysCache => ({\n keyDefsByEntityDefUuid: new Map(),\n keyValuesByEntityUuid: new Map(),\n})\n\nconst _getKeyDefs = (params: {\n survey: Survey\n entityDef: NodeDef<NodeDefType, NodeDefProps>\n cache?: EntityKeysCache\n}): NodeDef<NodeDefType, NodeDefProps>[] => {\n const { survey, entityDef, cache } = params\n let keyDefs = cache?.keyDefsByEntityDefUuid.get(entityDef.uuid)\n if (!keyDefs) {\n keyDefs = Surveys.getNodeDefKeys({ survey, nodeDef: entityDef })\n cache?.keyDefsByEntityDefUuid.set(entityDef.uuid, keyDefs)\n }\n return keyDefs\n}\n\nconst _getKeyValuesByDefUuid = (params: {\n survey: Survey\n record: ArenaRecord\n entity: ArenaRecordNode\n keyDefs: NodeDef<NodeDefType, NodeDefProps>[]\n cache?: EntityKeysCache\n}): Dictionary<any> => {\n const { survey, record, entity, keyDefs, cache } = params\n let keyValues = cache?.keyValuesByEntityUuid.get(entity.uuid)\n if (!keyValues) {\n keyValues = Records.getEntityKeyValuesByDefUuid(survey, entity, undefined, keyDefs)(record)\n cache?.keyValuesByEntityUuid.set(entity.uuid, keyValues)\n }\n return keyValues\n}\n\nconst _isEntityDuplicate = (params: {\n survey: Survey\n record: ArenaRecord\n entity: ArenaRecordNode\n entityKeysCache?: EntityKeysCache\n}): boolean => {\n const { survey, entity, record, entityKeysCache: cache } = params\n // 1. get sibling entities\n const nodeParent = Records.getParent(entity)(record)\n if (!nodeParent) return false\n\n const siblingEntities = Records.getChildren(\n nodeParent,\n entity.nodeDefUuid\n )(record).filter((node) => !Nodes.areEqual(entity, node))\n\n // 2. get key values\n const entityDef = Surveys.getNodeDefByUuid({ survey, uuid: entity.nodeDefUuid })\n const keyDefs = _getKeyDefs({ survey, entityDef, cache })\n\n if (Objects.isEmpty(siblingEntities) || Objects.isEmpty(keyDefs)) {\n return false\n }\n const keyValuesByDefUuid = _getKeyValuesByDefUuid({ survey, record, entity, keyDefs, cache })\n return (\n !Objects.isEmpty(keyValuesByDefUuid) &&\n siblingEntities.some((sibilingEntity) => {\n const siblingKeyValuesByDefUuid = _getKeyValuesByDefUuid({\n survey,\n record,\n entity: sibilingEntity,\n keyDefs,\n cache,\n })\n return keyDefs.every((keyDef) => {\n const keyValue = keyValuesByDefUuid[keyDef.uuid]\n const siblingKeyValue = siblingKeyValuesByDefUuid[keyDef.uuid]\n return NodeValues.isValueEqual({\n survey,\n nodeDef: keyDef,\n value: keyValue,\n valueSearch: siblingKeyValue,\n })\n })\n })\n )\n}\n\nconst isNodeDefToBeValidated = (params: { survey: Survey; nodeDef: NodeDef<NodeDefType, NodeDefProps> }): boolean => {\n const { survey, nodeDef } = params\n if (!NodeDefs.isKey(nodeDef)) return false\n\n const nodeDefParent = Surveys.getNodeDefParent({ survey, nodeDef })\n // root entity key attributes will be validated by another validator\n return !!nodeDefParent && !NodeDefs.isRoot(nodeDefParent)\n}\n\nconst validateAttributeKey =\n (params: {\n survey: Survey\n record: ArenaRecord\n nodeDef: NodeDef<NodeDefType, NodeDefProps>\n entityKeysCache?: EntityKeysCache\n }) =>\n (_propName: string, node: ArenaRecordNode): Promise<ValidationResult> => {\n const { survey, nodeDef, record, entityKeysCache } = params\n\n if (isNodeDefToBeValidated({ survey, nodeDef })) {\n const entity = Records.getParent(node)(record)\n if (entity && _isEntityDuplicate({ survey, record, entity, entityKeysCache })) {\n return Promise.resolve(\n ValidationResultFactory.createInstance({\n valid: false,\n severity: ValidationSeverity.error,\n key: 'record.entity.keyDuplicate',\n })\n )\n }\n }\n\n return Promise.resolve(ValidationResultFactory.createInstance())\n }\n\nexport const AttributeKeyValidator = {\n validateAttributeKey,\n}\n"]}
1
+ {"version":3,"file":"attributeKeyValidator.js","sourceRoot":"","sources":["../../../../src/record/recordValidator/attributeKeyValidator.ts"],"names":[],"mappings":"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","sourcesContent":["import { ArenaRecord } from '../record'\nimport { Records } from '../records'\nimport { NodeDef, NodeDefs, NodeDefProps, NodeDefType } from '../../nodeDef'\nimport { ArenaRecordNode, Nodes, NodeValues } from '../../node'\nimport { ValidationResult, ValidationResultFactory, ValidationSeverity } from '../../validation'\nimport { Survey, Surveys } from '../../survey'\n\nimport { Dictionary } from '../../common'\nimport { Objects } from '../../utils'\n\n/**\n * Cache of entity key values (and key defs by entity def) used during a single validation run (the record doesn't change):\n * without it, validating the keys of S sibling entities would compute the key values of every sibling S times.\n */\nexport type EntityKeysCache = {\n keyDefsByEntityDefUuid: Map<string, NodeDef<NodeDefType, NodeDefProps>[]>\n keyValuesByEntityUuid: Map<string, Dictionary<any>>\n}\n\nexport const createEntityKeysCache = (): EntityKeysCache => ({\n keyDefsByEntityDefUuid: new Map(),\n keyValuesByEntityUuid: new Map(),\n})\n\nconst _getKeyDefs = (params: {\n survey: Survey\n entityDef: NodeDef<NodeDefType, NodeDefProps>\n cache?: EntityKeysCache\n}): NodeDef<NodeDefType, NodeDefProps>[] => {\n const { survey, entityDef, cache } = params\n let keyDefs = cache?.keyDefsByEntityDefUuid.get(entityDef.uuid)\n if (!keyDefs) {\n keyDefs = Surveys.getNodeDefKeys({ survey, nodeDef: entityDef })\n cache?.keyDefsByEntityDefUuid.set(entityDef.uuid, keyDefs)\n }\n return keyDefs\n}\n\nconst _getKeyValuesByDefUuid = (params: {\n survey: Survey\n record: ArenaRecord\n entity: ArenaRecordNode\n keyDefs: NodeDef<NodeDefType, NodeDefProps>[]\n cache?: EntityKeysCache\n}): Dictionary<any> => {\n const { survey, record, entity, keyDefs, cache } = params\n let keyValues = cache?.keyValuesByEntityUuid.get(entity.uuid)\n if (!keyValues) {\n keyValues = Records.getEntityKeyValuesByDefUuid(survey, entity, undefined, keyDefs)(record)\n cache?.keyValuesByEntityUuid.set(entity.uuid, keyValues)\n }\n return keyValues\n}\n\nconst _isEntityDuplicate = (params: {\n survey: Survey\n record: ArenaRecord\n entity: ArenaRecordNode\n entityKeysCache?: EntityKeysCache\n}): boolean => {\n const { survey, entity, record, entityKeysCache: cache } = params\n // 1. get sibling entities\n const nodeParent = Records.getParent(entity)(record)\n if (!nodeParent) return false\n\n const siblingEntities = Records.getChildren(\n nodeParent,\n entity.nodeDefUuid\n )(record).filter((node) => !Nodes.areEqual(entity, node))\n\n // 2. get key values\n const entityDef = Surveys.getNodeDefByUuid({ survey, uuid: entity.nodeDefUuid })\n const keyDefs = _getKeyDefs({ survey, entityDef, cache })\n\n if (Objects.isEmpty(siblingEntities) || Objects.isEmpty(keyDefs)) {\n return false\n }\n const keyValuesByDefUuid = _getKeyValuesByDefUuid({ survey, record, entity, keyDefs, cache })\n // entities with all key values empty are never duplicates (they are reported as missing values)\n return (\n !Object.values(keyValuesByDefUuid).every(Objects.isEmpty) &&\n siblingEntities.some((sibilingEntity) => {\n const siblingKeyValuesByDefUuid = _getKeyValuesByDefUuid({\n survey,\n record,\n entity: sibilingEntity,\n keyDefs,\n cache,\n })\n return keyDefs.every((keyDef) => {\n const keyValue = keyValuesByDefUuid[keyDef.uuid]\n const siblingKeyValue = siblingKeyValuesByDefUuid[keyDef.uuid]\n return NodeValues.isValueEqual({\n survey,\n nodeDef: keyDef,\n value: keyValue,\n valueSearch: siblingKeyValue,\n })\n })\n })\n )\n}\n\nconst isNodeDefToBeValidated = (params: { survey: Survey; nodeDef: NodeDef<NodeDefType, NodeDefProps> }): boolean => {\n const { survey, nodeDef } = params\n if (!NodeDefs.isKey(nodeDef)) return false\n\n const nodeDefParent = Surveys.getNodeDefParent({ survey, nodeDef })\n // root entity key attributes will be validated by another validator\n return !!nodeDefParent && !NodeDefs.isRoot(nodeDefParent)\n}\n\nconst validateAttributeKey =\n (params: {\n survey: Survey\n record: ArenaRecord\n nodeDef: NodeDef<NodeDefType, NodeDefProps>\n entityKeysCache?: EntityKeysCache\n }) =>\n (_propName: string, node: ArenaRecordNode): Promise<ValidationResult> => {\n const { survey, nodeDef, record, entityKeysCache } = params\n\n if (isNodeDefToBeValidated({ survey, nodeDef })) {\n const entity = Records.getParent(node)(record)\n if (entity && _isEntityDuplicate({ survey, record, entity, entityKeysCache })) {\n return Promise.resolve(\n ValidationResultFactory.createInstance({\n valid: false,\n severity: ValidationSeverity.error,\n key: 'record.entity.keyDuplicate',\n })\n )\n }\n }\n\n return Promise.resolve(ValidationResultFactory.createInstance())\n }\n\nexport const AttributeKeyValidator = {\n validateAttributeKey,\n}\n"]}
@@ -11,6 +11,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.RecordUpdater = void 0;
13
13
  const error_1 = require("../error");
14
+ const nodeDef_1 = require("../nodeDef");
14
15
  const survey_1 = require("../survey");
15
16
  const utils_1 = require("../utils");
16
17
  const validations_1 = require("../validation/validations");
@@ -43,7 +44,7 @@ const getDependentValidationNodePointers = (params) => {
43
44
  return Array.from(uniquePointersByKey.values());
44
45
  };
45
46
  const _onRecordNodesCreateOrUpdate = (params) => __awaiter(void 0, void 0, void 0, function* () {
46
- const { user, survey, dateModified = utils_1.Dates.nowFormattedForStorage() } = params;
47
+ const { user, survey, categoryItemProvider, taxonProvider, areaOfInterestProvider, additionalNodesToValidate, dateModified = utils_1.Dates.nowFormattedForStorage(), } = params;
47
48
  const updateResult = yield recordNodesUpdater_1.RecordNodesUpdater.updateNodesDependents(params);
48
49
  const { nodes: updatedNodes, record: updatedRecord } = updateResult;
49
50
  const dependentValidationNodePointers = getDependentValidationNodePointers({
@@ -55,10 +56,13 @@ const _onRecordNodesCreateOrUpdate = (params) => __awaiter(void 0, void 0, void
55
56
  record: updatedRecord,
56
57
  nodePointers: dependentValidationNodePointers,
57
58
  });
58
- const nodesToValidate = Object.assign(Object.assign({}, updatedNodes), dependentValidationNodes);
59
+ const nodesToValidate = Object.assign(Object.assign(Object.assign({}, updatedNodes), dependentValidationNodes), additionalNodesToValidate);
59
60
  const validationUpdatedNodes = yield recordValidator_1.RecordValidator.validateNodes({
60
61
  user,
61
62
  survey,
63
+ categoryItemProvider,
64
+ taxonProvider,
65
+ areaOfInterestProvider,
62
66
  record: updatedRecord,
63
67
  nodes: nodesToValidate,
64
68
  });
@@ -94,11 +98,49 @@ const updateAttributeValue = (params) => __awaiter(void 0, void 0, void 0, funct
94
98
  const _record = records_1.Records.addNode(attributeUpdated, { sideEffect })(record);
95
99
  return _onRecordNodesCreateOrUpdate(Object.assign(Object.assign({}, params), { record: _record, nodes: nodesUpdated }));
96
100
  });
101
+ /**
102
+ * Returns the key and unique attributes of the siblings of the specified entities:
103
+ * they could have been duplicates of the ones being deleted, so they must be validated again.
104
+ */
105
+ const getEntityKeyOrUniqueAttributes = (params) => {
106
+ const { survey, record, entity, uniqueAttributeDefs } = params;
107
+ return [
108
+ ...records_1.Records.getEntityKeyNodes(survey, entity)(record),
109
+ ...uniqueAttributeDefs.flatMap((uniqueDef) => records_1.Records.getChildren(entity, uniqueDef.uuid)(record)),
110
+ ].filter(Boolean);
111
+ };
112
+ const getSiblingKeyOrUniqueAttributes = (params) => {
113
+ const { survey, record, nodeUuids } = params;
114
+ const nodeUuidsSet = new Set(nodeUuids);
115
+ const result = {};
116
+ for (const nodeUuid of nodeUuids) {
117
+ const node = records_1.Records.getNodeByUuid(nodeUuid)(record);
118
+ const parentNode = node ? records_1.Records.getParent(node)(record) : undefined;
119
+ const nodeDef = node ? survey_1.Surveys.getNodeDefByUuid({ survey, uuid: node.nodeDefUuid }) : undefined;
120
+ if (!parentNode || !nodeDef || !nodeDef_1.NodeDefs.isEntity(nodeDef))
121
+ continue;
122
+ const uniqueAttributeDefs = survey_1.Surveys.getNodeDefChildren({ survey, nodeDef }).filter((childDef) => { var _a; return (_a = nodeDef_1.NodeDefs.getValidations(childDef)) === null || _a === void 0 ? void 0 : _a.unique; });
123
+ const siblings = records_1.Records.getChildren(parentNode, nodeDef.uuid)(record).filter((s) => !nodeUuidsSet.has(s.uuid));
124
+ for (const sibling of siblings) {
125
+ for (const attribute of getEntityKeyOrUniqueAttributes({
126
+ survey,
127
+ record,
128
+ entity: sibling,
129
+ uniqueAttributeDefs,
130
+ })) {
131
+ result[attribute.uuid] = attribute;
132
+ }
133
+ }
134
+ }
135
+ return result;
136
+ };
97
137
  const deleteNodes = (params) => __awaiter(void 0, void 0, void 0, function* () {
98
- const { nodeUuids, record: _record, sideEffect = false } = params;
138
+ const { survey, nodeUuids, record: _record, sideEffect = false } = params;
139
+ // found before removing the nodes: the deleted nodes are no longer reachable from their siblings afterwards
140
+ const siblingKeyOrUniqueAttributes = getSiblingKeyOrUniqueAttributes({ survey, record: _record, nodeUuids });
99
141
  const updateResult = records_1.Records.removeNodes(nodeUuids, { sideEffect })(_record);
100
142
  const { record, nodesDeleted } = updateResult;
101
- const result = yield _onRecordNodesCreateOrUpdate(Object.assign(Object.assign({}, params), { record, nodes: nodesDeleted }));
143
+ const result = yield _onRecordNodesCreateOrUpdate(Object.assign(Object.assign({}, params), { record, nodes: nodesDeleted, additionalNodesToValidate: siblingKeyOrUniqueAttributes }));
102
144
  result.nodesDeleted = nodesDeleted;
103
145
  return result;
104
146
  });
@@ -1 +1 @@
1
- {"version":3,"file":"recordUpdater.js","sourceRoot":"","sources":["../../src/record/recordUpdater.ts"],"names":[],"mappings":"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","sourcesContent":["import { Dictionary } from '../common'\nimport { SystemError, SystemErrorKeys } from '../error'\nimport { ArenaRecordNode, NodePointer, NodesMap } from '../node'\nimport { Survey, SurveyDependencyType, Surveys } from '../survey'\nimport { Dates, Objects } from '../utils'\nimport { Validations } from '../validation/validations'\nimport { NodePointers } from './nodePointers'\nimport { ArenaRecord } from './record'\nimport { RecordNodesUpdater, RecordUpdateResult } from './recordNodesUpdater'\nimport { NodeCreateParams, NodesUpdateParams } from './recordNodesUpdater/recordNodesCreator'\nimport { Records } from './records'\nimport { RecordValidator } from './recordValidator'\n\nconst validationDependencyTypes = [\n SurveyDependencyType.validations,\n SurveyDependencyType.required,\n SurveyDependencyType.minCount,\n SurveyDependencyType.maxCount,\n]\n\nconst getDependentValidationNodePointers = (params: {\n survey: Survey\n record: ArenaRecord\n nodes: NodesMap\n}): NodePointer[] => {\n const { survey, record, nodes } = params\n const uniquePointersByKey = new Map<string, NodePointer>()\n for (const node of Object.values(nodes)) {\n for (const dependencyType of validationDependencyTypes) {\n const nodePointers = Records.getDependentNodePointers(survey, node, dependencyType)(record)\n for (const pointer of nodePointers) {\n const nodeCtxUuid = pointer.nodeCtx.uuid\n const nodeDefUuid = pointer.nodeDef.uuid\n const key = `${nodeCtxUuid ?? ''}:${nodeDefUuid ?? ''}`\n if (!uniquePointersByKey.has(key)) {\n uniquePointersByKey.set(key, pointer)\n }\n }\n }\n }\n return Array.from(uniquePointersByKey.values())\n}\n\nconst _onRecordNodesCreateOrUpdate = async (\n params: NodesUpdateParams & { nodes: Dictionary<ArenaRecordNode> }\n): Promise<RecordUpdateResult> => {\n const { user, survey, dateModified = Dates.nowFormattedForStorage() } = params\n\n const updateResult = await RecordNodesUpdater.updateNodesDependents(params)\n const { nodes: updatedNodes, record: updatedRecord } = updateResult\n\n const dependentValidationNodePointers = getDependentValidationNodePointers({\n survey,\n record: updatedRecord,\n nodes: updatedNodes,\n })\n\n const dependentValidationNodes = NodePointers.getNodesMapFromNodePointers({\n record: updatedRecord,\n nodePointers: dependentValidationNodePointers,\n })\n\n const nodesToValidate = { ...updatedNodes, ...dependentValidationNodes }\n\n const validationUpdatedNodes = await RecordValidator.validateNodes({\n user,\n survey,\n record: updatedRecord,\n nodes: nodesToValidate,\n })\n\n const validation = Validations.mergeValidations(validationUpdatedNodes)(Validations.getValidation(updatedRecord))\n updatedRecord.validation = validation\n updatedRecord.dateModified = dateModified\n\n updateResult.validation = validation\n\n return updateResult\n}\n\nconst createDescendants = async (params: NodeCreateParams): Promise<RecordUpdateResult> => {\n const { record: updatedRecord, nodes: createdNodes } = await RecordNodesUpdater.addDescendants(params)\n\n return _onRecordNodesCreateOrUpdate({ ...params, record: updatedRecord, nodes: createdNodes })\n}\n\nconst createNodeAndDescendants = async (params: NodeCreateParams): Promise<RecordUpdateResult> => {\n const { record: updatedRecord, nodes: createdNodes } = await RecordNodesUpdater.addNodeAndDescendants(params)\n\n return _onRecordNodesCreateOrUpdate({ ...params, record: updatedRecord, nodes: createdNodes })\n}\n\nconst createRootEntity = async (\n params: NodesUpdateParams & {\n createMultipleEntities?: boolean\n }\n): Promise<RecordUpdateResult> => {\n const { survey, createMultipleEntities = true } = params\n\n return createNodeAndDescendants({ ...params, nodeDef: Surveys.getNodeDefRoot({ survey }), createMultipleEntities })\n}\n\nconst updateAttributeValue = async (\n params: NodesUpdateParams & {\n attributeUuid: string\n value: any\n }\n): Promise<RecordUpdateResult> => {\n const { attributeUuid, record, value, dateModified = Dates.nowFormattedForStorage(), sideEffect = false } = params\n const attribute = Records.getNodeByUuid(attributeUuid)(record)\n if (!attribute) throw new SystemError(SystemErrorKeys.record.nodeNotFound, { uuid: attributeUuid })\n\n let attributeUpdated = sideEffect ? attribute : { ...attribute }\n attributeUpdated.value = value\n attributeUpdated.dateModified = dateModified\n // reset defaultValueApplied flag\n attributeUpdated = Objects.dissocPath({ obj: attributeUpdated, path: ['meta', 'defaultValueApplied'], sideEffect })\n\n const nodesUpdated = { [attributeUuid]: attributeUpdated }\n\n const _record = Records.addNode(attributeUpdated, { sideEffect })(record)\n\n return _onRecordNodesCreateOrUpdate({ ...params, record: _record, nodes: nodesUpdated })\n}\n\nconst deleteNodes = async (\n params: NodesUpdateParams & {\n nodeUuids: string[]\n }\n): Promise<RecordUpdateResult> => {\n const { nodeUuids, record: _record, sideEffect = false } = params\n\n const updateResult = Records.removeNodes(nodeUuids, { sideEffect })(_record)\n const { record, nodesDeleted } = updateResult\n\n const result = await _onRecordNodesCreateOrUpdate({ ...params, record, nodes: nodesDeleted })\n result.nodesDeleted = nodesDeleted\n return result\n}\n\nconst deleteNode = async (params: NodesUpdateParams & { nodeUuid: string }): Promise<RecordUpdateResult> => {\n const { nodeUuid } = params\n return deleteNodes({ ...params, nodeUuids: [nodeUuid] })\n}\n\nexport const RecordUpdater = {\n /** Creates the descendants of a node and updates dependents (applicability, default values, calculations...) and validation */\n createDescendants,\n /** Creates a node and its descendants and updates dependents (applicability, default values, calculations...) and validation */\n createNodeAndDescendants,\n createRootEntity,\n updateAttributeValue,\n /** Deletes the node and its descendants and updates dependents (applicability, default values, calculations...) and validation */\n deleteNode,\n /** Deletes the nodes and their descendants and updates dependents (applicability, default values, calculations...) and validation */\n deleteNodes,\n}\n"]}
1
+ {"version":3,"file":"recordUpdater.js","sourceRoot":"","sources":["../../src/record/recordUpdater.ts"],"names":[],"mappings":"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","sourcesContent":["import { Dictionary } from '../common'\nimport { SystemError, SystemErrorKeys } from '../error'\nimport { ArenaRecordNode, NodePointer, NodesMap } from '../node'\nimport { NodeDef, NodeDefs } from '../nodeDef'\nimport { Survey, SurveyDependencyType, Surveys } from '../survey'\nimport { Dates, Objects } from '../utils'\nimport { Validations } from '../validation/validations'\nimport { NodePointers } from './nodePointers'\nimport { ArenaRecord } from './record'\nimport { RecordNodesUpdater, RecordUpdateResult } from './recordNodesUpdater'\nimport { NodeCreateParams, NodesUpdateParams } from './recordNodesUpdater/recordNodesCreator'\nimport { Records } from './records'\nimport { RecordValidator } from './recordValidator'\n\nconst validationDependencyTypes = [\n SurveyDependencyType.validations,\n SurveyDependencyType.required,\n SurveyDependencyType.minCount,\n SurveyDependencyType.maxCount,\n]\n\nconst getDependentValidationNodePointers = (params: {\n survey: Survey\n record: ArenaRecord\n nodes: NodesMap\n}): NodePointer[] => {\n const { survey, record, nodes } = params\n const uniquePointersByKey = new Map<string, NodePointer>()\n for (const node of Object.values(nodes)) {\n for (const dependencyType of validationDependencyTypes) {\n const nodePointers = Records.getDependentNodePointers(survey, node, dependencyType)(record)\n for (const pointer of nodePointers) {\n const nodeCtxUuid = pointer.nodeCtx.uuid\n const nodeDefUuid = pointer.nodeDef.uuid\n const key = `${nodeCtxUuid ?? ''}:${nodeDefUuid ?? ''}`\n if (!uniquePointersByKey.has(key)) {\n uniquePointersByKey.set(key, pointer)\n }\n }\n }\n }\n return Array.from(uniquePointersByKey.values())\n}\n\nconst _onRecordNodesCreateOrUpdate = async (\n params: NodesUpdateParams & { nodes: Dictionary<ArenaRecordNode>; additionalNodesToValidate?: NodesMap }\n): Promise<RecordUpdateResult> => {\n const {\n user,\n survey,\n categoryItemProvider,\n taxonProvider,\n areaOfInterestProvider,\n additionalNodesToValidate,\n dateModified = Dates.nowFormattedForStorage(),\n } = params\n\n const updateResult = await RecordNodesUpdater.updateNodesDependents(params)\n const { nodes: updatedNodes, record: updatedRecord } = updateResult\n\n const dependentValidationNodePointers = getDependentValidationNodePointers({\n survey,\n record: updatedRecord,\n nodes: updatedNodes,\n })\n\n const dependentValidationNodes = NodePointers.getNodesMapFromNodePointers({\n record: updatedRecord,\n nodePointers: dependentValidationNodePointers,\n })\n\n const nodesToValidate = { ...updatedNodes, ...dependentValidationNodes, ...additionalNodesToValidate }\n\n const validationUpdatedNodes = await RecordValidator.validateNodes({\n user,\n survey,\n categoryItemProvider,\n taxonProvider,\n areaOfInterestProvider,\n record: updatedRecord,\n nodes: nodesToValidate,\n })\n\n const validation = Validations.mergeValidations(validationUpdatedNodes)(Validations.getValidation(updatedRecord))\n updatedRecord.validation = validation\n updatedRecord.dateModified = dateModified\n\n updateResult.validation = validation\n\n return updateResult\n}\n\nconst createDescendants = async (params: NodeCreateParams): Promise<RecordUpdateResult> => {\n const { record: updatedRecord, nodes: createdNodes } = await RecordNodesUpdater.addDescendants(params)\n\n return _onRecordNodesCreateOrUpdate({ ...params, record: updatedRecord, nodes: createdNodes })\n}\n\nconst createNodeAndDescendants = async (params: NodeCreateParams): Promise<RecordUpdateResult> => {\n const { record: updatedRecord, nodes: createdNodes } = await RecordNodesUpdater.addNodeAndDescendants(params)\n\n return _onRecordNodesCreateOrUpdate({ ...params, record: updatedRecord, nodes: createdNodes })\n}\n\nconst createRootEntity = async (\n params: NodesUpdateParams & {\n createMultipleEntities?: boolean\n }\n): Promise<RecordUpdateResult> => {\n const { survey, createMultipleEntities = true } = params\n\n return createNodeAndDescendants({ ...params, nodeDef: Surveys.getNodeDefRoot({ survey }), createMultipleEntities })\n}\n\nconst updateAttributeValue = async (\n params: NodesUpdateParams & {\n attributeUuid: string\n value: any\n }\n): Promise<RecordUpdateResult> => {\n const { attributeUuid, record, value, dateModified = Dates.nowFormattedForStorage(), sideEffect = false } = params\n const attribute = Records.getNodeByUuid(attributeUuid)(record)\n if (!attribute) throw new SystemError(SystemErrorKeys.record.nodeNotFound, { uuid: attributeUuid })\n\n let attributeUpdated = sideEffect ? attribute : { ...attribute }\n attributeUpdated.value = value\n attributeUpdated.dateModified = dateModified\n // reset defaultValueApplied flag\n attributeUpdated = Objects.dissocPath({ obj: attributeUpdated, path: ['meta', 'defaultValueApplied'], sideEffect })\n\n const nodesUpdated = { [attributeUuid]: attributeUpdated }\n\n const _record = Records.addNode(attributeUpdated, { sideEffect })(record)\n\n return _onRecordNodesCreateOrUpdate({ ...params, record: _record, nodes: nodesUpdated })\n}\n\n/**\n * Returns the key and unique attributes of the siblings of the specified entities:\n * they could have been duplicates of the ones being deleted, so they must be validated again.\n */\nconst getEntityKeyOrUniqueAttributes = (params: {\n survey: Survey\n record: ArenaRecord\n entity: ArenaRecordNode\n uniqueAttributeDefs: NodeDef<any>[]\n}): ArenaRecordNode[] => {\n const { survey, record, entity, uniqueAttributeDefs } = params\n return [\n ...Records.getEntityKeyNodes(survey, entity)(record),\n ...uniqueAttributeDefs.flatMap((uniqueDef) => Records.getChildren(entity, uniqueDef.uuid)(record)),\n ].filter(Boolean)\n}\n\nconst getSiblingKeyOrUniqueAttributes = (params: {\n survey: Survey\n record: ArenaRecord\n nodeUuids: string[]\n}): NodesMap => {\n const { survey, record, nodeUuids } = params\n const nodeUuidsSet = new Set(nodeUuids)\n const result: NodesMap = {}\n for (const nodeUuid of nodeUuids) {\n const node = Records.getNodeByUuid(nodeUuid)(record)\n const parentNode = node ? Records.getParent(node)(record) : undefined\n const nodeDef = node ? Surveys.getNodeDefByUuid({ survey, uuid: node.nodeDefUuid }) : undefined\n if (!parentNode || !nodeDef || !NodeDefs.isEntity(nodeDef)) continue\n\n const uniqueAttributeDefs = Surveys.getNodeDefChildren({ survey, nodeDef }).filter(\n (childDef) => NodeDefs.getValidations(childDef)?.unique\n )\n const siblings = Records.getChildren(parentNode, nodeDef.uuid)(record).filter((s) => !nodeUuidsSet.has(s.uuid))\n for (const sibling of siblings) {\n for (const attribute of getEntityKeyOrUniqueAttributes({\n survey,\n record,\n entity: sibling,\n uniqueAttributeDefs,\n })) {\n result[attribute.uuid] = attribute\n }\n }\n }\n return result\n}\n\nconst deleteNodes = async (\n params: NodesUpdateParams & {\n nodeUuids: string[]\n }\n): Promise<RecordUpdateResult> => {\n const { survey, nodeUuids, record: _record, sideEffect = false } = params\n\n // found before removing the nodes: the deleted nodes are no longer reachable from their siblings afterwards\n const siblingKeyOrUniqueAttributes = getSiblingKeyOrUniqueAttributes({ survey, record: _record, nodeUuids })\n\n const updateResult = Records.removeNodes(nodeUuids, { sideEffect })(_record)\n const { record, nodesDeleted } = updateResult\n\n const result = await _onRecordNodesCreateOrUpdate({\n ...params,\n record,\n nodes: nodesDeleted,\n additionalNodesToValidate: siblingKeyOrUniqueAttributes,\n })\n result.nodesDeleted = nodesDeleted\n return result\n}\n\nconst deleteNode = async (params: NodesUpdateParams & { nodeUuid: string }): Promise<RecordUpdateResult> => {\n const { nodeUuid } = params\n return deleteNodes({ ...params, nodeUuids: [nodeUuid] })\n}\n\nexport const RecordUpdater = {\n /** Creates the descendants of a node and updates dependents (applicability, default values, calculations...) and validation */\n createDescendants,\n /** Creates a node and its descendants and updates dependents (applicability, default values, calculations...) and validation */\n createNodeAndDescendants,\n createRootEntity,\n updateAttributeValue,\n /** Deletes the node and its descendants and updates dependents (applicability, default values, calculations...) and validation */\n deleteNode,\n /** Deletes the nodes and their descendants and updates dependents (applicability, default values, calculations...) and validation */\n deleteNodes,\n}\n"]}
@@ -44,7 +44,8 @@ const _isEntityDuplicate = (params) => {
44
44
  return false;
45
45
  }
46
46
  const keyValuesByDefUuid = _getKeyValuesByDefUuid({ survey, record, entity, keyDefs, cache });
47
- return (!utils_1.Objects.isEmpty(keyValuesByDefUuid) &&
47
+ // entities with all key values empty are never duplicates (they are reported as missing values)
48
+ return (!Object.values(keyValuesByDefUuid).every(utils_1.Objects.isEmpty) &&
48
49
  siblingEntities.some((sibilingEntity) => {
49
50
  const siblingKeyValuesByDefUuid = _getKeyValuesByDefUuid({
50
51
  survey,
@@ -1 +1 @@
1
- {"version":3,"file":"attributeKeyValidator.js","sourceRoot":"","sources":["../../../src/record/recordValidator/attributeKeyValidator.ts"],"names":[],"mappings":"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","sourcesContent":["import { ArenaRecord } from '../record'\nimport { Records } from '../records'\nimport { NodeDef, NodeDefs, NodeDefProps, NodeDefType } from '../../nodeDef'\nimport { ArenaRecordNode, Nodes, NodeValues } from '../../node'\nimport { ValidationResult, ValidationResultFactory, ValidationSeverity } from '../../validation'\nimport { Survey, Surveys } from '../../survey'\n\nimport { Dictionary } from '../../common'\nimport { Objects } from '../../utils'\n\n/**\n * Cache of entity key values (and key defs by entity def) used during a single validation run (the record doesn't change):\n * without it, validating the keys of S sibling entities would compute the key values of every sibling S times.\n */\nexport type EntityKeysCache = {\n keyDefsByEntityDefUuid: Map<string, NodeDef<NodeDefType, NodeDefProps>[]>\n keyValuesByEntityUuid: Map<string, Dictionary<any>>\n}\n\nexport const createEntityKeysCache = (): EntityKeysCache => ({\n keyDefsByEntityDefUuid: new Map(),\n keyValuesByEntityUuid: new Map(),\n})\n\nconst _getKeyDefs = (params: {\n survey: Survey\n entityDef: NodeDef<NodeDefType, NodeDefProps>\n cache?: EntityKeysCache\n}): NodeDef<NodeDefType, NodeDefProps>[] => {\n const { survey, entityDef, cache } = params\n let keyDefs = cache?.keyDefsByEntityDefUuid.get(entityDef.uuid)\n if (!keyDefs) {\n keyDefs = Surveys.getNodeDefKeys({ survey, nodeDef: entityDef })\n cache?.keyDefsByEntityDefUuid.set(entityDef.uuid, keyDefs)\n }\n return keyDefs\n}\n\nconst _getKeyValuesByDefUuid = (params: {\n survey: Survey\n record: ArenaRecord\n entity: ArenaRecordNode\n keyDefs: NodeDef<NodeDefType, NodeDefProps>[]\n cache?: EntityKeysCache\n}): Dictionary<any> => {\n const { survey, record, entity, keyDefs, cache } = params\n let keyValues = cache?.keyValuesByEntityUuid.get(entity.uuid)\n if (!keyValues) {\n keyValues = Records.getEntityKeyValuesByDefUuid(survey, entity, undefined, keyDefs)(record)\n cache?.keyValuesByEntityUuid.set(entity.uuid, keyValues)\n }\n return keyValues\n}\n\nconst _isEntityDuplicate = (params: {\n survey: Survey\n record: ArenaRecord\n entity: ArenaRecordNode\n entityKeysCache?: EntityKeysCache\n}): boolean => {\n const { survey, entity, record, entityKeysCache: cache } = params\n // 1. get sibling entities\n const nodeParent = Records.getParent(entity)(record)\n if (!nodeParent) return false\n\n const siblingEntities = Records.getChildren(\n nodeParent,\n entity.nodeDefUuid\n )(record).filter((node) => !Nodes.areEqual(entity, node))\n\n // 2. get key values\n const entityDef = Surveys.getNodeDefByUuid({ survey, uuid: entity.nodeDefUuid })\n const keyDefs = _getKeyDefs({ survey, entityDef, cache })\n\n if (Objects.isEmpty(siblingEntities) || Objects.isEmpty(keyDefs)) {\n return false\n }\n const keyValuesByDefUuid = _getKeyValuesByDefUuid({ survey, record, entity, keyDefs, cache })\n return (\n !Objects.isEmpty(keyValuesByDefUuid) &&\n siblingEntities.some((sibilingEntity) => {\n const siblingKeyValuesByDefUuid = _getKeyValuesByDefUuid({\n survey,\n record,\n entity: sibilingEntity,\n keyDefs,\n cache,\n })\n return keyDefs.every((keyDef) => {\n const keyValue = keyValuesByDefUuid[keyDef.uuid]\n const siblingKeyValue = siblingKeyValuesByDefUuid[keyDef.uuid]\n return NodeValues.isValueEqual({\n survey,\n nodeDef: keyDef,\n value: keyValue,\n valueSearch: siblingKeyValue,\n })\n })\n })\n )\n}\n\nconst isNodeDefToBeValidated = (params: { survey: Survey; nodeDef: NodeDef<NodeDefType, NodeDefProps> }): boolean => {\n const { survey, nodeDef } = params\n if (!NodeDefs.isKey(nodeDef)) return false\n\n const nodeDefParent = Surveys.getNodeDefParent({ survey, nodeDef })\n // root entity key attributes will be validated by another validator\n return !!nodeDefParent && !NodeDefs.isRoot(nodeDefParent)\n}\n\nconst validateAttributeKey =\n (params: {\n survey: Survey\n record: ArenaRecord\n nodeDef: NodeDef<NodeDefType, NodeDefProps>\n entityKeysCache?: EntityKeysCache\n }) =>\n (_propName: string, node: ArenaRecordNode): Promise<ValidationResult> => {\n const { survey, nodeDef, record, entityKeysCache } = params\n\n if (isNodeDefToBeValidated({ survey, nodeDef })) {\n const entity = Records.getParent(node)(record)\n if (entity && _isEntityDuplicate({ survey, record, entity, entityKeysCache })) {\n return Promise.resolve(\n ValidationResultFactory.createInstance({\n valid: false,\n severity: ValidationSeverity.error,\n key: 'record.entity.keyDuplicate',\n })\n )\n }\n }\n\n return Promise.resolve(ValidationResultFactory.createInstance())\n }\n\nexport const AttributeKeyValidator = {\n validateAttributeKey,\n}\n"]}
1
+ {"version":3,"file":"attributeKeyValidator.js","sourceRoot":"","sources":["../../../src/record/recordValidator/attributeKeyValidator.ts"],"names":[],"mappings":"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","sourcesContent":["import { ArenaRecord } from '../record'\nimport { Records } from '../records'\nimport { NodeDef, NodeDefs, NodeDefProps, NodeDefType } from '../../nodeDef'\nimport { ArenaRecordNode, Nodes, NodeValues } from '../../node'\nimport { ValidationResult, ValidationResultFactory, ValidationSeverity } from '../../validation'\nimport { Survey, Surveys } from '../../survey'\n\nimport { Dictionary } from '../../common'\nimport { Objects } from '../../utils'\n\n/**\n * Cache of entity key values (and key defs by entity def) used during a single validation run (the record doesn't change):\n * without it, validating the keys of S sibling entities would compute the key values of every sibling S times.\n */\nexport type EntityKeysCache = {\n keyDefsByEntityDefUuid: Map<string, NodeDef<NodeDefType, NodeDefProps>[]>\n keyValuesByEntityUuid: Map<string, Dictionary<any>>\n}\n\nexport const createEntityKeysCache = (): EntityKeysCache => ({\n keyDefsByEntityDefUuid: new Map(),\n keyValuesByEntityUuid: new Map(),\n})\n\nconst _getKeyDefs = (params: {\n survey: Survey\n entityDef: NodeDef<NodeDefType, NodeDefProps>\n cache?: EntityKeysCache\n}): NodeDef<NodeDefType, NodeDefProps>[] => {\n const { survey, entityDef, cache } = params\n let keyDefs = cache?.keyDefsByEntityDefUuid.get(entityDef.uuid)\n if (!keyDefs) {\n keyDefs = Surveys.getNodeDefKeys({ survey, nodeDef: entityDef })\n cache?.keyDefsByEntityDefUuid.set(entityDef.uuid, keyDefs)\n }\n return keyDefs\n}\n\nconst _getKeyValuesByDefUuid = (params: {\n survey: Survey\n record: ArenaRecord\n entity: ArenaRecordNode\n keyDefs: NodeDef<NodeDefType, NodeDefProps>[]\n cache?: EntityKeysCache\n}): Dictionary<any> => {\n const { survey, record, entity, keyDefs, cache } = params\n let keyValues = cache?.keyValuesByEntityUuid.get(entity.uuid)\n if (!keyValues) {\n keyValues = Records.getEntityKeyValuesByDefUuid(survey, entity, undefined, keyDefs)(record)\n cache?.keyValuesByEntityUuid.set(entity.uuid, keyValues)\n }\n return keyValues\n}\n\nconst _isEntityDuplicate = (params: {\n survey: Survey\n record: ArenaRecord\n entity: ArenaRecordNode\n entityKeysCache?: EntityKeysCache\n}): boolean => {\n const { survey, entity, record, entityKeysCache: cache } = params\n // 1. get sibling entities\n const nodeParent = Records.getParent(entity)(record)\n if (!nodeParent) return false\n\n const siblingEntities = Records.getChildren(\n nodeParent,\n entity.nodeDefUuid\n )(record).filter((node) => !Nodes.areEqual(entity, node))\n\n // 2. get key values\n const entityDef = Surveys.getNodeDefByUuid({ survey, uuid: entity.nodeDefUuid })\n const keyDefs = _getKeyDefs({ survey, entityDef, cache })\n\n if (Objects.isEmpty(siblingEntities) || Objects.isEmpty(keyDefs)) {\n return false\n }\n const keyValuesByDefUuid = _getKeyValuesByDefUuid({ survey, record, entity, keyDefs, cache })\n // entities with all key values empty are never duplicates (they are reported as missing values)\n return (\n !Object.values(keyValuesByDefUuid).every(Objects.isEmpty) &&\n siblingEntities.some((sibilingEntity) => {\n const siblingKeyValuesByDefUuid = _getKeyValuesByDefUuid({\n survey,\n record,\n entity: sibilingEntity,\n keyDefs,\n cache,\n })\n return keyDefs.every((keyDef) => {\n const keyValue = keyValuesByDefUuid[keyDef.uuid]\n const siblingKeyValue = siblingKeyValuesByDefUuid[keyDef.uuid]\n return NodeValues.isValueEqual({\n survey,\n nodeDef: keyDef,\n value: keyValue,\n valueSearch: siblingKeyValue,\n })\n })\n })\n )\n}\n\nconst isNodeDefToBeValidated = (params: { survey: Survey; nodeDef: NodeDef<NodeDefType, NodeDefProps> }): boolean => {\n const { survey, nodeDef } = params\n if (!NodeDefs.isKey(nodeDef)) return false\n\n const nodeDefParent = Surveys.getNodeDefParent({ survey, nodeDef })\n // root entity key attributes will be validated by another validator\n return !!nodeDefParent && !NodeDefs.isRoot(nodeDefParent)\n}\n\nconst validateAttributeKey =\n (params: {\n survey: Survey\n record: ArenaRecord\n nodeDef: NodeDef<NodeDefType, NodeDefProps>\n entityKeysCache?: EntityKeysCache\n }) =>\n (_propName: string, node: ArenaRecordNode): Promise<ValidationResult> => {\n const { survey, nodeDef, record, entityKeysCache } = params\n\n if (isNodeDefToBeValidated({ survey, nodeDef })) {\n const entity = Records.getParent(node)(record)\n if (entity && _isEntityDuplicate({ survey, record, entity, entityKeysCache })) {\n return Promise.resolve(\n ValidationResultFactory.createInstance({\n valid: false,\n severity: ValidationSeverity.error,\n key: 'record.entity.keyDuplicate',\n })\n )\n }\n }\n\n return Promise.resolve(ValidationResultFactory.createInstance())\n }\n\nexport const AttributeKeyValidator = {\n validateAttributeKey,\n}\n"]}
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "name": "OpenForis",
5
5
  "email": "openforis.arena@gmail.com"
6
6
  },
7
- "version": "2.16.4",
7
+ "version": "2.16.5",
8
8
  "description": "Arena core module",
9
9
  "repository": {
10
10
  "type": "git",