Skip to content

feat(davinci-client): support form agreements with AgreementCollector#579

Open
ancheetah wants to merge 1 commit intomainfrom
SDKS-4691-agreements
Open

feat(davinci-client): support form agreements with AgreementCollector#579
ancheetah wants to merge 1 commit intomainfrom
SDKS-4691-agreements

Conversation

@ancheetah
Copy link
Copy Markdown
Collaborator

@ancheetah ancheetah commented Apr 23, 2026

JIRA Ticket

https://pingidentity.atlassian.net/browse/SDKS-4691

Description

Adds a new NoValueCollector called AgreementCollector that stores AGREEMENT field options in the output. Includes unit tests. Agreement component has been added to e2e app but e2e tests will be handled in another ticket once we can create more flows in DaVinci. SINGLE_CHECKBOX will be addressed in another ticket.

Screenshot 2026-04-23 at 11 32 20 AM

Summary by CodeRabbit

Release Notes

  • New Features

    • Forms now support agreement fields, enabling display of configurable agreement content with optional titles, enable/disable visibility controls, and customizable agreement metadata for enhanced user interaction.
  • Tests

    • Added comprehensive type safety validation and unit tests covering agreement field creation, initialization, type inference, and end-to-end rendering behavior.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 23, 2026

📝 Walkthrough

Walkthrough

Adds support for form agreements through a new AgreementCollector type. Introduces AgreementField type definition, agreement collector utility functions, reducer handling for agreement fields, e2e component rendering, and comprehensive type and unit test coverage.

Changes

Cohort / File(s) Summary
Type System & Definitions
packages/davinci-client/src/lib/davinci.types.ts, packages/davinci-client/src/lib/collector.types.ts, packages/davinci-client/src/lib/node.types.ts
Introduces AgreementField and AgreementCollector types with output schema supporting title/enablement controls and nested agreement metadata. Updates ReadOnlyFields and Collectors unions to include new variants.
Utilities & Logic
packages/davinci-client/src/lib/collector.utils.ts, packages/davinci-client/src/lib/node.reducer.ts
Adds returnAgreementCollector utility function and extends returnNoValueCollector to handle agreement field types. Updates reducer to recognize AGREEMENT field type and route to agreement collector factory.
E2E Component & Integration
e2e/davinci-app/components/agreement.ts, e2e/davinci-app/main.ts
Introduces agreement e2e component that conditionally renders title and label from collector output. Integrates component into form renderer with collector type branching.
Type Test Coverage
packages/davinci-client/src/lib/collector.types.test-d.ts, packages/davinci-client/src/lib/node.types.test-d.ts
Adds compile-time type inference tests for InferNoValueCollectorType with AgreementCollector and updates Collectors union expectations in node type tests.
Unit Test Coverage
packages/davinci-client/src/lib/collector.utils.test.ts, packages/davinci-client/src/lib/node.reducer.test.ts
Validates returnAgreementCollector function with complete field structure and error handling. Tests reducer produces correct AgreementCollector instance from AGREEMENT field action.
Changelog
.changeset/silent-ideas-joke.md
Documents minor version bump for @forgerock/davinci-client package with agreement collector support.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • ryanbas21
  • cerebrl

Poem

Form agreements bound through the code,
AgreementCollectors lighten the load,
New types take their place,
Tests keep up the pace,
A rabbit's delightful new mode! 🐰✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: adding support for form agreements through a new AgreementCollector type in the davinci-client package.
Description check ✅ Passed The description includes the JIRA ticket reference and clearly explains the changes: adding AgreementCollector, unit tests, e2e component, and deferred work items.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch SDKS-4691-agreements

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 and usage tips.

@ancheetah ancheetah requested review from cerebrl and ryanbas21 April 23, 2026 15:33
@nx-cloud
Copy link
Copy Markdown
Contributor

nx-cloud Bot commented Apr 23, 2026

View your CI Pipeline Execution ↗ for commit 2690d5b

Command Status Duration Result
nx run-many -t build --no-agents ✅ Succeeded <1s View ↗
nx affected -t build lint test typecheck e2e-ci ✅ Succeeded 7m 12s View ↗

☁️ Nx Cloud last updated this comment at 2026-04-23 15:42:02 UTC

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
packages/davinci-client/src/lib/collector.types.ts (1)

485-555: ⚠️ Potential issue | 🟡 Minor

Align the exported NoValueCollector<T> generic with AgreementCollector specifics.

The generic NoValueCollector<T> on line 555 is exported in the public API but still aliases NoValueCollectorBase<T>, which means NoValueCollector<'AgreementCollector'> resolves to an incomplete type missing the required titleEnabled, title, agreement, and enabled fields. While the internal codebase correctly uses InferNoValueCollectorType<T> which handles this, external consumers of the public type could inadvertently use the incorrect generic path.

Proposed type alignment
-export type NoValueCollector<T extends NoValueCollectorTypes> = NoValueCollectorBase<T>;
+export type NoValueCollector<T extends NoValueCollectorTypes> =
+  T extends 'QrCodeCollector'
+    ? QrCodeCollectorBase
+    : T extends 'AgreementCollector'
+      ? AgreementCollector
+      : NoValueCollectorBase<T>;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/davinci-client/src/lib/collector.types.ts` around lines 485 - 555,
The exported generic NoValueCollector<T> currently aliases
NoValueCollectorBase<T>, which makes NoValueCollector<'AgreementCollector'> miss
AgreementCollector-specific fields; change the exported type definition of
NoValueCollector<T> to use the existing conditional helper
InferNoValueCollectorType<T> (i.e., export type NoValueCollector<T extends
NoValueCollectorTypes> = InferNoValueCollectorType<T>) so consumers get the
correct narrowed types for 'AgreementCollector' and others while keeping
NoValueCollectorBase, QrCodeCollectorBase and AgreementCollector definitions
unchanged.
packages/davinci-client/src/lib/collector.utils.ts (1)

732-758: ⚠️ Potential issue | 🟡 Minor

Inconsistent null-safety on agreement output fields.

field.agreement?.id ?? '', useDynamicAgreement ?? false, and enabled ?? false all defensively handle missing values, but titleEnabled and title are assigned raw. Given the existing "missing content" error path is tolerant of malformed fields (see the returnAgreementCollector error-case test which passes { type: 'AGREEMENT', key: '...' }), these two properties can end up as undefined at runtime while the AgreementCollector interface declares them as boolean / string (non-optional).

Either tighten the defaults here or mark them optional on the type — whichever better matches the DaVinci contract.

🔧 Suggested defensive defaults
   if (collectorType === 'AgreementCollector' && field.type === 'AGREEMENT') {
     output = {
-      titleEnabled: field.titleEnabled,
-      title: field.title,
+      titleEnabled: field.titleEnabled ?? false,
+      title: field.title ?? '',
       agreement: {
         id: field.agreement?.id ?? '',
         useDynamicAgreement: field.agreement?.useDynamicAgreement ?? false,
       },
       enabled: field.enabled ?? false,
     };
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/davinci-client/src/lib/collector.utils.ts` around lines 732 - 758,
The AgreementCollector output can yield undefined for titleEnabled and title
while other fields use safe defaults; update the output construction in the
AgreementCollector branch (the object built when collectorType ===
'AgreementCollector' and field.type === 'AGREEMENT') to apply defensive defaults
like titleEnabled: field.titleEnabled ?? false and title: field.title ?? ''
(keep agreement.id, agreement.useDynamicAgreement, and enabled defaults as-is)
so the produced object matches the AgreementCollector shape expected by
InferNoValueCollectorType<CollectorType>.
🧹 Nitpick comments (3)
packages/davinci-client/src/lib/collector.utils.test.ts (1)

888-929: Consider adding a test for nullish-fallback behavior.

The happy path and the missing-content error case are covered, but returnNoValueCollector's AGREEMENT branch has explicit nullish coalescing for field.agreement?.id ?? '', agreement?.useDynamicAgreement ?? false, and field.enabled ?? false. None of those fallbacks are exercised here. A test with a field missing agreement / enabled would lock in the documented behavior and catch regressions if the fallbacks are ever changed.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/davinci-client/src/lib/collector.utils.test.ts` around lines 888 -
929, Add a test exercising the nullish-fallbacks in returnAgreementCollector
(the AGREEMENT branch of returnNoValueCollector): create a mock AgreementField
that omits the agreement and enabled properties (e.g., { type: 'AGREEMENT', key:
'agreement-field', content: 'label' }) call returnAgreementCollector(mockField,
0) and assert the resulting output.agreement.id === '' ,
output.agreement.useDynamicAgreement === false, and output.enabled === false
(also assert id/name follow the '-0' suffix as in other tests) to lock in the
documented fallback behavior.
packages/davinci-client/src/lib/collector.utils.ts (1)

720-744: Consider extracting per-type output builders as this union grows.

returnNoValueCollector now handles three distinct field shapes (ReadOnly, QrCode via returnQrCodeCollector, Agreement here) with a let output = {} + conditional block pattern. As more NoValueCollector variants are added (e.g., the deferred SINGLE_CHECKBOX), this will get harder to keep coherent with InferNoValueCollectorType. A small per-type builder (mirroring how returnQrCodeCollector layers on top of the base) would scale better than stacking more if (collectorType === ...) branches inside the base factory.

Non-blocking — safe to defer.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/davinci-client/src/lib/collector.utils.ts` around lines 720 - 744,
returnNoValueCollector currently builds different variant outputs inline (using
let output = {} and conditional branches for Agreement and others) which will
not scale; extract per-type builder functions (e.g., buildAgreementCollector,
buildReadOnlyCollector) that accept the Field and idx and return the specific
output shape, then have returnNoValueCollector delegate to these builders (or
use a map keyed by CollectorType) and call returnQrCodeCollector where
appropriate to keep layering consistent; update signatures and any type guards
to use those new builders and remove the growing collectorType conditional
blocks inside returnNoValueCollector.
packages/davinci-client/src/lib/node.reducer.test.ts (1)

455-497: LGTM — AGREEMENT reducer coverage matches the collector contract.

Expected collector shape aligns with AgreementCollector (category, type, id/name with -0 suffix, and full output mapping).

Nit: unlike the other node/next tests in this suite, the payload omits formData. Not a functional problem since the reducer path for AGREEMENT doesn't consume it, but adding formData: {} would keep the test payloads uniform.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/davinci-client/src/lib/node.reducer.test.ts` around lines 455 - 497,
The test "should handle AGREEMENT field type" omits formData in the action
payload which breaks uniformity with other node/next tests; update the payload
passed to nodeCollectorReducer in this test to include formData: {} (i.e., add
formData: {} inside action.payload) so the test payloads are consistent while
leaving nodeCollectorReducer and AgreementCollector expectations unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@packages/davinci-client/src/lib/collector.types.ts`:
- Around line 485-555: The exported generic NoValueCollector<T> currently
aliases NoValueCollectorBase<T>, which makes
NoValueCollector<'AgreementCollector'> miss AgreementCollector-specific fields;
change the exported type definition of NoValueCollector<T> to use the existing
conditional helper InferNoValueCollectorType<T> (i.e., export type
NoValueCollector<T extends NoValueCollectorTypes> =
InferNoValueCollectorType<T>) so consumers get the correct narrowed types for
'AgreementCollector' and others while keeping NoValueCollectorBase,
QrCodeCollectorBase and AgreementCollector definitions unchanged.

In `@packages/davinci-client/src/lib/collector.utils.ts`:
- Around line 732-758: The AgreementCollector output can yield undefined for
titleEnabled and title while other fields use safe defaults; update the output
construction in the AgreementCollector branch (the object built when
collectorType === 'AgreementCollector' and field.type === 'AGREEMENT') to apply
defensive defaults like titleEnabled: field.titleEnabled ?? false and title:
field.title ?? '' (keep agreement.id, agreement.useDynamicAgreement, and enabled
defaults as-is) so the produced object matches the AgreementCollector shape
expected by InferNoValueCollectorType<CollectorType>.

---

Nitpick comments:
In `@packages/davinci-client/src/lib/collector.utils.test.ts`:
- Around line 888-929: Add a test exercising the nullish-fallbacks in
returnAgreementCollector (the AGREEMENT branch of returnNoValueCollector):
create a mock AgreementField that omits the agreement and enabled properties
(e.g., { type: 'AGREEMENT', key: 'agreement-field', content: 'label' }) call
returnAgreementCollector(mockField, 0) and assert the resulting
output.agreement.id === '' , output.agreement.useDynamicAgreement === false, and
output.enabled === false (also assert id/name follow the '-0' suffix as in other
tests) to lock in the documented fallback behavior.

In `@packages/davinci-client/src/lib/collector.utils.ts`:
- Around line 720-744: returnNoValueCollector currently builds different variant
outputs inline (using let output = {} and conditional branches for Agreement and
others) which will not scale; extract per-type builder functions (e.g.,
buildAgreementCollector, buildReadOnlyCollector) that accept the Field and idx
and return the specific output shape, then have returnNoValueCollector delegate
to these builders (or use a map keyed by CollectorType) and call
returnQrCodeCollector where appropriate to keep layering consistent; update
signatures and any type guards to use those new builders and remove the growing
collectorType conditional blocks inside returnNoValueCollector.

In `@packages/davinci-client/src/lib/node.reducer.test.ts`:
- Around line 455-497: The test "should handle AGREEMENT field type" omits
formData in the action payload which breaks uniformity with other node/next
tests; update the payload passed to nodeCollectorReducer in this test to include
formData: {} (i.e., add formData: {} inside action.payload) so the test payloads
are consistent while leaving nodeCollectorReducer and AgreementCollector
expectations unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: dd168374-d95a-460f-adfd-0ec59e5d8d27

📥 Commits

Reviewing files that changed from the base of the PR and between b28b6b0 and 2690d5b.

📒 Files selected for processing (12)
  • .changeset/silent-ideas-joke.md
  • e2e/davinci-app/components/agreement.ts
  • e2e/davinci-app/main.ts
  • packages/davinci-client/src/lib/collector.types.test-d.ts
  • packages/davinci-client/src/lib/collector.types.ts
  • packages/davinci-client/src/lib/collector.utils.test.ts
  • packages/davinci-client/src/lib/collector.utils.ts
  • packages/davinci-client/src/lib/davinci.types.ts
  • packages/davinci-client/src/lib/node.reducer.test.ts
  • packages/davinci-client/src/lib/node.reducer.ts
  • packages/davinci-client/src/lib/node.types.test-d.ts
  • packages/davinci-client/src/lib/node.types.ts

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Apr 23, 2026

🦋 Changeset detected

Latest commit: 2690d5b

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 12 packages
Name Type
@forgerock/davinci-client Minor
@forgerock/device-client Minor
@forgerock/journey-client Minor
@forgerock/oidc-client Minor
@forgerock/protect Minor
@forgerock/sdk-types Minor
@forgerock/sdk-utilities Minor
@forgerock/iframe-manager Minor
@forgerock/sdk-logger Minor
@forgerock/sdk-oidc Minor
@forgerock/sdk-request-middleware Minor
@forgerock/storage Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented Apr 23, 2026

Open in StackBlitz

@forgerock/davinci-client

pnpm add https://pkg.pr.new/ForgeRock/ping-javascript-sdk/@forgerock/davinci-client@579

@forgerock/device-client

pnpm add https://pkg.pr.new/ForgeRock/ping-javascript-sdk/@forgerock/device-client@579

@forgerock/journey-client

pnpm add https://pkg.pr.new/ForgeRock/ping-javascript-sdk/@forgerock/journey-client@579

@forgerock/oidc-client

pnpm add https://pkg.pr.new/ForgeRock/ping-javascript-sdk/@forgerock/oidc-client@579

@forgerock/protect

pnpm add https://pkg.pr.new/ForgeRock/ping-javascript-sdk/@forgerock/protect@579

@forgerock/sdk-types

pnpm add https://pkg.pr.new/ForgeRock/ping-javascript-sdk/@forgerock/sdk-types@579

@forgerock/sdk-utilities

pnpm add https://pkg.pr.new/ForgeRock/ping-javascript-sdk/@forgerock/sdk-utilities@579

@forgerock/iframe-manager

pnpm add https://pkg.pr.new/ForgeRock/ping-javascript-sdk/@forgerock/iframe-manager@579

@forgerock/sdk-logger

pnpm add https://pkg.pr.new/ForgeRock/ping-javascript-sdk/@forgerock/sdk-logger@579

@forgerock/sdk-oidc

pnpm add https://pkg.pr.new/ForgeRock/ping-javascript-sdk/@forgerock/sdk-oidc@579

@forgerock/sdk-request-middleware

pnpm add https://pkg.pr.new/ForgeRock/ping-javascript-sdk/@forgerock/sdk-request-middleware@579

@forgerock/storage

pnpm add https://pkg.pr.new/ForgeRock/ping-javascript-sdk/@forgerock/storage@579

commit: 2690d5b

@codecov-commenter
Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 15.73%. Comparing base (5d6747a) to head (2690d5b).
⚠️ Report is 37 commits behind head on main.

❌ Your project status has failed because the head coverage (15.73%) is below the target coverage (40.00%). You can increase the head coverage or adjust the target coverage.

Additional details and impacted files
@@             Coverage Diff             @@
##             main     #579       +/-   ##
===========================================
- Coverage   70.90%   15.73%   -55.18%     
===========================================
  Files          53      154      +101     
  Lines        2021    26685    +24664     
  Branches      377     1134      +757     
===========================================
+ Hits         1433     4199     +2766     
- Misses        588    22486    +21898     
Files with missing lines Coverage Δ
packages/davinci-client/src/lib/collector.types.ts 100.00% <ø> (ø)
packages/davinci-client/src/lib/collector.utils.ts 82.62% <100.00%> (ø)
packages/davinci-client/src/lib/davinci.types.ts 100.00% <ø> (ø)
packages/davinci-client/src/lib/node.reducer.ts 71.49% <100.00%> (ø)
packages/davinci-client/src/lib/node.types.ts 100.00% <ø> (ø)

... and 96 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@github-actions
Copy link
Copy Markdown
Contributor

Deployed 78fda76 to https://ForgeRock.github.io/ping-javascript-sdk/pr-579/78fda761aef551163b4aa89f163a0471ca4cc283 branch gh-pages in ForgeRock/ping-javascript-sdk

@github-actions
Copy link
Copy Markdown
Contributor

📦 Bundle Size Analysis

📦 Bundle Size Analysis

🚨 Significant Changes

🔻 @forgerock/device-client - 0.0 KB (-9.9 KB, -100.0%)
🔻 @forgerock/journey-client - 0.0 KB (-89.9 KB, -100.0%)

📊 Minor Changes

📉 @forgerock/device-client - 9.9 KB (-0.0 KB)
📈 @forgerock/davinci-client - 48.4 KB (+0.4 KB)

➖ No Changes

@forgerock/oidc-client - 25.2 KB
@forgerock/sdk-utilities - 11.2 KB
@forgerock/sdk-types - 7.9 KB
@forgerock/protect - 150.1 KB
@forgerock/journey-client - 89.9 KB
@forgerock/storage - 1.5 KB
@forgerock/sdk-oidc - 4.8 KB
@forgerock/sdk-request-middleware - 4.5 KB
@forgerock/sdk-logger - 1.6 KB
@forgerock/iframe-manager - 2.4 KB


14 packages analyzed • Baseline from latest main build

Legend

🆕 New package
🔺 Size increased
🔻 Size decreased
➖ No change

ℹ️ How bundle sizes are calculated
  • Current Size: Total gzipped size of all files in the package's dist directory
  • Baseline: Comparison against the latest build from the main branch
  • Files included: All build outputs except source maps and TypeScript build cache
  • Exclusions: .map, .tsbuildinfo, and .d.ts.map files

🔄 Updated automatically on each push to this PR

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

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants