Skip to content

feat(davinci-client): add metadata collector (SDKS-5100)#727

Open
ancheetah wants to merge 1 commit into
mainfrom
SDKS-5100-metadata-collector
Open

feat(davinci-client): add metadata collector (SDKS-5100)#727
ancheetah wants to merge 1 commit into
mainfrom
SDKS-5100-metadata-collector

Conversation

@ancheetah

@ancheetah ancheetah commented Jul 20, 2026

Copy link
Copy Markdown
Collaborator

JIRA Ticket

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

Description

What

Adds MetadataCollector support to davinci-client, enabling DaVinci flows to deliver server-side metadata payloads (e.g. session tokens, user context) to client applications. The collector carries its payload in output.value and reports success or structured errors back to the server via transformActionRequest.

Why

DaVinci flows need a mechanism to push arbitrary metadata to the client and receive a status (success or error) in return, without relying on a standard form submission. This fills that gap in the collector model.

Changes

  • New types: MetadataCollector, MetadataError, MetadataField, and DaVinciRequestValueTypes (extracted from inline union in davinci.utils.ts)
  • collector.types.ts / collector.utils.ts: Added MetadataCollector type alias and returnMetadataCollector factory; updated returnObjectCollector to handle the label-less METADATA field shape
  • node.reducer.ts: Added METADATA case to build a MetadataCollector from a field; added object-type guard for MetadataCollector update values
  • node.slice.ts: Falls back to metadataCollector.output.key for client.action when no SubmitCollector is present
  • davinci.api.ts: Auto-routes to transformActionRequest when a MetadataCollector is in the collector list
  • davinci.utils.ts: transformActionRequest now collects form data from all value-bearing collectors and includes it in the request body alongside actionKey
  • client.store.ts: Exposes getMetadataError helper on the client facade for constructing structured error objects
  • Tests: Unit tests for returnMetadataCollector and reducer MetadataField handling (create, update, invalid update)
  • E2e app: Added metadataComponent demonstrating success and error paths; wired into main.ts

Summary by CodeRabbit

  • New Features

    • Added support for metadata fields and collectors.
    • Metadata can now be submitted with custom success or error outcomes.
    • Added structured metadata error handling.
    • Enhanced action submissions to include metadata values.
  • Bug Fixes

    • Improved handling of metadata updates and validation.
    • Expanded flow handling for additional node and server states.
  • Tests

    • Added coverage for metadata creation, updates, submissions, and invalid values.

@changeset-bot

changeset-bot Bot commented Jul 20, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: b8c285f

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

@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds MetadataField, MetadataCollector, and MetadataError support across DaVinci types, reducers, request construction, client APIs, and the end-to-end example application. Metadata values can be updated, submitted, and represented as success or error results.

Changes

Metadata collector support

Layer / File(s) Summary
Metadata contracts and public types
packages/davinci-client/src/lib/{davinci.types.ts,client.types.ts,collector.types.ts,node.types.ts}, packages/davinci-client/api-report/*
Adds metadata field, collector, error, inference, and public API typings, including updated client and server unions.
Metadata collector construction and updates
packages/davinci-client/src/lib/{collector.utils.ts,node.reducer.ts}, packages/davinci-client/src/lib/*test*
Constructs metadata collectors from METADATA fields, stores payload output, validates object updates, and adds reducer and factory coverage.
Metadata submission and client state
packages/davinci-client/src/lib/{davinci.api.ts,davinci.utils.ts,node.slice.ts,client.store.ts}
Builds action requests with collector form data, selects metadata action keys, and exposes structured metadata error creation.
Metadata form interaction
e2e/davinci-app/{main.ts,components/metadata.ts}
Renders metadata success and error buttons that update the collector and submit the form.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant metadataComponent
  participant davinciClient
  participant DaVinciAPI
  User->>metadataComponent: Click metadata success or error
  metadataComponent->>davinciClient: Update MetadataCollector
  metadataComponent->>davinciClient: Submit form
  davinciClient->>DaVinciAPI: Build metadata action request
  DaVinciAPI-->>davinciClient: Return next node
Loading

Possibly related PRs

Suggested reviewers: ryanbas21

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 80.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.
Title check ✅ Passed The title clearly matches the main change: adding metadata collector support to davinci-client.
Description check ✅ Passed The description follows the template well, with a Jira ticket and a substantive summary of what changed and why.
✨ 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-5100-metadata-collector

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.

@nx-cloud

nx-cloud Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

View your CI Pipeline Execution ↗ for commit b8c285f

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 37s View ↗

💡 Verify your cache is correct by running tasks in a sandbox. Read docs ↗


☁️ Nx Cloud last updated this comment at 2026-07-20 20:15:18 UTC

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

Actionable comments posted: 2

🧹 Nitpick comments (2)
packages/davinci-client/src/lib/client.store.ts (1)

459-471: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove commented-out code.

It looks like the isClientError property was considered but isn't part of the MetadataError type. Consider removing the dead code to keep the file clean.

♻️ Proposed refactor
     /**
      * `@method` getMetadataError - Constructs a structured error object from a code and message.
      * `@param` {{ code: string; message: string; }} errorDetails - An error code and description.
      * `@returns` {{ code: string; message: string }} The structured error object.
      */
-    getMetadataError: (errorDetails: MetadataError): MetadataError => {
-      return {
-        code: errorDetails.code,
-        message: errorDetails.message,
-        // isClientError: true,
-      };
-    },
+    getMetadataError: (errorDetails: MetadataError): MetadataError => ({
+      code: errorDetails.code,
+      message: errorDetails.message,
+    }),
🤖 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 `@packages/davinci-client/src/lib/client.store.ts` around lines 459 - 471,
Remove the commented-out isClientError line from getMetadataError while
preserving the existing MetadataError fields and return behavior.
packages/davinci-client/src/lib/node.slice.ts (1)

195-198: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Prefer find over filter()[0].

Using find is slightly more efficient and idiomatic since it avoids allocating an intermediate array and stops iterating once a match is found.

♻️ Proposed refactor
-      const metadataCollector = collectors.filter(
+      const metadataCollector = collectors.find(
         (collector): collector is MetadataCollector => collector.type === 'MetadataCollector',
-      )[0];
+      );
🤖 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 `@packages/davinci-client/src/lib/node.slice.ts` around lines 195 - 198, Update
the metadata collector lookup in the surrounding node slice logic to use find
with the existing MetadataCollector type guard instead of filter()[0]. Preserve
the current result, including undefined when no matching collector exists.
🤖 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.

Inline comments:
In `@packages/davinci-client/src/lib/client.types.ts`:
- Around line 47-48: Update the CollectorValueTypes union in client.types.ts to
include Record<string, unknown> alongside MetadataError and the existing value
types, allowing standard object payloads for MetadataCollector updates.

In `@packages/davinci-client/src/lib/node.reducer.ts`:
- Around line 341-347: Update the MetadataCollector validation in the reducer to
explicitly reject null values in addition to non-objects and arrays before
assigning collector.input.value. Also harden the PhoneNumberCollector and
FidoRegistrationCollector validation paths against null before using the in
operator, preserving their existing validation behavior for valid objects.

---

Nitpick comments:
In `@packages/davinci-client/src/lib/client.store.ts`:
- Around line 459-471: Remove the commented-out isClientError line from
getMetadataError while preserving the existing MetadataError fields and return
behavior.

In `@packages/davinci-client/src/lib/node.slice.ts`:
- Around line 195-198: Update the metadata collector lookup in the surrounding
node slice logic to use find with the existing MetadataCollector type guard
instead of filter()[0]. Preserve the current result, including undefined when no
matching collector exists.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e7a845e5-8d09-49fc-ae39-e9e2f576e8e7

📥 Commits

Reviewing files that changed from the base of the PR and between 9549894 and d076641.

📒 Files selected for processing (17)
  • e2e/davinci-app/components/metadata.ts
  • e2e/davinci-app/main.ts
  • packages/davinci-client/api-report/davinci-client.api.md
  • packages/davinci-client/api-report/davinci-client.types.api.md
  • packages/davinci-client/src/lib/client.store.ts
  • packages/davinci-client/src/lib/client.types.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.api.ts
  • packages/davinci-client/src/lib/davinci.types.ts
  • packages/davinci-client/src/lib/davinci.utils.ts
  • packages/davinci-client/src/lib/node.reducer.test.ts
  • packages/davinci-client/src/lib/node.reducer.ts
  • packages/davinci-client/src/lib/node.slice.ts
  • packages/davinci-client/src/lib/node.types.test-d.ts
  • packages/davinci-client/src/lib/node.types.ts

Comment on lines +47 to +48
| FidoAuthenticationInputValue
| MetadataError;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Add Record<string, unknown> to CollectorValueTypes.

The CollectorValueTypes union defines the acceptable payloads for the updateCollectorValues action. Since the new MetadataCollector stores input values typed as Record<string, unknown>, this type must be explicitly included in the union alongside MetadataError. Without it, dispatching updates for a metadata collector using a standard object payload can result in TypeScript compilation errors.

💻 Proposed fix
-  | FidoAuthenticationInputValue
-  | MetadataError;
+  | FidoAuthenticationInputValue
+  | MetadataError
+  | Record<string, unknown>;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
| FidoAuthenticationInputValue
| MetadataError;
| FidoAuthenticationInputValue
| MetadataError
| Record<string, unknown>;
🤖 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 `@packages/davinci-client/src/lib/client.types.ts` around lines 47 - 48, Update
the CollectorValueTypes union in client.types.ts to include Record<string,
unknown> alongside MetadataError and the existing value types, allowing standard
object payloads for MetadataCollector updates.

Comment on lines +341 to +347

if (collector.type === 'MetadataCollector') {
if (typeof action.payload.value !== 'object' || Array.isArray(action.payload.value)) {
throw new Error('Value argument must be an object');
}
collector.input.value = action.payload.value as Record<string, unknown>;
}

@coderabbitai coderabbitai Bot Jul 20, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Prevent null values from bypassing the object validation.

In JavaScript, typeof null evaluates to 'object'. A null payload value will pass this validation check and be incorrectly assigned to collector.input.value as a Record<string, unknown>, which could cause native TypeErrors when downstream logic attempts to iterate over or spread the object.

Add an explicit null check. (Note: Other collectors like PhoneNumberCollector and FidoRegistrationCollector earlier in this reducer share a similar missing null check before using the in operator; consider hardening those as well).

🛡️ Proposed fix
       if (collector.type === 'MetadataCollector') {
-        if (typeof action.payload.value !== 'object' || Array.isArray(action.payload.value)) {
+        if (typeof action.payload.value !== 'object' || action.payload.value === null || Array.isArray(action.payload.value)) {
           throw new Error('Value argument must be an object');
         }
         collector.input.value = action.payload.value as Record<string, unknown>;
       }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (collector.type === 'MetadataCollector') {
if (typeof action.payload.value !== 'object' || Array.isArray(action.payload.value)) {
throw new Error('Value argument must be an object');
}
collector.input.value = action.payload.value as Record<string, unknown>;
}
if (collector.type === 'MetadataCollector') {
if (typeof action.payload.value !== 'object' || action.payload.value === null || Array.isArray(action.payload.value)) {
throw new Error('Value argument must be an object');
}
collector.input.value = action.payload.value as Record<string, unknown>;
}
🤖 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 `@packages/davinci-client/src/lib/node.reducer.ts` around lines 341 - 347,
Update the MetadataCollector validation in the reducer to explicitly reject null
values in addition to non-objects and arrays before assigning
collector.input.value. Also harden the PhoneNumberCollector and
FidoRegistrationCollector validation paths against null before using the in
operator, preserving their existing validation behavior for valid objects.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

may be valid if null can sneak in here. perhaps a !== null

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@ryanbas21, agreed. null can reach this runtime path and passes typeof value === 'object'; the condition should explicitly reject it:

if (
  typeof action.payload.value !== 'object' ||
  action.payload.value === null ||
  Array.isArray(action.payload.value)
) {
  throw new Error('Value argument must be an object');
}

That preserves valid record payloads while rejecting null and arrays.

@nx-cloud nx-cloud Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Important

At least one additional CI pipeline execution has run since the conclusion below was written and it may no longer be applicable.

Nx Cloud is proposing a fix for your failed CI:

We updated the transformActionRequest test to include formData: {} in the expected request payload, matching the new behavior introduced by this PR. The PR modified transformActionRequest in davinci.utils.ts to reduce non-action collectors into a formData object and include it in the request, but the test's expected value was not updated accordingly. This fix aligns the test with the current implementation so the assertion passes correctly.

Tip

We verified this fix by re-running @forgerock/davinci-client:test.

diff --git a/packages/davinci-client/src/lib/davinci.utils.test.ts b/packages/davinci-client/src/lib/davinci.utils.test.ts
index 094d4d9..f2f36eb 100644
--- a/packages/davinci-client/src/lib/davinci.utils.test.ts
+++ b/packages/davinci-client/src/lib/davinci.utils.test.ts
@@ -243,6 +243,7 @@ describe('transformActionRequest', () => {
         eventType: 'action',
         data: {
           actionKey: 'TEST_ACTION',
+          formData: {},
         },
       },
     };

Apply fix via Nx Cloud  Reject fix via Nx Cloud


Or Apply changes locally with:

npx nx-cloud apply-locally nPN3-s8Wg

Apply fix locally with your editor ↗   View interactive diff ↗



🎓 Learn more about Self-Healing CI on nx.dev

@ancheetah
ancheetah force-pushed the SDKS-5100-metadata-collector branch from d076641 to b8c285f Compare July 20, 2026 20:06
@pkg-pr-new

pkg-pr-new Bot commented Jul 20, 2026

Copy link
Copy Markdown

Open in StackBlitz

@forgerock/davinci-client

pnpm add https://pkg.pr.new/@forgerock/davinci-client@727

@forgerock/device-client

pnpm add https://pkg.pr.new/@forgerock/device-client@727

@forgerock/journey-client

pnpm add https://pkg.pr.new/@forgerock/journey-client@727

@forgerock/oidc-client

pnpm add https://pkg.pr.new/@forgerock/oidc-client@727

@forgerock/protect

pnpm add https://pkg.pr.new/@forgerock/protect@727

@forgerock/sdk-types

pnpm add https://pkg.pr.new/@forgerock/sdk-types@727

@forgerock/sdk-utilities

pnpm add https://pkg.pr.new/@forgerock/sdk-utilities@727

@forgerock/iframe-manager

pnpm add https://pkg.pr.new/@forgerock/iframe-manager@727

@forgerock/sdk-logger

pnpm add https://pkg.pr.new/@forgerock/sdk-logger@727

@forgerock/sdk-oidc

pnpm add https://pkg.pr.new/@forgerock/sdk-oidc@727

@forgerock/sdk-request-middleware

pnpm add https://pkg.pr.new/@forgerock/sdk-request-middleware@727

@forgerock/storage

pnpm add https://pkg.pr.new/@forgerock/storage@727

commit: b8c285f

@codecov-commenter

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 63.26531% with 18 lines in your changes missing coverage. Please review.
✅ Project coverage is 23.20%. Comparing base (eafe277) to head (b8c285f).
⚠️ Report is 48 commits behind head on main.

Files with missing lines Patch % Lines
packages/davinci-client/src/lib/davinci.utils.ts 50.00% 8 Missing ⚠️
packages/davinci-client/src/lib/client.store.ts 16.66% 5 Missing ⚠️
packages/davinci-client/src/lib/davinci.api.ts 0.00% 5 Missing ⚠️

❌ Your project status has failed because the head coverage (23.20%) 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     #727      +/-   ##
==========================================
+ Coverage   18.07%   23.20%   +5.12%     
==========================================
  Files         155      161       +6     
  Lines       24398    25645    +1247     
  Branches     1203     1624     +421     
==========================================
+ Hits         4410     5950    +1540     
+ Misses      19988    19695     -293     
Files with missing lines Coverage Δ
packages/davinci-client/src/lib/client.types.ts 100.00% <ø> (ø)
packages/davinci-client/src/lib/collector.types.ts 100.00% <ø> (ø)
packages/davinci-client/src/lib/collector.utils.ts 86.85% <100.00%> (+1.70%) ⬆️
packages/davinci-client/src/lib/davinci.types.ts 100.00% <ø> (ø)
packages/davinci-client/src/lib/node.reducer.ts 73.79% <100.00%> (+3.30%) ⬆️
packages/davinci-client/src/lib/node.slice.ts 76.95% <100.00%> (+0.25%) ⬆️
packages/davinci-client/src/lib/node.types.ts 100.00% <ø> (ø)
packages/davinci-client/src/lib/client.store.ts 25.60% <16.66%> (+25.32%) ⬆️
packages/davinci-client/src/lib/davinci.api.ts 29.65% <0.00%> (+17.68%) ⬆️
packages/davinci-client/src/lib/davinci.utils.ts 86.41% <50.00%> (-3.53%) ⬇️

... and 13 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 4b804d1 to https://ForgeRock.github.io/ping-javascript-sdk/pr-727/4b804d1288f8b88912a04e24ee66ecdc394a5b75 branch gh-pages in ForgeRock/ping-javascript-sdk

@github-actions

Copy link
Copy Markdown
Contributor

📦 Bundle Size Analysis

📦 Bundle Size Analysis

🆕 New Packages

🆕 @forgerock/device-client - 10.0 KB (new)
🆕 @forgerock/device-client - 0.0 KB (new)
🆕 @forgerock/journey-client - 92.6 KB (new)
🆕 @forgerock/journey-client - 0.0 KB (new)

📊 Minor Changes

📈 @forgerock/davinci-client - 55.2 KB (+0.8 KB)

➖ No Changes

@forgerock/sdk-types - 9.1 KB
@forgerock/protect - 144.6 KB
@forgerock/sdk-request-middleware - 4.6 KB
@forgerock/iframe-manager - 3.2 KB
@forgerock/sdk-logger - 1.6 KB
@forgerock/sdk-oidc - 5.7 KB
@forgerock/storage - 1.5 KB
@forgerock/oidc-client - 35.3 KB
@forgerock/sdk-utilities - 18.6 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.

3 participants