Skip to content

SDKS-4477 Refactor FIDO2 collectors to use ActionKeyProvider for error handling and update related tests - #230

Merged
spetrov merged 7 commits into
developfrom
SDKS-4477
Aug 7, 2026
Merged

SDKS-4477 Refactor FIDO2 collectors to use ActionKeyProvider for error handling and update related tests#230
spetrov merged 7 commits into
developfrom
SDKS-4477

Conversation

@vibhorgoswami

@vibhorgoswami vibhorgoswami commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

JIRA Ticket

SDKS-4477

Description

  • Add a Failable interface so collectors can report a DOM/credential
    error alongside their payload.
  • AbstractFidoCollector now maps CreateCredential/GetCredential exceptions (cancellation, unsupported, DOM errors) to their corresponding error names and switches its event type from "submit" to "action" when an error is present, allowing the Journey server to handle FIDO2 failures without treating them as a generic submit failure.

Summary by CodeRabbit

  • New Features
    • FIDO2 failures now generate actionable “action” events with WebAuthn-style error details.
    • Collector events can use action keys independently from submitted form data.
  • Bug Fixes
    • Collector responses more reliably distinguish action and submit events while preserving payloads.
    • FIDO authentication and registration clear stale error or credential state between attempts.
    • Sample FIDO flows continue correctly after failures.
  • Tests
    • Expanded coverage for event selection, serialization, error handling, and state resets.

@vibhorgoswami vibhorgoswami self-assigned this Jul 23, 2026
@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

FIDO collectors now map failures to WebAuthn error codes, expose them as DaVinci action events, and reset error state across lifecycle operations. Collector JSON serialization separates action keys from form data. Sample FIDO flows advance through onNext after failures.

Changes

FIDO failure state propagation

Layer / File(s) Summary
Collector contract and serialization
foundation/davinci-plugin/..., mfa/fido/.../Constants.kt, davinci/.../collector/*
Adds ActionKeyProvider, action event support, collector action keys, and updated event-type and JSON serialization behavior.
Collector serialization validation
davinci/src/test/.../CollectorsTest.kt
Tests event selection, request interceptor ordering, action-key handling, form-data mapping, and JSON value serialization.
FIDO error mapping and lifecycle
mfa/fido/src/main/.../davinci/*Collector.kt
Maps FIDO exceptions to error identifiers, exposes error state through actionKey, returns an empty error payload, and clears state during initialization and closing.
FIDO failure behavior validation
mfa/fido/src/test/.../davinci/*Test.kt
Covers exception mappings, action transitions, cancellation behavior, error payloads, retry cleanup, and lifecycle resets.
Sample FIDO flow wiring
samples/pingsampleapp/.../collector/*
Removes error dialogs and routes authentication and registration failures directly to onNext.

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

Sequence Diagram(s)

sequenceDiagram
  participant FidoCollector
  participant FidoClient
  participant Collectors
  FidoCollector->>FidoClient: Start FIDO operation
  FidoClient-->>FidoCollector: Return success or exception
  FidoCollector->>FidoCollector: Map exception to errorCode
  Collectors->>FidoCollector: Read eventType, actionKey, and payload
  FidoCollector-->>Collectors: Return submit or action JSON data
Loading

Possibly related PRs

Suggested reviewers: spetrov

Poem

A rabbit maps each error code,
Then sends the action down the road.
Empty payloads mark the state,
Reset before the next attempt.
onNext hops through the flow.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 6.38% 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
Title check ✅ Passed The title clearly identifies the FIDO2 collector refactor, ActionKeyProvider adoption, error handling, and related tests.
Description check ✅ Passed The description includes the required JIRA Ticket and Description sections and explains the FIDO2 error-handling changes.
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
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch SDKS-4477

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.

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

Actionable comments posted: 1

Caution

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

⚠️ Outside diff range comments (1)
davinci/src/main/kotlin/com/pingidentity/davinci/collector/Collectors.kt (1)

22-35: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

eventType() and asJson() can disagree on which collector's action/error wins.

eventType() returns on the FIRST Submittable match (payload or Failable error), but asJson()'s loop processes ALL collectors and lets the LAST matching one overwrite actionKey. If two collectors simultaneously have non-null payload/error (e.g. a failed FIDO collector plus another Submittable/Flow collector with payload), the reported eventType and the actionKey sent to the server can point to different collectors — undermining the goal of correctly surfacing FIDO2 failures as action events.

♻️ Suggested approach: unify the selection logic
 internal fun Collectors.eventType(): String? {
-    forEach {
-        when (it) {
-            is Submittable -> {
-                val eventType = it.eventType()
-                if (it.payload() != null || (it is Failable && it.error() != null)) {
-                    return eventType
-                }
-            }
-            else -> {}
-        }
-    }
-    return null
+    return firstNotNullOfOrNull { it.actingSubmittableOrNull() }?.eventType()
 }

+private fun Collector<*>.actingSubmittableOrNull(): Submittable? =
+    (this as? Submittable)?.takeIf { payload() != null || (this@actingSubmittableOrNull is Failable && (this@actingSubmittableOrNull as Failable).error() != null) }

Then have asJson() locate the same "acting" collector once (e.g. collectors.firstOrNull { ... }) and use it for actionKey, instead of letting every matching collector overwrite it in the loop.

Also applies to: 58-78

🤖 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 `@davinci/src/main/kotlin/com/pingidentity/davinci/collector/Collectors.kt`
around lines 22 - 35, Unify collector selection between Collectors.eventType()
and asJson(): identify the first Submittable whose payload is non-null or whose
Failable error is non-null, and use that same collector for actionKey. Update
asJson() to select this acting collector once rather than allowing later
matching collectors to overwrite the value, while preserving existing
serialization for all other collectors.
🤖 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
`@mfa/fido/src/main/kotlin/com/pingidentity/fido/davinci/AbstractFidoCollector.kt`:
- Line 68: Restrict the visibility of AbstractFidoCollector.error by adding a
private setter, matching the existing key, label, trigger, and required
properties. Change handleError from public to internal so only same-module
subclasses can invoke the exception-mapping logic, while preserving its current
behavior.

---

Outside diff comments:
In `@davinci/src/main/kotlin/com/pingidentity/davinci/collector/Collectors.kt`:
- Around line 22-35: Unify collector selection between Collectors.eventType()
and asJson(): identify the first Submittable whose payload is non-null or whose
Failable error is non-null, and use that same collector for actionKey. Update
asJson() to select this acting collector once rather than allowing later
matching collectors to overwrite the value, while preserving existing
serialization for all other collectors.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: e1264a46-22a3-47b9-a75b-e6a9f974281f

📥 Commits

Reviewing files that changed from the base of the PR and between 5f1e47f and 96972f1.

📒 Files selected for processing (10)
  • davinci/src/main/kotlin/com/pingidentity/davinci/collector/Collectors.kt
  • davinci/src/test/kotlin/com/pingidentity/davinci/collector/CollectorsTest.kt
  • foundation/davinci-plugin/src/main/kotlin/com/pingidentity/davinci/plugin/Failable.kt
  • mfa/fido/src/main/kotlin/com/pingidentity/fido/Constants.kt
  • mfa/fido/src/main/kotlin/com/pingidentity/fido/davinci/AbstractFidoCollector.kt
  • mfa/fido/src/main/kotlin/com/pingidentity/fido/davinci/FidoAuthenticationCollector.kt
  • mfa/fido/src/main/kotlin/com/pingidentity/fido/davinci/FidoRegistrationCollector.kt
  • mfa/fido/src/test/kotlin/com/pingidentity/fido/davinci/AbstractFidoCollectorTest.kt
  • mfa/fido/src/test/kotlin/com/pingidentity/fido/davinci/FidoAuthenticationCollectorTest.kt
  • mfa/fido/src/test/kotlin/com/pingidentity/fido/davinci/FidoRegistrationCollectorTest.kt

Comment thread mfa/fido/src/main/kotlin/com/pingidentity/fido/davinci/AbstractFidoCollector.kt Outdated
@vibhorgoswami

Copy link
Copy Markdown
Contributor Author

@coderabbitai summary

@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Summary regeneration triggered.

@vibhorgoswami vibhorgoswami changed the title SDKS-4477 SDKS-4477 Enhance FIDO2 collectors with error handling and introduce Failable interface SDKS-4477 Refactor FIDO2 collectors to use ActionKeyProvider for error handling and update related tests Jul 23, 2026
@vibhorgoswami
vibhorgoswami marked this pull request as ready for review July 27, 2026 18:35
@codecov

codecov Bot commented Jul 27, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 0.00%. Comparing base (04c8a6b) to head (2651753).

Additional details and impacted files
@@      Coverage Diff       @@
##   develop   #230   +/-   ##
==============================
==============================

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

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

@vahancouver vahancouver 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 with some comments

Comment thread mfa/fido/src/main/kotlin/com/pingidentity/fido/davinci/AbstractFidoCollector.kt Outdated
Comment thread mfa/fido/src/main/kotlin/com/pingidentity/fido/davinci/AbstractFidoCollector.kt Outdated
@vibhorgoswami

Copy link
Copy Markdown
Contributor Author

@coderabbitai walkthrough

@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

repo="ForgeRock/ping-android-sdk"
pr="230"

gh pr view "$pr" --repo "$repo" --json title,body,files \
  --jq '.title, (.files[] | "\(.path) (+\(.additions)/-\(.deletions))")'

gh pr diff "$pr" --repo "$repo" -- \
  mfa/fido/src/main/kotlin/com/pingidentity/fido/davinci/AbstractFidoCollector.kt \
  davinci/src/main/kotlin/com/pingidentity/davinci/collector/Collectors.kt \
  mfa/fido/src/main/kotlin/com/pingidentity/fido/davinci/FidoAuthenticationCollector.kt \
  mfa/fido/src/main/kotlin/com/pingidentity/fido/davinci/FidoRegistrationCollector.kt \
  foundation/davinci-plugin/src/main/kotlin/com/pingidentity/davinci/plugin/ActionKeyProvider.kt \
  samples/pingsampleapp/src/main/java/com/pingidentity/samples/pingsampleapp/davinci/collector/FidoAuthentication.kt \
  samples/pingsampleapp/src/main/java/com/pingidentity/samples/pingsampleapp/davinci/collector/FidoRegistration.kt

Length of output: 2642


🏁 Script executed:

#!/bin/bash
set -euo pipefail

files=(
  "davinci/src/main/kotlin/com/pingidentity/davinci/collector/Collectors.kt"
  "foundation/davinci-plugin/src/main/kotlin/com/pingidentity/davinci/plugin/ActionKeyProvider.kt"
  "mfa/fido/src/main/kotlin/com/pingidentity/fido/davinci/AbstractFidoCollector.kt"
  "mfa/fido/src/main/kotlin/com/pingidentity/fido/davinci/FidoAuthenticationCollector.kt"
  "mfa/fido/src/main/kotlin/com/pingidentity/fido/davinci/FidoRegistrationCollector.kt"
  "samples/pingsampleapp/src/main/java/com/pingidentity/samples/pingsampleapp/davinci/collector/DaVinciContinueNode.kt"
  "samples/pingsampleapp/src/main/java/com/pingidentity/samples/pingsampleapp/davinci/collector/FidoAuthentication.kt"
  "samples/pingsampleapp/src/main/java/com/pingidentity/samples/pingsampleapp/davinci/collector/FidoRegistration.kt"
)

for file in "${files[@]}"; do
  echo "===== OUTLINE: $file ====="
  ast-grep outline "$file" --items all
done

for file in "${files[@]}"; do
  echo "===== SOURCE: $file ====="
  cat -n "$file"
done

Length of output: 51428


@vibhorgoswami Here’s the walkthrough for #230:

What changes conceptually

Previously, FIDO2 failures stayed in the sample app’s local error/retry UI. This PR converts a FIDO failure into a DaVinci action so the Journey server can decide the next step based on the WebAuthn error name.

sequenceDiagram
    participant UI as Sample FIDO UI
    participant Collector as FIDO Collector
    participant CM as Android Credential Manager
    participant DaVinci as DaVinci request serializer
    participant Server as Journey server

    UI->>Collector: authenticate() / register()
    Collector->>CM: Perform FIDO2 ceremony
    alt Success
        CM-->>Collector: Assertion / attestation
        UI->>DaVinci: onNext()
        DaVinci->>Server: submit + formData
    else FIDO failure
        CM-->>Collector: Credential or DOM exception
        Collector->>Collector: Map exception to errorCode
        UI->>DaVinci: onNext()
        DaVinci->>Server: action + actionKey=<DOM error name>
    end
Loading

Key implementation pieces

  • New ActionKeyProvider contract
    foundation/davinci-plugin/.../ActionKeyProvider.kt adds an optional actionKey. This lets a collector contribute a workflow action directly instead of regular formData.

  • FIDO errors become action keys
    AbstractFidoCollector now implements ActionKeyProvider. Its errorCode is exposed as actionKey, and determines the event type:

    • no error → submit
    • error present → action
  • Exception normalization
    handleError() maps Credential Manager failures to WebAuthn-compatible names:

    • unsupported operation → NotSupportedError
    • credential cancellation → NotAllowedError
    • credential DOM exceptions → the underlying DOM exception class name
    • other failures → UnknownError
    • coroutine CancellationException is rethrown rather than translated.
  • Empty-payload sentinel enables the action path
    On an error, FIDO collectors return an empty JsonObject from payload(). Collectors.eventType() only chooses collectors with non-null payloads, so this deliberately makes the failed FIDO collector eligible to drive the continuation.

  • Serialization behavior
    Collectors.asJson() writes an ActionKeyProvider’s non-null key to the top-level actionKey. It does not add the empty error payload to formData. Successful FIDO operations continue to put assertion or attestation data in formData.

  • UI now advances on failure too
    The sample FidoAuthentication and FidoRegistration composables log a failure and call onNext(). That submits the action to the Journey server rather than showing the former local retry dialog.

  • State is reset safely
    errorCode is cleared during collector initialization, before each FIDO operation, and in close(), preventing an earlier error from affecting a later ceremony.

Test coverage

The PR adds focused coverage for collector serialization/event selection plus FIDO error mappings, cancellation propagation, error payload behavior, action-key emission, and reset behavior after init() and close().

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

Changes look good to me. I also performed a few quick tests.
Claude review however flagged the following as "blocking"... TBH, I don't quite understand these, but @witrisna, can you please review and see if these worth our attention?

  • CancellationException rethrow inside Result.onFailure will crash the coroutine scope silently. handleError() rethrows CancellationException, but it's called from inside an onFailure { handleError(exception) } lambda.
    Kotlin's Result.onFailure doesn't protect against rethrows — the CancellationException propagates up the coroutine chain and cancels the parent scope rather than surfacing as a failure. The unit test handleError should rethrow CancellationException validates the raw method in isolation, but no test runs the full authenticate()/register() path with a CancellationException.
    Fix: guard against it at the call-site (before invoking handleError) rather than rethrowing from within the lambda.
  • eventType()/asJson() coherence risk if errorCode is non-null but payload() returns null. If actionKey is set (non-null) while payload() still returns null (e.g., external mutation before authenticate() runs), eventType() returns null via the payload() != null check, but actionKey is still emitted in asJson() — producing a JSON payload with actionKey but no eventType.
    This is gated on the errorCode var being public-mutable (which CodeRabbit has already flagged but is still unresolved...)

@witrisna

Copy link
Copy Markdown
Contributor

Changes look good to me. I also performed a few quick tests. Claude review however flagged the following as "blocking"... TBH, I don't quite understand these, but @witrisna, can you please review and see if these worth our attention?

  • CancellationException rethrow inside Result.onFailure will crash the coroutine scope silently. handleError() rethrows CancellationException, but it's called from inside an onFailure { handleError(exception) } lambda.
    Kotlin's Result.onFailure doesn't protect against rethrows — the CancellationException propagates up the coroutine chain and cancels the parent scope rather than surfacing as a failure. The unit test handleError should rethrow CancellationException validates the raw method in isolation, but no test runs the full authenticate()/register() path with a CancellationException.
    Fix: guard against it at the call-site (before invoking handleError) rather than rethrowing from within the lambda.
  • eventType()/asJson() coherence risk if errorCode is non-null but payload() returns null. If actionKey is set (non-null) while payload() still returns null (e.g., external mutation before authenticate() runs), eventType() returns null via the payload() != null check, but actionKey is still emitted in asJson() — producing a JSON payload with actionKey but no eventType.
    This is gated on the errorCode var being public-mutable (which CodeRabbit has already flagged but is still unresolved...)

For the CancellationException, it will be good to add test cases for authenticate() and register() functions.
For the eventType/asJson(), under discussion, yes, the logic looks a bit weird, see if we can improve it.

…oved eventType handling and error management

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

Actionable comments posted: 1

🤖 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 `@davinci/src/main/kotlin/com/pingidentity/davinci/collector/Collectors.kt`:
- Around line 58-62: Update the collector selection flow in Collectors so the
first eligible Submittable collector is selected once and reused for both
eventType() and actionKey, rather than overwriting actionKey while iterating
through later ActionKeyProvider instances. Preserve payload collection for
eligible providers, and add a regression test covering two eligible action-key
collectors to verify both fields come from the selected collector.
🪄 Autofix

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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 7beadc4d-c55f-4577-a14b-4022fc43e1f2

📥 Commits

Reviewing files that changed from the base of the PR and between 7153294 and 809af5f.

📒 Files selected for processing (9)
  • davinci/src/main/kotlin/com/pingidentity/davinci/collector/Collectors.kt
  • davinci/src/main/kotlin/com/pingidentity/davinci/collector/FlowCollector.kt
  • davinci/src/main/kotlin/com/pingidentity/davinci/collector/MetadataCollector.kt
  • davinci/src/main/kotlin/com/pingidentity/davinci/collector/SubmitCollector.kt
  • davinci/src/test/kotlin/com/pingidentity/davinci/collector/CollectorsTest.kt
  • mfa/fido/src/main/kotlin/com/pingidentity/fido/davinci/FidoAuthenticationCollector.kt
  • mfa/fido/src/main/kotlin/com/pingidentity/fido/davinci/FidoRegistrationCollector.kt
  • mfa/fido/src/test/kotlin/com/pingidentity/fido/davinci/FidoAuthenticationCollectorTest.kt
  • mfa/fido/src/test/kotlin/com/pingidentity/fido/davinci/FidoRegistrationCollectorTest.kt

Comment thread davinci/src/main/kotlin/com/pingidentity/davinci/collector/Collectors.kt Outdated
…rror codes and enhance ActionKeyProvider documentation
@vibhorgoswami
vibhorgoswami requested a review from spetrov August 5, 2026 23:03
@vibhorgoswami

Copy link
Copy Markdown
Contributor Author

@coderabbitai summary

@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Summary regeneration triggered.


return buildJsonObject {
val map = mutableMapOf<String, Any>()
var actionKeySet = false

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

actionKeySet is a "first-wins guard" that only protects the fallback ActionKeyProvider branch (the third when case)

Priority Branch Behaviour
1st SubmitCollector / FlowCollector Always writes actionKey (no guard check)
2nd MetadataCollector Always writes actionKey (no guard check)
3rd Any other ActionKeyProvider (e.g. FIDO error) Writes actionKey only if not already set

@vibhorgoswami
vibhorgoswami requested a review from witrisna August 6, 2026 21:33
is Submittable -> {
val eventType = it.eventType()
it.payload()?.let {
if (it.payload() != null) {

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.

Any reason to make this change?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I don't see this change anymore in my code.

* @return the event type string, or null if no matching collector is found.
*/
internal fun Collectors.eventType(): String? {
// First pass: honor explicit Submit/Flow actions.

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.

I believe this is same logic, but clearer

internal fun Collectors.eventType(): String? {
    // First pass: honor explicit Submit/Flow actions.
    forEach {
        if ((it is SubmitCollector || it is FlowCollector) && it.payload() != null) {
            return it.eventType()
        }
    }
    // Second pass: fall back to any Submittable with a payload (e.g. FIDO errors).
    forEach {
        if (it is Submittable && it.payload() != null) {
            return it.eventType()
        }
    }
    return null
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Hey Andy, thanks for approving the PR. I asked Claude to give me a clarification on what the difference is between the proposed code and the current code.

  • The current code (actionKey != null) makes the intent explicit — first pass is about the user having made a selection, which is exactly what actionKey signals.
  • The proposed code (payload() != null) is less intent-revealing — it could be confused with the second pass, which uses the same condition but on any Submittable.

The iOS PR specifically introduced actionKey on SubmitCollector/FlowCollector to make this distinction clear — checking actionKey != null is the idiomatic iOS way, and we should mirror that in Android for consistency.

@spetrov
spetrov merged commit 6bc51d1 into develop Aug 7, 2026
1 of 2 checks passed
@spetrov
spetrov deleted the SDKS-4477 branch August 7, 2026 16:22
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.

4 participants