Skip to content

feat(#10904): route file attachments to correct sub-docs in report fo…#11115

Draft
jkuester wants to merge 31 commits into
medic:masterfrom
jkuester:10903-sub-contact-routing
Draft

feat(#10904): route file attachments to correct sub-docs in report fo…#11115
jkuester wants to merge 31 commits into
medic:masterfrom
jkuester:10903-sub-contact-routing

Conversation

@jkuester
Copy link
Copy Markdown
Contributor

…rms (#10922)

Co-authored-by: Bernard K. kagondubernard81@gmail.com<!--
Please use semantic PR titles that respect this format:

(#):

Quick example:

feat(#1234): add hat wobble
^--^(#^--^): ^------------^
| | |
| | + - > subject
| |
| + -------- > issue number
|

  • -------------- > type: build|feat|fix|perf|refactor|test|chore

https://docs.communityhealthtoolkit.org/contribute/code/workflow/#commit-message-format
-->

Description

Code review checklist

  • UI/UX backwards compatible: Test it works for the new design (enabled by default). And test it works in the old design, enable can_view_old_navigation permission to see the old design. Test it has appropriate design for RTL languages.
  • Readable: Concise, well named, follows the style guide
  • Documented: Configuration and user documentation on cht-docs
  • Tested: Unit and/or e2e where appropriate
  • Internationalised: All user facing text
  • Backwards compatible: Works with existing data and configuration or includes a migration. Any breaking changes documented in the release notes.
  • AI disclosure: Please disclose use of AI per the guidelines.

License

The software is provided under AGPL-3.0. Contributions to this project are accepted under the same license.

YASHSHARMAOFFICIALLY and others added 30 commits May 21, 2026 10:36
…rt forms (medic#10922)

Co-authored-by: Bernard K. <kagondubernard81@gmail.com>
…rt forms

When a report form contains [db-doc="true"] sub-documents with binary/file
fields, attachments are now routed to the owning sub-document instead of
always attaching to the main report doc.

- Add resolveOwnerDoc() to walk up XML tree to nearest db-doc ancestor
- Route FileManager file uploads to correct owner doc
- Route inline binary blobs to correct owner doc
- Fall back to main report doc when element is not inside a sub-doc
processAllAttachments now walks the parsed XML to determine which
prepared doc owns each [type=binary] element (main / sibling / repeat
child) and attaches files accordingly, instead of dumping every upload
on preparedDocs[0]. Field-value sanitization and orphan cleanup also
run per-doc.

Adds two private helpers:
- resolveContactOwnerDoc: DOM-walk from any element to its section root,
  then to the owning prepared doc (with mainDoc fallback).
- findContactOwnerForFilename: locates the [type=binary] node whose text
  matches a FileManager filename and resolves its owner.

No public API changes; no new service dependencies.
saveContact now uses validateAttachments(preparedDocs.preparedDocs)
New 'attachment routing to sub-contacts' describe block exercising:
- file uploaded inside a sibling section -> sibling doc
- file uploaded inside a repeat child -> i-th repeat doc
- mixed uploads across main / sibling / repeat -> each owner
- FileManager file with no matching binary node -> main doc fallback
- inline binary (draw widget) inside sibling -> sibling doc
- per-doc field-value sanitization (sibling field rewritten, main untouched)
- main-doc orphan cleanup on edit path with per-doc loop
- main-doc & sub-doc oversize attachment fails saveContact
- normal-sized attachment passes validation
Enketo's setVal rewrites uploaded binary nodes to type="file" the
moment a value is set.
add sub-contact attachment routing form fixtures & testes
Co-authored-by: Joshua Kuestersteffen <jkuester@kuester7.com>
…t attachments

Inline-binary form fields (`<foo type="binary">…base64…</foo>` populated by
form `<instance>` defaults, `calculate` expressions, or external `instanceData` injection)
previously had attachments stored under `user-file/<xpath>` while the saved field
was blanked, requiring downstream binary fields attachment name reconstruction from
`label` at render time. That reconstruction was broken for sub-docs,
where the parent-form prefix in the attachment name is not derivable from the sub-doc alone.

With this change, a media field's value is the name of its attachment, regardless of how
the attachment was produced. Inline-binary fields keep their stable,
xml based name (`user-file/<xpath>`), which is also written back into the field value so
the renderer can resolve the image by value alone, consistent with file-widget uploads.

- `nodesToJs` no longer sets `[type=binary]` to ''.
- `bindJsonToXml` skips binding into `[type=binary]` for empty & attachmentment name
reference. inline base64 still binds.
- `xmlToDocs` attaches `[type=binary]` blob under the xpath-derived name and rewrites
the element text to that name.
- `getImagePath` gets the value directly when it starts with `user-file/` and
preserves the legacy fetch path

Existing data behavior:
- Pre-existing docs with `<binary>=""` and a `user-file/<xpath>` attachment, gets
overwrite-in-place on edit-re-save as the base64 is supplied for re-attachment under
the same xpath-derived key.
- Un-edited legacy main-doc records continue to render via the legacy
`user-file/<label-path>` fallback in `getImagePath`.
- Sub-doc image rendering, previously broken because the parent-form prefix was
unreachable from sub-doc field paths, now works once the field has been rewrittento the
literal attachment name.
- `calculate`-derived per-doc base64 is preserved across edits via re-computation at Enketo's form-init
The report path was searching [type=binary] for file-widget filenames,
so modern [type=file] widgets in sub-docs fell back to the main report
doc. Narrow both report and contact paths to [type=file] (inline
binaries are handled by their own loops).
A media field's value is now uniformly its attachment name minus the
USER_FILE_PREFIX, for both file-widget uploads and inline-binary fields.

- export USER_FILE_PREFIX from enketo.service; use it for both file and
  inline-binary attachment names (binary -> user-file-<formId>/<xpath>/<field>)
- store the bare reference (<formId>/<xpath>/<field>) as the binary field value
- add EnketoTranslationService.isAttachmentRef (path-pattern regex) and use it
  in the three former startsWith('user-file/') guards
- remove the dedicated user-file/ branch from getImagePath; the bare value
  resolves through the unified user-file- + value branch
- reduce processAllAttachments cognitive complexity (extract helpers)
- reduce bindJsonToXml cognitive complexity (extract shouldSkipBinaryBind,
  bindArrayToXml, bindObjectToXml)
- namespace report sub-doc binaries by ownerDoc.type
- update unit specs; fix stale type="binary" file-widget fixtures to type="file"
Make the sub-contact badge assertions robust to the exact xpath: verify the
inline-binary attachment exists and the field value is that name minus the
user-file- prefix, and that the value is preserved unchanged on edit.
Sub-docs carry their own form, so revert attachInlineBinary to
`ownerDoc.form || doc.form`. Update the db-doc-with-binary fixture so its
sub-docs include a <form> element, matching real sub-report shape.
- fill unit-coverage gaps: binary-in-repeat, empty binary, file dedup
- gitignore tmep folder
- tests hygiene: variable renaming and comments clean up
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants