Skip to content

feat: thread constants, enrollment, events, and supplementary data into rule engines - #16

Merged
cursor[bot] merged 2 commits into
mainfrom
cursor/rule-variable-inputs-3a44
Aug 2, 2026
Merged

cursor[bot] merged 2 commits into
mainfrom
cursor/rule-variable-inputs-3a44

Conversation

@nnkogift

@nnkogift nnkogift commented Aug 2, 2026 •

Copy link
Copy Markdown
Owner

Why

Event and enrollment rule evaluation only wired a fraction of @dhis2/rule-engine inputs. supplementaryData / constantsValues were always empty, enrollment evaluation hardcoding [] other events, and useEventForm never passing enrollment context. Real programs using C{…}, d2:inUserGroup(), cross-stage event variables, or TEI_ATTRIBUTE from an event form silently got wrong/empty results.

What

  • metadata: fetch global constants via event/tracker config queries; add constants to EventProgramMetadata / TrackerProgramMetadata
  • rules: thread constants + supplementary data into both engine contexts; export toRuleEventFromInput / toRuleEnrollment / toRuleSupplementaryData; pass other events into buildEnrollmentRuleEngine
  • hooks: optional enrollment / events / supplementaryData on useEventForm; optional events / supplementaryData on useTrackerForm; constants derived from metadata.constants

FormStore / evaluateAndMap unchanged — new context is baked into the BuiltRuleEngine closure. 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 lacked constants. Guarded toEnrollmentContext and added constants: [] to storybook fixtures.

Tests

  • metadata: CONSTANT_FIELDS, unfiltered constants query, resolver default/passthrough
  • rules: constants, d2:inUserGroup, TEI_ATTRIBUTE via enrollment, V{event_count} sibling events
  • hooks: one test per new option + constants auto-derivation
  • pnpm typecheck && pnpm test green (including Storybook 80/80)
Open in Web Open in Cursor 

Summary by CodeRabbit

  • New Features
    • Added support for program constants in event and tracker metadata.
    • Rules can now use enrollment attributes, sibling events, supplementary data, and user-group information.
    • Event and tracker forms now accept additional rule-evaluation inputs.
    • Added handling for missing or empty constants, defaulting safely to an empty collection.
  • Bug Fixes
    • Improved rule evaluation for event counts, enrollment data, and metadata constants.
    • Standardized handling of event dates and invalid event inputs.

…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.
@coderabbitai

coderabbitai Bot commented Aug 2, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The 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.

Changes

Metadata and rule-engine inputs

Layer / File(s) Summary
Metadata contracts and global constants queries
packages/metadata/src/*
Metadata types expose ProgramConstant and constants. Event and tracker queries fetch global constants using id,value fields without paging.
Metadata resolution and defaults
packages/metadata/src/resolvers/*, apps/storybook/fixtures/*, apps/playground/..., packages/metadata/src/buildTrackerSchema.test.ts, utils/hooks/src/queries/*
Resolvers preserve constants and default missing values to []. Fixtures and metadata expectations include the new property.
Rule context conversion
utils/rules/src/context.*
Rule context construction converts constants and supplementary data. Event inputs become rule events with status and instant handling.
Enrollment engine wiring
utils/rules/src/enrollmentContext.*, utils/rules/src/index.ts
Enrollment contexts use constants and supplementary data. Enrollment evaluation accepts sibling events. New conversion helpers are publicly exported.
Form hook rule inputs
utils/hooks/src/useEventForm.*, utils/hooks/src/useTrackerForm.*
Form hooks accept enrollment data, events, and supplementary data, then pass converted inputs to rule evaluation. Tests cover constants, TEI attributes, event counts, and user groups.

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
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change by identifying the rule-engine inputs being threaded through the system.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch cursor/rule-variable-inputs-3a44

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@nnkogift
nnkogift marked this pull request as ready for review August 2, 2026 16:40
Storybook JSON fixtures cast to TrackerProgramMetadata without a constants
field; toEnrollmentContext now guards that path and fixtures include
constants: [].
cursor[bot]
cursor Bot approved these changes Aug 2, 2026 •

@cursor cursor Bot 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.

Approved: Cursor Security Agent completed successfully with no findings requiring human review; Cursor Bugbot was not present on this PR. No reviewers assigned.

Open in Web View Automation 

Sent by Cursor Approval Agent: Pull Request Router and Approver

@coderabbitai coderabbitai Bot 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.

🧹 Nitpick comments (2)
utils/hooks/src/useEventForm.ts (1)

79-92: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

events memoization keyed by reference in both form hooks. Both hooks memoize converted rule events on options.events by reference. If a caller passes an inline array, the memo recomputes on every render, ruleEngine changes identity, and formStore.reinit runs 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 the options.events ?? [] fallback, or compare event inputs by content before rebuilding enrollmentContext.
  • utils/hooks/src/useTrackerForm.ts#L55-L62: apply the same guard to the ruleEvents memo.
🤖 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 win

Duplicated constants-map construction in utils/rules/src/context.ts and utils/rules/src/enrollmentContext.ts. Both files convert ProgramConstant[] into a Map<string, string> with the same flatMap and String(value) logic. The copies already differ: only the enrollment copy guards with Array.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 inline constantsMap with a call to the shared helper.
  • utils/rules/src/enrollmentContext.ts#L140-L144: replace the inline constantsMap, including the Array.isArray guard, 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

📥 Commits

Reviewing files that changed from the base of the PR and between 35f6e5a and fe38607.

📒 Files selected for processing (30)
  • apps/playground/src/utils/trackerPayloads.test.ts
  • apps/storybook/fixtures/anc.event.json
  • apps/storybook/fixtures/child-programme.event.json
  • apps/storybook/fixtures/child-programme.tracker.json
  • packages/metadata/src/buildTrackerSchema.test.ts
  • packages/metadata/src/fieldFilters.ts
  • packages/metadata/src/index.ts
  • packages/metadata/src/queries/eventProgramConfig.query.test.ts
  • packages/metadata/src/queries/eventProgramConfig.query.ts
  • packages/metadata/src/queries/fields.const.test.ts
  • packages/metadata/src/queries/fields.const.ts
  • packages/metadata/src/queries/trackerConfig.query.test.ts
  • packages/metadata/src/queries/trackerConfig.query.ts
  • packages/metadata/src/resolvers/resolveEventProgramMetadata.test.ts
  • packages/metadata/src/resolvers/resolveEventProgramMetadata.ts
  • packages/metadata/src/resolvers/resolveTrackerProgramMetadata.test.ts
  • packages/metadata/src/resolvers/resolveTrackerProgramMetadata.ts
  • packages/metadata/src/trackerTypes.ts
  • packages/metadata/src/types.ts
  • utils/hooks/src/queries/useEventProgramMetadataQuery.test.ts
  • utils/hooks/src/useEventForm.test.tsx
  • utils/hooks/src/useEventForm.ts
  • utils/hooks/src/useRuleEffectTrace.test.tsx
  • utils/hooks/src/useTrackerForm.test.tsx
  • utils/hooks/src/useTrackerForm.ts
  • utils/rules/src/context.test.ts
  • utils/rules/src/context.ts
  • utils/rules/src/enrollmentContext.test.ts
  • utils/rules/src/enrollmentContext.ts
  • utils/rules/src/index.ts

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