refactor(remote-config): add RemoteConfigManager read facade (topic/body)#3703
Conversation
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #3703 +/- ##
==========================================
+ Coverage 80.57% 80.59% +0.02%
==========================================
Files 395 396 +1
Lines 16316 16366 +50
Branches 2322 2337 +15
==========================================
+ Hits 13146 13190 +44
- Misses 2243 2245 +2
- Partials 927 931 +4 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
57d60c2 to
9d78258
Compare
5659295 to
8cfe71a
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ 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 9d78258. Configure here.
9d78258 to
0932eb0
Compare
Adds suspend topic()/body() reads to RemoteConfigManager. On a cache miss, body() waits for an in-flight sync or triggers one on demand, unless the endpoint is disabled or no app user is known. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
topic()/body() and RemoteConfigTopicStore now take a closed RemoteConfigTopic (Workflows/UiConfig/Sources) instead of a raw topic string, so consumers can only read known topics. The disk cache stays string-keyed; the enum maps to its wire name at the store boundary. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add two integration tests that read back through RemoteConfigManager's public topic()/body() facade after a sync persists to disk: - RemoteConfigManagerIntegrationTest: real disk cache + blob store, mocked backend; reads an inline-only item's topic and body off disk. - ProductionRemoteConfigIntegrationTest: real backend; a cold body() drives a genuine /v1/config fetch + persist, then reads an inlined workflows blob back off disk through the facade. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…gate reads when disabled Refines the read facade on the RemoteConfigManager: - Rename `body(topic, itemKey)` to `blobData`, with two overloads: a reified `blobData<T>` that parses the resolved blob as JSON into a `@Serializable` T via the shared JsonProvider, and a `blobData(..., transform)` overload for non-JSON blobs the caller decodes itself. - Drop the inline-content fallback: an item with no `blob_ref` now resolves to `null` (its inline data is exposed only through `topic()`), instead of returning the config JSON as a payload. - Rename `RemoteConfiguration.ConfigItem.content` to `metadata`. - Rename the session kill-switch `isUnavailable`/`unavailable` to `isDisabled`/`disabled`, and gate reads: `topic()` and `blobData()` return `null` once the endpoint is disabled (4xx), rather than serving cached data. - Refresh remote config as part of syncAttributesAndOfferingsIfNeeded. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…reads - PurchasesCommonTest: syncAttributesAndOfferingsIfNeeded triggers RemoteConfigManager.refreshRemoteConfig via the sync completion. - RemoteConfigManagerTest: reified blobData<T> fetches on demand then deserializes, matching the transform overload's wait/download path. - ProductionRemoteConfigIntegrationTest: end-to-end typed blobData<T> read of a workflows blob. Uses a minimal test-only type rather than PublishedWorkflow, which the shared defaultJson can't decode (polymorphic component tree) and which these lazy paywall workflows omit ui_config for. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Make RemoteConfigManager.topic() mirror blobData(): when no topic is committed yet, wait for an in-flight refresh (or trigger one on demand) and re-read before returning null, so a topic read during the initial sync returns fresh data instead of a spurious empty result. A committed topic still returns immediately, and a disabled endpoint or unknown user returns null with no network call. Simplify the integration tests to read topic() directly (the cold-load priming step is now redundant) and add unit coverage mirroring the blobData self-prime suite. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ee90f08 to
3553a9b
Compare
rickvdl
left a comment
There was a problem hiding this comment.
Awesome work! Couple of notes / questions
| suspend inline fun <reified T> blobData(topic: RemoteConfigTopic, itemKey: String): T? = | ||
| blobData(topic, itemKey) { bytes -> | ||
| try { | ||
| JsonProvider.defaultJson.decodeFromString<T>(bytes.decodeToString()) |
There was a problem hiding this comment.
I think this is what you meant over slack; but decoding errors are now also returned as null (which is expected in it's current state), but indeed maybe some typed errors would be nice
There was a problem hiding this comment.
Right, in the end I guess it depends on the consumers and whether and how we want to expose this later on. We already log these internally, so a consumer of this API right now can only know that something went wrong, but not what... So yeah, adding a better response might make more sense, but might leave that for a separate PR and also see if consumers do actually need that info.


Adds a read API to
RemoteConfigManagerso consumers can read remote-config data:topic(name)— a topic's committed metadata.blobData(topicName, itemKey)— a resolved item payload (its inline content, or its blob).On a cache miss, a read resolves from an in-progress or on-demand sync instead of returning empty.
Note
Medium Risk
New suspend read paths can block on network and trigger on-demand
/v1/configsyncs; behavior is complex but heavily tested and gated on identity/disable state.Overview
Adds a suspend read API on
RemoteConfigManager:topic(RemoteConfigTopic)for committed item metadata, andblobData(transform + JSON reified) for blob-backed payloads. Reads run on IO, respect the session 4xx kill-switch (isUnavailable→isDisabled), and on a cache miss wait for an in-flight sync or trigger a foreground on-demand refresh when an app user ID is available (wired fromPurchasesFactoryviaappUserIDProvider).Introduces
RemoteConfigTopicand threads it throughRemoteConfigTopicStore/ source parsing. Renames config item inline fieldscontent→metadataand documents that payloads withoutblob_refare index-only (blobDatareturns null).syncAttributesAndOfferingsIfNeedednow callsrefreshRemoteConfigbefore fetching offerings. Refresh completion is signaled withCompletableDeferredso blocked readers unblock on 204, errors,clearCache, andclose. Unit, integration, and production golden tests cover the facade and waiting behavior.Reviewed by Cursor Bugbot for commit e3f74fb. Bugbot is set up for automated code reviews on this repo. Configure here.