Skip to content

Commit

Permalink
Merge pull request #287 from getodk/feature/engine/instance-root-stat…
Browse files Browse the repository at this point in the history
…ic-attrs

Include primary instance root attributes, namespace declarations in submission XML
  • Loading branch information
eyelidlessness authored Jan 28, 2025
2 parents f1fecfb + 5d309b7 commit 11a86ef
Show file tree
Hide file tree
Showing 11 changed files with 398 additions and 55 deletions.
7 changes: 7 additions & 0 deletions .changeset/healthy-jobs-look.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@getodk/xforms-engine': patch
'@getodk/web-forms': patch
'@getodk/scenario': patch
---

Fix: include namespace declarations in submission XML
7 changes: 7 additions & 0 deletions .changeset/light-mails-appear.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@getodk/xforms-engine': patch
'@getodk/web-forms': patch
'@getodk/scenario': patch
---

Fix: include primary instance root attributes in submission XML
74 changes: 53 additions & 21 deletions packages/scenario/src/serialization/ComparableXMLSerialization.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { XFORMS_NAMESPACE_URI } from '@getodk/common/constants/xmlns.ts';
import {
XFORMS_NAMESPACE_URI,
XMLNS_NAMESPACE_URI,
XMLNS_PREFIX,
} from '@getodk/common/constants/xmlns.ts';
import { InspectableComparisonError } from '@getodk/common/test/assertions/helpers.ts';
import type { SimpleAssertionResult } from '@getodk/common/test/assertions/vitest/shared-extension-types.ts';
import { ComparableAssertableValue } from '../comparable/ComparableAssertableValue.ts';
Expand All @@ -8,38 +12,53 @@ class ComparableXMLQualifiedName {

constructor(
readonly namespaceURI: string | null,
readonly nodeName: string,
readonly localName: string
) {
this.sortKey = JSON.stringify({ namespaceURI, localName });
let namespaceDeclarationType: string;

if (namespaceURI === XMLNS_NAMESPACE_URI) {
if (nodeName === XMLNS_PREFIX) {
namespaceDeclarationType = 'default';
} else {
namespaceDeclarationType = 'non-default';
}
} else {
namespaceDeclarationType = 'none';
}

this.sortKey = JSON.stringify({
namespaceDeclarationType,
namespaceURI,
localName,
});
}

/**
* @todo prefix re-serialization
*/
toString(): string {
const { namespaceURI } = this;
const { namespaceURI, nodeName } = this;

if (namespaceURI == null || namespaceURI === XFORMS_NAMESPACE_URI) {
return this.localName;
}

return this.sortKey;
return nodeName;
}
}

class ComparableXMLAttribute {
static from(attr: Attr): ComparableXMLAttribute {
return new this(attr.namespaceURI, attr.localName, attr.value);
return new this(attr.namespaceURI, attr.nodeName, attr.localName, attr.value);
}

readonly qualifiedName: ComparableXMLQualifiedName;

private constructor(
namespaceURI: string | null,
nodeName: string,
localName: string,
readonly value: string
) {
this.qualifiedName = new ComparableXMLQualifiedName(namespaceURI, localName);
this.qualifiedName = new ComparableXMLQualifiedName(namespaceURI, nodeName, localName);
}

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

return attributes.sort(({ qualifiedName: a }, { qualifiedName: b }) => {
if (a > b) {
return 1;
return attributes.sort(
(
// prettier-ignore
{ qualifiedName: { sortKey: a } },
{ qualifiedName: { sortKey: b } }
) => {
if (a > b) {
return 1;
}

if (b > a) {
return -1;
}

return 0;
}

if (b > a) {
return -1;
}

return 0;
});
);
};

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

return new this(element.namespaceURI, element.localName, attributes, children);
return new this(
element.namespaceURI,
element.nodeName,
element.localName,
attributes,
children
);
}

readonly qualifiedName: ComparableXMLQualifiedName;

private constructor(
namespaceURI: string | null,
nodeName: string,
localName: string,
readonly attributes: readonly ComparableXMLAttribute[],
readonly children: readonly ComparableXMLElementChild[]
) {
this.qualifiedName = new ComparableXMLQualifiedName(namespaceURI, localName);
this.qualifiedName = new ComparableXMLQualifiedName(namespaceURI, nodeName, localName);
}

toString(): string {
Expand Down
Loading

0 comments on commit 11a86ef

Please sign in to comment.