Skip to content

Expose observations on /api/approvalStatuses and confirm the CSV auto-approve path #1053

Description

@ombhardwajj

As an integration consumer, I need the approval API to return the coded answers, so that systems outside Avni can read the reason a record was rejected.

Acceptance Criteria

  • A system reading approvals through the API sees the form answers alongside the status. Verify: GET /api/approvalStatuses includes them for decisions that have them.
  • Decisions without answers come back exactly as they do today. Verify: no new key, or an explicit null — never an error.
  • Approving records in bulk from a spreadsheet still works when an approval form is attached. Those records simply carry no answers, because nobody opened a form. Verify: import with autoApprove=true succeeds and writes null observations.
  • Bulk approval where no approval form is attached behaves exactly as it does today.

Technical Details

  • avni-server-api/src/main/java/org/avni/server/web/response/EntityApprovalStatusResponse.java:8-16 — the response is a LinkedHashMap built with human-readable keys ("Entity ID", "Approval status comment", …). Add the observations after "Approval status comment", rendering them through Response.mapObservations(conceptRepository, conceptService, …) — the helper every other /api/* response already uses. It emits {"<question name>": "<resolved answer>"}, resolving coded answer UUIDs to concept names via ConceptService.getObservationValue and applying the API-version date normalisation.

    Do not use ObservationService.constructObservations. An earlier version of this story named it and claimed it matched the other API responses; both halves were wrong. It is the web/DEA contract helper (EncounterService, IndividualService use it), it returns a list of ObservationContract rather than a name-keyed map, and it leaves coded values as raw UUIDs — so an integration reading /api/approvalStatuses would get a different shape from /api/subjects, which is the opposite of this story's point. It also opens two problems mapObservations avoids: a per-row concept lookup (N+1 across a page) and an NPE surface on Subject-datatype answers whose referenced subject is voided or out of RLS scope.

  • avni-server-api/src/main/java/org/avni/server/importer/batch/csv/writer/EntityApprovalStatusWriter.java:26-30 — no code change expected. It calls entityApprovalStatusService.createStatus(...), which story 2 deliberately leaves alone, so rows arrive with null observations. This story is to prove that and add the regression test, not to change behaviour.

Do NOT:

  • Do not block CSV import when an Approval form is mapped. That was considered and rejected in the spec: bulk import is an administrative action, and blocking it would break existing imports for any org that later attaches a form.
  • Do not add observations to the CSV upload format. Supplying form answers per row is a much larger feature and is not in scope.

Testing Gotchas

  • /api/approvalStatuses is gated by @PreAuthorize("hasAnyAuthority('user')") and then checkApprovePrivilegeOnEntityApprovalStatuses, which throws if the caller lacks the approve privilege for any entity type in the page. A test user needs the right privileges or the endpoint 403s before serialisation is reached.
  • The response keys contain spaces and are order-sensitive (LinkedHashMap). Assert on content, not on a serialised string.
  • autoApprove reaches the writer as a @Value("#{jobParameters['autoApprove']}") String, parsed with Boolean.parseBoolean. Anything other than "true" is false — a common source of confusing test results.

avni-models


Epic: avniproject/avni-product#1901

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions