Skip to content

Add network scaffolding for remote config endpoint#3435

Merged
tonidero merged 7 commits into
mainfrom
toniricodiez/add-remote-config-network-scaffolding
May 8, 2026
Merged

Add network scaffolding for remote config endpoint#3435
tonidero merged 7 commits into
mainfrom
toniricodiez/add-remote-config-network-scaffolding

Conversation

@tonidero
Copy link
Copy Markdown
Contributor

@tonidero tonidero commented May 5, 2026

Motivation

First step in landing remote-config support. The backend exposes GET /v2/config, which returns a manifest describing remote-controlled topics (e.g. product-entitlement mapping) plus the asset sources for downloading them. Wiring up the endpoint and its response models is a prerequisite for the orchestration and on-disk cache work in the rest of the stack.

Description

  • Adds Endpoint.GetRemoteConfig to the sealed endpoint hierarchy and Backend.getRemoteConfig(appUserID, appInBackground, onSuccess, onError) to dispatch the call. Reuses the existing BackgroundAwareCallbackCacheKey / RemoteConfigCallback callback-coalescing pattern.
  • Adds the response models in a new feature package com.revenuecat.purchases.common.remoteconfig:
    • RemoteConfigResponse(apiSources, blobSources, manifest)
    • ApiSource(id, url, priority, weight)
    • BlobSource(id, urlFormat, priority, weight)
    • Manifest(topics)
    • Topic enum (PRODUCT_ENTITLEMENT_MAPPING) with fromKey(key) for wire-key lookups
    • TopicEntry(blobRef)
    • TopicsMapSerializer — custom kotlinx-serialization KSerializer that drops unknown topic wire keys at decode time and re-emits the enum's wire key on encode.
  • Test coverage for the serializer (unknown-key filtering, default decoding, round-trip), plus BackendGetRemoteConfigTest for the endpoint plumbing and error paths.
  • Adds a developer-only build-time override: setting REMOTE_CONFIG_BASE_URL in local.properties (e.g. http://localhost:8080/) redirects only getRemoteConfig to that host. Empty by default and gated to debug builds, so CI / release traffic is unaffected. Plumbed via a new BuildConfig.REMOTE_CONFIG_BASE_URL field, mirroring the existing ENABLE_EXTRA_REQUEST_LOGGING pattern.

All new types are internal; no public API surface change.

Stack

Checklist

  • Unit tests
  • Follow-up issues for purchases-ios / hybrids (deferred — feature is not yet wired to any consumer)

Note

Medium Risk
Adds new backend networking path (GET /v2/config) and callback coalescing logic in Backend, plus a debug-only base URL override; issues here could affect request routing or parsing for this new call.

Overview
Adds initial remote-config networking support by introducing Endpoint.GetRemoteConfig (/v2/config) and a new Backend.getRemoteConfig call that coalesces concurrent requests and parses a typed RemoteConfigResponse.

Introduces internal remote-config response models (including a custom serializer that drops unknown topic keys) and adds unit tests covering endpoint properties, parsing/unknown-field behavior, error propagation, and request de-duping.

Adds a debug-only REMOTE_CONFIG_BASE_URL BuildConfig field (documented in local.properties.example) to optionally route only the remote-config GET request to a local host.

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

Adds GET /v1/subscribers/{appUserId}/config to fetch SDK configuration
(api_sources, asset_sources, manifest of asset topics) that will be
consumed by future PRs.

Includes:
- Endpoint.GetRemoteConfig with signing+nonce and fallback URL support
- @serializable RemoteConfigResponse with custom TopicsMapSerializer
  that drops unknown topic names so future backend additions don't
  break older SDKs
- Backend.getRemoteConfig() background-aware method modeled on
  getVirtualCurrencies
- Unit tests covering parsing, unknown-field tolerance, errors, and
  call deduplication

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@codecov
Copy link
Copy Markdown

codecov Bot commented May 5, 2026

Codecov Report

❌ Patch coverage is 76.92308% with 18 lines in your changes missing coverage. Please review.
✅ Project coverage is 79.46%. Comparing base (aa0f786) to head (a7f33ec).
⚠️ Report is 15 commits behind head on main.

Files with missing lines Patch % Lines
.../kotlin/com/revenuecat/purchases/common/Backend.kt 71.11% 7 Missing and 6 partials ⚠️
...chases/common/remoteconfig/RemoteConfigResponse.kt 81.48% 0 Missing and 5 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3435      +/-   ##
==========================================
- Coverage   79.47%   79.46%   -0.01%     
==========================================
  Files         362      363       +1     
  Lines       14547    14625      +78     
  Branches     1977     1993      +16     
==========================================
+ Hits        11561    11622      +61     
- Misses       2190     2196       +6     
- Partials      796      807      +11     

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

Drop config_version, api_sources, and AssetSource.{test_url, blacklist_time_seconds};
rename asset_sources to sources; rename TopicEntry.asset_blob_ref to blob_ref and drop
content_type/prefetch. Rename Kotlin AssetSource to Source now that it has no peer.
These fields can be reintroduced when consumers need them.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@tonidero tonidero force-pushed the toniricodiez/add-remote-config-network-scaffolding branch from 39308f9 to 01f4829 Compare May 6, 2026 13:33
Set REMOTE_CONFIG_BASE_URL in local.properties to redirect only the
GET /v1/subscribers/<id>/config request to a developer-controlled host
(e.g. http://localhost:8080/). Other endpoints continue to hit
appConfig.baseURL. Empty by default, and only takes effect in debug
builds, so production traffic is unaffected.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The remote-config server contract changed: the URL drops the user id
(/v2/config) and the response root renames `sources` to `blob_sources`
plus adds a new `api_sources` array describing API host candidates.

- Endpoint.GetRemoteConfig becomes a parameterless object.
- Backend.getRemoteConfig drops the appUserID parameter.
- RemoteConfigResponse: `sources: List<Source>` -> `blobSources: List<BlobSource>`,
  add `apiSources: List<ApiSource>` with the new shape.
- Stub appConfig.isDebugBuild in BackendGetRemoteConfigTest setUp (was
  missing since the override-base-URL commit, causing pre-existing
  MockKExceptions in non-success-path tests).
override fun getPath(useFallback: Boolean) = pathTemplate.format(Uri.encode(userId))
}
object GetRemoteConfig : Endpoint(
pathTemplate = "/v2/config",
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.

This is not final... but we can iterate on it in future PRs.

Also, we need to decide whether this endpoint will be available in the fallback url... But again, we can leave this for once this is more advanced

val path = endpoint.getPath()
val cacheKey = BackgroundAwareCallbackCacheKey(listOf(path), appInBackground)

val overrideURL = BuildConfig.REMOTE_CONFIG_BASE_URL
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.

Keeping this while we test things, but the intention is for it to go away in the final version.

@tonidero tonidero marked this pull request as ready for review May 8, 2026 07:55
@tonidero tonidero requested a review from a team as a code owner May 8, 2026 07:55
Copy link
Copy Markdown
Member

@ajpallares ajpallares left a comment

Choose a reason for hiding this comment

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

Looking good! I only have a couple of comments.

Also, I think the PR description is a bit stale:

  • It still references the previous GET /v1/subscribers/{appUserID}/config.
  • It says "concurrent calls for the same user dedupe". But the cache key doesn't really depend on the appUserID anymore, so it'd just be "concurrent calls dedupe"
  • The current bullet still mentions RemoteConfigResponse(sources, manifest) and a single Source(id, urlFormat, priority, weight).

Comment thread local.properties.example Outdated
Copy link
Copy Markdown
Contributor

@cursor cursor Bot left a comment

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 a7f33ec. Configure here.

}

private fun <K, V> entry(key: K, value: V): Map.Entry<K, V> =
java.util.AbstractMap.SimpleEntry(key, value)
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.

Inline fully-qualified reference instead of import

Low Severity

The entry helper uses the fully-qualified inline reference java.util.AbstractMap.SimpleEntry instead of importing java.util.AbstractMap (or java.util.AbstractMap.SimpleEntry) at the top of the file. This violates the rule requiring FQN imports over inline fully-qualified references.

Fix in Cursor Fix in Web

Triggered by learned rule: Use FQN imports, not inline fully-qualified references

Reviewed by Cursor Bugbot for commit a7f33ec. Configure here.

@tonidero tonidero added this pull request to the merge queue May 8, 2026
Merged via the queue into main with commit 315b770 May 8, 2026
43 checks passed
@tonidero tonidero deleted the toniricodiez/add-remote-config-network-scaffolding branch May 8, 2026 11:42
github-merge-queue Bot pushed a commit that referenced this pull request May 13, 2026
**This is an automatic release.**

## RevenueCat SDK
### 📦 Dependency Updates
* [RENOVATE] Update dependency gradle to v8.14.5 (#3459) via RevenueCat
Git Bot (@RCGitBot)

## RevenueCatUI SDK
### ✨ New Features
* Pre-warm image cache for workflow step states (#3447) via Cesar de la
Vega (@vegaro)
### Paywallv2
#### ✨ New Features
* Add `close_workflow` button action (#3453) via Cesar de la Vega
(@vegaro)
#### 🐞 Bugfixes
* Fix preload VideoComponent fallback override images (#3449) via Cesar
de la Vega (@vegaro)

### 🔄 Other Changes
* Select blob source by priority and weighted random (#3458) via Toni
Rico (@tonidero)
* [AUTOMATIC] Update golden test files for backend integration tests
(#3473) via RevenueCat Git Bot (@RCGitBot)
* Clean up unreferenced topic files after successful remote-config
refresh (#3439) via Toni Rico (@tonidero)
* Cache remote config response in memory with TTL and persist to disk
(#3457) via Toni Rico (@tonidero)
* build(deps): bump fastlane from 2.233.1 to 2.234.0 (#3463) via
dependabot[bot] (@dependabot[bot])
* Update codelabs links (#3460) via Jaewoong Eum (@skydoves)
* Add RemoteConfigManager and TopicFetcher (#3437) via Toni Rico
(@tonidero)
* Add exit offers support to workflows (#3452) via Cesar de la Vega
(@vegaro)
* Update baseline profiles (#3461) via RevenueCat Git Bot (@RCGitBot)
* Add network scaffolding for remote config endpoint (#3435) via Toni
Rico (@tonidero)
* test: cover singleStepFallbackId == initialStepId edge case (#3445)
via Facundo Menzella (@facumenzella)

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Low risk: this is a release/versioning update (SNAPSHOT -> final) plus
docs deployment path changes, with no functional code changes beyond
version constants.
> 
> **Overview**
> Finalizes the `10.6.0` release by switching all version references
from `10.6.0-SNAPSHOT` to `10.6.0` (root `.version`,
`gradle.properties`, `Config.frameworkVersion`, and sample/test app
`libs.versions.toml` files).
> 
> Updates documentation publishing to point at the `10.6.0` docs path
(CircleCI S3 sync target and `docs/index.html` redirect), and prepends
the `10.6.0` section to `CHANGELOG.md`/`CHANGELOG.latest.md`.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
4da1697. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants