Repository navigation
feat: thread constants, enrollment, events, and supplementary data into rule engines - #16
Conversation
…to rule engines Populate RuleEngineContextJs constantsValues and supplementaryData from metadata and new optional hook inputs, fix enrollment other-events hardcoded to [], and pass enrollment context from useEventForm so TEI_ATTRIBUTE and cross-event variables evaluate correctly.
📝 WalkthroughWalkthroughThe PR adds global program constants to metadata queries, types, and resolvers. It extends rule evaluation with constants, tracker enrollment data, sibling events, and supplementary data. Event and tracker form hooks pass these inputs into the rule engine. ChangesMetadata and rule-engine inputs
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant FormHook
participant RuleContext
participant EnrollmentContext
participant RuleEngine
FormHook->>RuleContext: pass constants and supplementary data
FormHook->>EnrollmentContext: pass enrollment data and sibling events
RuleContext->>RuleEngine: provide converted rule context
EnrollmentContext->>RuleEngine: provide enrollment context and events
RuleEngine->>FormHook: evaluate form rules
Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Storybook JSON fixtures cast to TrackerProgramMetadata without a constants field; toEnrollmentContext now guards that path and fixtures include constants: [].
There was a problem hiding this comment.
🧹 Nitpick comments (2)
utils/hooks/src/useEventForm.ts (1)
79-92: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win
eventsmemoization keyed by reference in both form hooks. Both hooks memoize converted rule events onoptions.eventsby reference. If a caller passes an inline array, the memo recomputes on every render,ruleEnginechanges identity, andformStore.reinitruns during render. Both hooks document the contract in a comment, so the behavior is intentional; a shared internal guard would remove the footgun without an API change.
utils/hooks/src/useEventForm.ts#L79-L92: use a module-level empty-array constant for theoptions.events ?? []fallback, or compare event inputs by content before rebuildingenrollmentContext.utils/hooks/src/useTrackerForm.ts#L55-L62: apply the same guard to theruleEventsmemo.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@utils/hooks/src/useEventForm.ts` around lines 79 - 92, Prevent event conversion from rebuilding when callers omit events or provide equivalent inputs in both form hooks. In utils/hooks/src/useEventForm.ts lines 79-92, stabilize the options.events ?? [] fallback with a module-level empty-array constant or add content-based guarding before rebuilding enrollmentContext; apply the same guard to the ruleEvents memo in utils/hooks/src/useTrackerForm.ts lines 55-62, preserving the existing API and preventing unnecessary ruleEngine identity changes.utils/rules/src/context.ts (1)
176-192: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winDuplicated constants-map construction in
utils/rules/src/context.tsandutils/rules/src/enrollmentContext.ts. Both files convertProgramConstant[]into aMap<string, string>with the sameflatMapandString(value)logic. The copies already differ: only the enrollment copy guards withArray.isArray. Extract one exported helper and call it from both sites so the null handling stays identical.
utils/rules/src/context.ts#L176-L192: replace the inlineconstantsMapwith a call to the shared helper.utils/rules/src/enrollmentContext.ts#L140-L144: replace the inlineconstantsMap, including theArray.isArrayguard, with the same helper.♻️ Suggested shared helper
// utils/rules/src/context.ts export const toConstantsMap = (constants?: ProgramConstant[]): Map<string, string> => new Map( (Array.isArray(constants) ? constants : []).flatMap((c) => c.id ? [[c.id, String(c.value)] as const] : [] ) );- const constantsMap = new Map( - constants.flatMap((c) => (c.id ? [[c.id, String(c.value)] as const] : [])) - ); + const constantsMap = toConstantsMap(constants);- const constantsMap = new Map( - (Array.isArray(metadata.constants) ? metadata.constants : []).flatMap((c) => - c.id ? [[c.id, String(c.value)] as const] : [] - ) - ); + const constantsMap = toConstantsMap(metadata.constants);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@utils/rules/src/context.ts` around lines 176 - 192, Extract an exported toConstantsMap helper in utils/rules/src/context.ts that accepts optional ProgramConstant[] values, normalizes non-array input to an empty array, and preserves the existing id filtering and String(value) conversion. Replace the inline constantsMap construction at utils/rules/src/context.ts:176-192 with this helper, and replace the guarded construction at utils/rules/src/enrollmentContext.ts:140-144 with the same imported helper so both sites share identical null handling.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@utils/hooks/src/useEventForm.ts`:
- Around line 79-92: Prevent event conversion from rebuilding when callers omit
events or provide equivalent inputs in both form hooks. In
utils/hooks/src/useEventForm.ts lines 79-92, stabilize the options.events ?? []
fallback with a module-level empty-array constant or add content-based guarding
before rebuilding enrollmentContext; apply the same guard to the ruleEvents memo
in utils/hooks/src/useTrackerForm.ts lines 55-62, preserving the existing API
and preventing unnecessary ruleEngine identity changes.
In `@utils/rules/src/context.ts`:
- Around line 176-192: Extract an exported toConstantsMap helper in
utils/rules/src/context.ts that accepts optional ProgramConstant[] values,
normalizes non-array input to an empty array, and preserves the existing id
filtering and String(value) conversion. Replace the inline constantsMap
construction at utils/rules/src/context.ts:176-192 with this helper, and replace
the guarded construction at utils/rules/src/enrollmentContext.ts:140-144 with
the same imported helper so both sites share identical null handling.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 83c512ee-0cd5-47c1-8dca-80f91d514f89
📒 Files selected for processing (30)
apps/playground/src/utils/trackerPayloads.test.tsapps/storybook/fixtures/anc.event.jsonapps/storybook/fixtures/child-programme.event.jsonapps/storybook/fixtures/child-programme.tracker.jsonpackages/metadata/src/buildTrackerSchema.test.tspackages/metadata/src/fieldFilters.tspackages/metadata/src/index.tspackages/metadata/src/queries/eventProgramConfig.query.test.tspackages/metadata/src/queries/eventProgramConfig.query.tspackages/metadata/src/queries/fields.const.test.tspackages/metadata/src/queries/fields.const.tspackages/metadata/src/queries/trackerConfig.query.test.tspackages/metadata/src/queries/trackerConfig.query.tspackages/metadata/src/resolvers/resolveEventProgramMetadata.test.tspackages/metadata/src/resolvers/resolveEventProgramMetadata.tspackages/metadata/src/resolvers/resolveTrackerProgramMetadata.test.tspackages/metadata/src/resolvers/resolveTrackerProgramMetadata.tspackages/metadata/src/trackerTypes.tspackages/metadata/src/types.tsutils/hooks/src/queries/useEventProgramMetadataQuery.test.tsutils/hooks/src/useEventForm.test.tsxutils/hooks/src/useEventForm.tsutils/hooks/src/useRuleEffectTrace.test.tsxutils/hooks/src/useTrackerForm.test.tsxutils/hooks/src/useTrackerForm.tsutils/rules/src/context.test.tsutils/rules/src/context.tsutils/rules/src/enrollmentContext.test.tsutils/rules/src/enrollmentContext.tsutils/rules/src/index.ts


Why
Event and enrollment rule evaluation only wired a fraction of
@dhis2/rule-engineinputs.supplementaryData/constantsValueswere always empty, enrollment evaluation hardcoding[]other events, anduseEventFormnever passing enrollment context. Real programs usingC{…},d2:inUserGroup(), cross-stage event variables, orTEI_ATTRIBUTEfrom an event form silently got wrong/empty results.What
constantsvia event/tracker config queries; addconstantstoEventProgramMetadata/TrackerProgramMetadatatoRuleEventFromInput/toRuleEnrollment/toRuleSupplementaryData; pass other events intobuildEnrollmentRuleEngineenrollment/events/supplementaryDataonuseEventForm; optionalevents/supplementaryDataonuseTrackerForm; constants derived frommetadata.constantsFormStore/evaluateAndMapunchanged — new context is baked into theBuiltRuleEngineclosure. Playground UI wiring is out of scope (fixture-only compile fix).Follow-up fix
Storybook registration stories crashed with
Cannot read properties of undefined (reading 'flatMap')because cast JSON fixtures lackedconstants. GuardedtoEnrollmentContextand addedconstants: []to storybook fixtures.Tests
CONSTANT_FIELDS, unfiltered constants query, resolver default/passthroughd2:inUserGroup, TEI_ATTRIBUTE via enrollment,V{event_count}sibling eventspnpm typecheck && pnpm testgreen (including Storybook 80/80)Summary by CodeRabbit