aehrc / aehrc/smart-forms

$populate: an item with both an answer and child items skips answer filtering entirely

Open Beginner friendly
#2,121 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
TypeScript
Stars
61
Forks
29
Avg merge
2d 22h
Merged PRs (30d)
19

Description

Describe the bug

filterValueSetAnswersRecursive (packages/sdc-populate/src/SDCPopulateQuestionnaireOperation/utils/processValueSets.ts) returns as soon as it recurses into an item's children, without ever checking that item's own answer:

const items = qrItem.item;

if (items && items.length > 0) {
  // iterate through items of item recursively
  const qrItems: QuestionnaireResponseItem[] = items
    .map((item) => filterValueSetAnswersRecursive(item, ...))
    .filter((item): item is QuestionnaireResponseItem => item !== null);

  return { ...qrItem, item: qrItems };
}

// unreachable when qrItem.item is non-empty:
const answerOptionCodings = answerOptions[linkId]?.map((option) => option.valueCoding);
if (qrItem.answer && answerOptionCodings) { ... }

An item can legally carry both answer and nested item — normal FHIR Questionnaire nesting for a question with sub-questions. For such an item, the early return on the child-recursion branch means its own answer array never reaches filterAndNormaliseAnswers. An out-of-answerOption valueCoding survives unfiltered even on a plain choice item, and a matching coding is never normalised to the option's coding. No later pass compensates — addDisplayToQuestionnaireResponseCodings and removeEmptyAnswersFromResponse don't filter against answerOption/valueSet options.

To Reproduce

import { filterValueSetAnswersRecursive } from './utils/processValueSets';

const qrItem = {
  linkId: 'parent-with-subquestions',
  answer: [
    { valueCoding: { system: 'http://example.com', code: 'NOT-AN-OPTION', display: 'Bogus' } }
  ],
  item: [{ linkId: 'child', answer: [{ valueString: 'irrelevant' }] }]
};

const answerOptions = {
  'parent-with-subquestions': [
    { valueCoding: { system: 'http://example.com', code: 'REAL-OPTION', display: 'Real' } }
  ]
};

filterValueSetAnswersRecursive(qrItem, {}, answerOptions, {}, new Set());

Expected behavior

The item is choice-typed (not open-choice), so NOT-AN-OPTION — which isn't one of its answerOptions — should be filtered out, same as it would be if the item had no child items.

Actual behavior

{ "valueCoding": { "system": "http://example.com", "code": "NOT-AN-OPTION", "display": "Bogus" } }

survives unfiltered.

Additional context

  • Found while reviewing #2096 (which fixes #2095) — pre-existing, older than the defects #2096 addresses, and not touched by that PR.
  • A fix needs to filter the item's own answer before (or in addition to) recursing into item, rather than the two being mutually exclusive branches.
  • Originally noted by @not-in-stock in a comment on #2095.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in packages/sdc-populate/src/SDCPopulateQuestionnaireOperation/utils/processValueSets.ts at filterValueSetAnswersRecursive, using the supplied reproduction to trace items that contain both answer and child items. Ensure the item's own answers are checked against answerOptions while child items are recursively processed; done means the invalid coding is removed and a valid coding is normalised without losing the child item.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
api
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
78/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.