Skip to content

[FIX] download-template: query /organisationUnits instead of /metadata for v43 - #411

Open
anagperal wants to merge 2 commits into
developmentfrom
fix/org-units-query-dhis2-43
Open

anagperal wants to merge 2 commits into
developmentfrom
fix/org-units-query-dhis2-43

Conversation

@anagperal

@anagperal anagperal commented Jul 21, 2026 •

Copy link
Copy Markdown

📌 References

📝 Implementation

Why the 400 happened

The request went to the wrong endpoint. On /api/metadata, un-prefixed fields, filter and order are defaults applied to every exportable schema, not to the type you care about. The v43 docs say it literally — "Default order to apply to all types" — and the wording is identical in the 2.41 docs, so this call was always incorrect. It was simply harmless before: filter=id:in:[...] matched nothing in the other schemas, so they came back as empty arrays and only organisationUnits was visible in the response.

Why it broke in v43 specifically

Comparing JpaCriteriaQueryEngine.java across the dhis2-core release branches:

  • 2.41 / 2.42 — ordering was a flat root.get(property.getFieldName()) with no display* handling at all. Ordering by displayName never reached the database.
  • 2.43 — added tryGetTranslatableOrder / handleDisplayProperty (absent in both earlier branches) to push translatable ordering down into JPA.

The final fallback in handleDisplayProperty calls getRegularOrder without checking that the entity actually maps the column. displayName resolves to name and displayShortName to shortName, so applying it across all schemas ends up calling root.get("shortName") on OptionGroupSet and root.get("name") on ApiToken, which don't map those columns. DHIS2's Schema claims the property exists; the Hibernate entity doesn't have it.

v43 also upgraded Hibernate 5 → 6, whose AbstractManagedType.checkNotNull throws where the old metamodel tolerated the mismatch. That's the top frame of the stack trace, and the reason this surfaces as a 400 rather than being silently ignored.

🔥 Notes for the reviewer

  • download a template from a program

🎨 Screenshots

ERROR:

image

FIXED:

image

📑 Others

… for org units

On /api/metadata, un-prefixed fields/filter/order params are applied to every
exportable schema, not only to the type being queried. Since DHIS2 43 the query
engine resolves translatable order properties against each schema (displayName ->
name, displayShortName -> shortName), so the request failed with a 400 on schemas
that do not map those columns, e.g. ApiToken and OptionGroupSet.

Query /api/organisationUnits instead, so fields/filter/order can only ever affect
a single schema. paging: false is required: /api/metadata never paginates, but
/api/organisationUnits defaults to 50 per page and the chunks here are up to 400
ids, so omitting it would silently truncate the generated templates.
Pre-existing formatting drift picked up by `yarn prettify`. Whitespace only,
no behaviour change.
@anagperal
anagperal requested a review from Ramon-Jimenez July 21, 2026 09:15
@bundlemon

bundlemon Bot commented Jul 21, 2026

Copy link
Copy Markdown

BundleMon

No change in files bundle size

Unchanged groups (1)
Status Path Size Limits
✅ Build Folder
./**/*
1.74MB +20%

Final result: ✅

View report in BundleMon website ➡️


Current branch size history | Target branch size history

@adrianq
adrianq requested a review from xurxodev August 17, 2026 09:48

@xurxodev xurxodev left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @anagperal

@adrianq — approving, the fix is correct and well scoped.

Unrelated to this PR, but worth mentioning while we're in this file: DownloadTemplateUseCase uses D2Api directly, which breaks the Clean Architecture dependency rule. It's not just an import — the api is the first parameter of execute:

public async execute(api: D2Api, options: DownloadTemplateProps): Promise<void>

Because it sits in the public signature, the coupling propagates upwards: the two webapp callers pull api from useAppContext() solely to forward it. The use case already receives 6 repositories through its constructor in CompositionRoot.ts:110, so the right pattern is already there; api is the only one travelling through the wrong channel.

A clear candidate for a refactor. Leaving the details here so they don't get lost:

Remove the d2-api usage from the use case and apply the dependency rule properly using the repository pattern.

Callers — only two, and trivial since they just forward api: DownloadTemplatePage.tsx:69 and TemplateListTable.tsx:154. On top of that, getExecute (CompositionRoot.ts:170) derives the public type from the execute signature, so removing the parameter makes both fail to compile — none can be missed.

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.

2 participants