cht-conf 5.2.1 → 5.4.0

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/CHANGELOG.md CHANGED
@@ -1,3 +1,17 @@
1
+ # [5.4.0](https://github.com/medic/cht-conf/compare/v5.3.0...v5.4.0) (2025-09-11)
2
+
3
+
4
+ ### Features
5
+
6
+ * **#561:** add form validation for `instance::db-doc-ref` ([#738](https://github.com/medic/cht-conf/issues/738)) ([941a6f5](https://github.com/medic/cht-conf/commit/941a6f5b7504d6ff8c1682845d6e38efd6ea017b)), closes [#561](https://github.com/medic/cht-conf/issues/561)
7
+
8
+ # [5.3.0](https://github.com/medic/cht-conf/compare/v5.2.1...v5.3.0) (2025-09-04)
9
+
10
+
11
+ ### Features
12
+
13
+ * **#512:** add form validation for instance::db-doc ([#737](https://github.com/medic/cht-conf/issues/737)) ([0db687e](https://github.com/medic/cht-conf/commit/0db687eb8f2c5dbb6a2d2e387857af50af81a917)), closes [#512](https://github.com/medic/cht-conf/issues/512)
14
+
1
15
  ## [5.2.1](https://github.com/medic/cht-conf/compare/v5.2.0...v5.2.1) (2025-08-27)
2
16
 
3
17
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cht-conf",
3
- "version": "5.2.1",
3
+ "version": "5.4.0",
4
4
  "description": "Configure CHT deployments",
5
5
  "main": "./src/lib/main.js",
6
6
  "engines": {
@@ -92,6 +92,6 @@
92
92
  "pouchdb-adapter-memory": "^9.0.0",
93
93
  "rewire": "^8.0.0",
94
94
  "semantic-release": "^24.2.4",
95
- "sinon": "^19.0.2"
95
+ "sinon": "^21.0.0"
96
96
  }
97
97
  }
@@ -9,6 +9,14 @@ const getNode = (currentNode, path) =>
9
9
  const getNodes = (currentNode, path) =>
10
10
  xpath.parse(path).select({ node: currentNode, allowAnyNamespaceForNoPrefix: true });
11
11
 
12
+ const getFullNodePath = (childNode) => {
13
+ if (childNode.nodeType !== 1) {
14
+ return '';
15
+ }
16
+ const parentPath = getFullNodePath(childNode.parentNode);
17
+ return parentPath + '/' + childNode.nodeName;
18
+ };
19
+
12
20
  module.exports = {
13
21
  /**
14
22
  * Get the full path of the form, or null if the path doesn't exist.
@@ -68,6 +76,15 @@ module.exports = {
68
76
  */
69
77
  getPrimaryInstanceNode: xmlDoc => getNode(xmlDoc, `${XPATH_MODEL}/instance`),
70
78
 
79
+ /**
80
+ * Returns the path to the given child node relative to the primary instance node.
81
+ * This is useful for identifying the node in error/warning messages.
82
+ * @param childNode the child node
83
+ * @returns {string}
84
+ */
85
+ getPrimaryInstanceNodeChildPath: (childNode) => getFullNodePath(childNode)
86
+ .replace(`${XPATH_MODEL}/instance`, ''),
87
+
71
88
  /**
72
89
  * Check whether the XForm has the <instanceID/> tag.
73
90
  * @param {string} xmlDoc the XML document
@@ -0,0 +1,26 @@
1
+ const { getPrimaryInstanceNode, getNodes, getPrimaryInstanceNodeChildPath } = require('../../forms-utils');
2
+
3
+ module.exports = {
4
+ requiresInstance: false,
5
+ execute: async ({ xmlDoc, xformPath }) => {
6
+ const errors = [];
7
+ const primaryInstance = getPrimaryInstanceNode(xmlDoc);
8
+ const dbDocNodes = getNodes(xmlDoc, '//*[@db-doc="true"]', primaryInstance);
9
+ dbDocNodes.forEach(node => {
10
+ const childNodes = node.childNodes.filter(n => n.nodeType === 1);
11
+ if (!childNodes.length) {
12
+ const nodeName = getPrimaryInstanceNodeChildPath(node);
13
+ errors.push(` - ${nodeName}: the db-doc attribute must only be set on groups.`);
14
+ return;
15
+ }
16
+ const hasTypeField = childNodes.some(child => child.nodeName === 'type');
17
+ if (!hasTypeField) {
18
+ const nodeName = getPrimaryInstanceNodeChildPath(node);
19
+ errors.push(` - ${nodeName}: groups configured with the db-doc attribute must contain a valid type field.`);
20
+ }
21
+ });
22
+
23
+ const header = `Form at ${xformPath} contains invalid db-doc configuration:`;
24
+ return { errors: errors.length ? [header, ...errors]: errors };
25
+ }
26
+ };
@@ -0,0 +1,28 @@
1
+ const { getPrimaryInstanceNode, getNodes, getNode, getPrimaryInstanceNodeChildPath } = require('../../forms-utils');
2
+
3
+ module.exports = {
4
+ requiresInstance: false,
5
+ execute: ({ xmlDoc, xformPath }) => {
6
+ const errors = [];
7
+ const primaryInstance = getPrimaryInstanceNode(xmlDoc);
8
+ const formRootNode = Array
9
+ .from(primaryInstance.childNodes)
10
+ .find(n => n.nodeType === 1);
11
+ const refNodes = getNodes(xmlDoc, '//*[@db-doc-ref]', primaryInstance);
12
+ refNodes.forEach(node => {
13
+ const refPath = node
14
+ .getAttribute('db-doc-ref')
15
+ .trim()
16
+ .replace(/^\//, '');
17
+ const targetNode = getNode(primaryInstance, refPath);
18
+ if (!targetNode || (targetNode !== formRootNode && targetNode.getAttribute('db-doc') !== 'true')) {
19
+ const nodeName = getPrimaryInstanceNodeChildPath(node);
20
+ errors.push(` - ${nodeName}: the db-doc-ref value [${refPath}] does not reference a valid doc node.`);
21
+ }
22
+ });
23
+
24
+ const header = `Form at ${xformPath} contains invalid db-doc-ref configuration. A db-doc-ref value should point `
25
+ + `to either the root node of the form or to a group that is adding a new doc (with db-doc set to "true"):`;
26
+ return { errors: errors.length ? [header, ...errors]: errors };
27
+ },
28
+ };