Skip to content

Commit 11a86ef

Browse files
Merge pull request #287 from getodk/feature/engine/instance-root-static-attrs
Include primary instance root attributes, namespace declarations in submission XML
2 parents f1fecfb + 5d309b7 commit 11a86ef

File tree

11 files changed

+398
-55
lines changed

11 files changed

+398
-55
lines changed

.changeset/healthy-jobs-look.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'@getodk/xforms-engine': patch
3+
'@getodk/web-forms': patch
4+
'@getodk/scenario': patch
5+
---
6+
7+
Fix: include namespace declarations in submission XML

.changeset/light-mails-appear.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'@getodk/xforms-engine': patch
3+
'@getodk/web-forms': patch
4+
'@getodk/scenario': patch
5+
---
6+
7+
Fix: include primary instance root attributes in submission XML

packages/scenario/src/serialization/ComparableXMLSerialization.ts

Lines changed: 53 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
import { XFORMS_NAMESPACE_URI } from '@getodk/common/constants/xmlns.ts';
1+
import {
2+
XFORMS_NAMESPACE_URI,
3+
XMLNS_NAMESPACE_URI,
4+
XMLNS_PREFIX,
5+
} from '@getodk/common/constants/xmlns.ts';
26
import { InspectableComparisonError } from '@getodk/common/test/assertions/helpers.ts';
37
import type { SimpleAssertionResult } from '@getodk/common/test/assertions/vitest/shared-extension-types.ts';
48
import { ComparableAssertableValue } from '../comparable/ComparableAssertableValue.ts';
@@ -8,38 +12,53 @@ class ComparableXMLQualifiedName {
812

913
constructor(
1014
readonly namespaceURI: string | null,
15+
readonly nodeName: string,
1116
readonly localName: string
1217
) {
13-
this.sortKey = JSON.stringify({ namespaceURI, localName });
18+
let namespaceDeclarationType: string;
19+
20+
if (namespaceURI === XMLNS_NAMESPACE_URI) {
21+
if (nodeName === XMLNS_PREFIX) {
22+
namespaceDeclarationType = 'default';
23+
} else {
24+
namespaceDeclarationType = 'non-default';
25+
}
26+
} else {
27+
namespaceDeclarationType = 'none';
28+
}
29+
30+
this.sortKey = JSON.stringify({
31+
namespaceDeclarationType,
32+
namespaceURI,
33+
localName,
34+
});
1435
}
1536

16-
/**
17-
* @todo prefix re-serialization
18-
*/
1937
toString(): string {
20-
const { namespaceURI } = this;
38+
const { namespaceURI, nodeName } = this;
2139

2240
if (namespaceURI == null || namespaceURI === XFORMS_NAMESPACE_URI) {
2341
return this.localName;
2442
}
2543

26-
return this.sortKey;
44+
return nodeName;
2745
}
2846
}
2947

3048
class ComparableXMLAttribute {
3149
static from(attr: Attr): ComparableXMLAttribute {
32-
return new this(attr.namespaceURI, attr.localName, attr.value);
50+
return new this(attr.namespaceURI, attr.nodeName, attr.localName, attr.value);
3351
}
3452

3553
readonly qualifiedName: ComparableXMLQualifiedName;
3654

3755
private constructor(
3856
namespaceURI: string | null,
57+
nodeName: string,
3958
localName: string,
4059
readonly value: string
4160
) {
42-
this.qualifiedName = new ComparableXMLQualifiedName(namespaceURI, localName);
61+
this.qualifiedName = new ComparableXMLQualifiedName(namespaceURI, nodeName, localName);
4362
}
4463

4564
/**
@@ -59,17 +78,23 @@ const comparableXMLElementAttributes = (element: Element): readonly ComparableXM
5978
return ComparableXMLAttribute.from(attr);
6079
});
6180

62-
return attributes.sort(({ qualifiedName: a }, { qualifiedName: b }) => {
63-
if (a > b) {
64-
return 1;
81+
return attributes.sort(
82+
(
83+
// prettier-ignore
84+
{ qualifiedName: { sortKey: a } },
85+
{ qualifiedName: { sortKey: b } }
86+
) => {
87+
if (a > b) {
88+
return 1;
89+
}
90+
91+
if (b > a) {
92+
return -1;
93+
}
94+
95+
return 0;
6596
}
66-
67-
if (b > a) {
68-
return -1;
69-
}
70-
71-
return 0;
72-
});
97+
);
7398
};
7499

75100
const isElement = (node: ChildNode): node is Element => {
@@ -118,18 +143,25 @@ class ComparableXMLElement {
118143
const attributes = comparableXMLElementAttributes(element);
119144
const children = comparableXMLElementChildren(element);
120145

121-
return new this(element.namespaceURI, element.localName, attributes, children);
146+
return new this(
147+
element.namespaceURI,
148+
element.nodeName,
149+
element.localName,
150+
attributes,
151+
children
152+
);
122153
}
123154

124155
readonly qualifiedName: ComparableXMLQualifiedName;
125156

126157
private constructor(
127158
namespaceURI: string | null,
159+
nodeName: string,
128160
localName: string,
129161
readonly attributes: readonly ComparableXMLAttribute[],
130162
readonly children: readonly ComparableXMLElementChild[]
131163
) {
132-
this.qualifiedName = new ComparableXMLQualifiedName(namespaceURI, localName);
164+
this.qualifiedName = new ComparableXMLQualifiedName(namespaceURI, nodeName, localName);
133165
}
134166

135167
toString(): string {

0 commit comments

Comments
 (0)