Skip to content

Create WorkflowTriggerType and WorkflowTriggerActionType#3386

Merged
vegaro merged 14 commits into
mainfrom
cesar/add-enums-for-types
Apr 27, 2026
Merged

Create WorkflowTriggerType and WorkflowTriggerActionType#3386
vegaro merged 14 commits into
mainfrom
cesar/add-enums-for-types

Conversation

@vegaro

@vegaro vegaro commented Apr 24, 2026

Copy link
Copy Markdown
Member
  • Added WorkflowTriggerActionType and WorkflowTriggerType. Even though they only have one type now, it makes things cleaner.
  • Added the type to triggerAction so the Button can be the one passing the trigger type to the navigator and allow for different types of triggers. For now it's "on_press", but we could add other types, imagine "on_long_press"

Note

Medium Risk
Changes workflow JSON deserialization shapes and the triggerAction API, which could affect navigation if callers or backend payloads don’t match the new typed expectations. Risk is mitigated by explicit UNKNOWN fallbacks and added unit tests for forward-compatibility.

Overview
Workflow triggers/actions are now strongly typed and forward-compatible. WorkflowTrigger.type is changed from a String to a WorkflowTriggerType enum (currently ON_PRESS + UNKNOWN) with a defaulting deserializer for unknown values.

WorkflowTriggerAction is refactored from a plain data class into a sealed type (Step + Unknown) using a deserializer that safely falls back when the JSON is missing/invalid or the action type is unrecognized. WorkflowNavigator.triggerAction now requires a triggerType and only navigates when the resolved action is a Step; tests were updated and new deserialization tests were added to lock in the unknown-value behavior (plus a small assertion-style cleanup in FlexDistributionTests).

Reviewed by Cursor Bugbot for commit 710e4a7. Bugbot is set up for automated code reviews on this repo. Configure here.

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

Just a suggestion in the modeling, considering this is mostly internal. Lmk what you think!


@InternalRevenueCatAPI
@Serializable
public data class WorkflowTriggerAction(

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.

Considering this is internal, I wonder if we should represent this as a sealed class, since stepId should probably only exist when action type is step... Maybe the same thing could be applied to WorkflowTrigger, not sure?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I like it, I need to change the deserializer but I think it's worth it

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

For WorkflowTrigger, every trigger type carries the same fields (name, actionId, componentId), so a sealed class would look like:

sealed class WorkflowTrigger {
    data class OnPress(val name: String, val actionId: String, val componentId: String) : WorkflowTrigger()
    object Unknown : WorkflowTrigger()
}

The only win is that Unknown lives in the hierarchy rather than as an enum value. But no nullability removed in this case, so not sure if it's worth it?

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 guess it indeed depends on whether we think other parameters might be added in the future that are specific to only one type... if not, it can indeed be kept as is IMO.

@codecov

codecov Bot commented Apr 24, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 88.88889% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 79.36%. Comparing base (1bdaa5a) to head (a052e6b).

Files with missing lines Patch % Lines
...uecat/purchases/common/workflows/WorkflowModels.kt 85.71% 1 Missing and 1 partial ⚠️
...ls/serializers/PolymorphicSerializerWithDefault.kt 92.30% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3386      +/-   ##
==========================================
+ Coverage   79.32%   79.36%   +0.03%     
==========================================
  Files         361      362       +1     
  Lines       14448    14473      +25     
  Branches     1964     1969       +5     
==========================================
+ Hits        11461    11486      +25     
+ Misses       2194     2192       -2     
- Partials      793      795       +2     

☔ View full report in Codecov by Sentry.
📢 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.

@vegaro vegaro requested a review from a team as a code owner April 24, 2026 13:34

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 36cb7bc. Configure here.

@vegaro vegaro requested review from facumenzella and tonidero April 24, 2026 15:29

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

Looking good! Just one question on the new serializer utility, in case it can be simplified.

baseClass: KClass<T>,
private val unknownSerializer: DeserializationStrategy<T>,
private val typeField: String = "type",
private val serializers: Map<String, Pair<String, DeserializationStrategy<T>>> = emptyMap(),

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 see we have the selectByType available for this cases, but I'm wondering if we should use something like Map<String, Pair<List<String>, DeserializationStrategy<T>>> so we support multiple required parameters without having to override the other method?

I wonder if we should just not check required parameters at all, and just have the serializer try to perform the deserialization, and add a try-catch over serialization exceptions, so if it fails, we fallback to the unknownSerializer? This would be much simpler IMO...

I'm not sure if we want to have more customizable serializers in follow-up PRs that would explain this though, so lmk in that case :)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I might be overengineering this tbh. Went a bit in circles trying to make it generic so we could reuse it in the future but all versions are a bit meh. Are you fine with a custom serializer for WorkflowTriggerAction? that will be cleaner imho

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I've removed this in b0462e0

Wdyt? I think it's more readable and feels what we should be doing for now

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.

Right... I think this is something that could be useful down the line... but until we have other cases where this is needed, I'm ok keeping it focused to what we need right now.

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

Looks great!

@vegaro vegaro enabled auto-merge April 27, 2026 09:52
@vegaro vegaro added this pull request to the merge queue Apr 27, 2026
Merged via the queue into main with commit 4105c59 Apr 27, 2026
35 of 36 checks passed
@vegaro vegaro deleted the cesar/add-enums-for-types branch April 27, 2026 10:25
matteinn pushed a commit to matteinn/purchases-android that referenced this pull request May 5, 2026
**This is an automatic release.**

## RevenueCat SDK
### ✨ New Features
* Unified StoreReplacementMode API (RevenueCat#3234) via Will Taylor
(@fire-at-will)
* Add placement and targeting context to paywall events (RevenueCat#3253) via Dan
Pannasch (@dpannasch)
### 🐞 Bugfixes
* Fix null Placements when offering_ids_by_placement is absent (RevenueCat#3254)
via Dan Pannasch (@dpannasch)

## RevenueCatUI SDK
### Paywallv2
#### ✨ New Features
* Wire multipage workflow navigation into PaywallViewModel (RevenueCat#3381) via
Cesar de la Vega (@vegaro)

### 🔄 Other Changes
* Add `triggerType` to `WorkflowTrigger` (RevenueCat#3393) via Cesar de la Vega
(@vegaro)
* Extract private function `NavigateTo.toPaywallAction` (RevenueCat#3392) via
Cesar de la Vega (@vegaro)
* Bump revenucatui-tests gradle cache key (RevenueCat#3391) via Toni Rico
(@tonidero)
* Create `WorkflowTriggerType` and `WorkflowTriggerActionType` (RevenueCat#3386)
via Cesar de la Vega (@vegaro)
* Update baseline profiles (RevenueCat#3390) via RevenueCat Git Bot (@RCGitBot)
* Plumb `componentId` through buttons on workflow interactions (RevenueCat#3380)
via Cesar de la Vega (@vegaro)
* Add `ButtonComponent.Action.Workflow` (RevenueCat#3385) via Cesar de la Vega
(@vegaro)
* Add `componentId` to `ButtonCoomponentStyle` (RevenueCat#3384) via Cesar de la
Vega (@vegaro)
* Migrate all suspendCoroutine usages to suspendCancellableCoroutine
(RevenueCat#3365) via Jaewoong Eum (@skydoves)
* Add `WorkflowNavigator` for multipage workflow step navigation (RevenueCat#3379)
via Cesar de la Vega (@vegaro)
* build(deps): bump fastlane-plugin-revenuecat_internal from `b822f01`
to `d24ab26` (RevenueCat#3383) via dependabot[bot] (@dependabot[bot])
* Add `id` field to `ButtonComponent` (RevenueCat#3377) via Cesar de la Vega
(@vegaro)
* Add CI workflows for generating Baseline Profiles (RevenueCat#3372) via Jaewoong
Eum (@skydoves)
* add min sdk level for paywalls and customer center (RevenueCat#2465) via
Muhammad-Sharif Moustafa (@mshmoustafa)

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Low risk release bookkeeping: updates version strings, changelogs, and
documentation deployment targets without changing runtime logic beyond
the reported version identifier.
> 
> **Overview**
> Cuts the `10.3.0` release by updating all version references from
`10.3.0-SNAPSHOT` to `10.3.0` across build config (`gradle.properties`,
`.version`, `Config.frameworkVersion`) and sample/test app dependency
pins.
> 
> Updates release documentation publishing to sync Dokka output to the
`10.3.0` S3 path and changes `docs/index.html` to redirect to `10.3.0`.
Also promotes release notes by moving `CHANGELOG.latest.md` entries into
a new `CHANGELOG.md` section for `10.3.0`.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
056ce62. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
facumenzella added a commit that referenced this pull request Jun 12, 2026
Adds a missing WorkflowNavigatorTest case verifying triggerAction returns
null and does not navigate when the componentId matches but the
WorkflowTriggerType does not. Suggested during review of #3386.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants