feat(android): add getRegion/getJurisdiction with config caching#129
Merged
ethanpschoen merged 3 commits intoJul 15, 2026
Conversation
getLocation() returns GeoIP region data (IPInfo), not a jurisdiction —
add getRegion() as a clear accessor for that ("US-CA" combined form).
getJurisdiction() resolves the jurisdiction code from the full config.
Since getFullConfiguration() previously re-fetched over the network on
every call, cache the config on the Ketch instance keyed on the fields
that affect its URL path (org/property/env/jurisdiction/language/hash).
setJurisdiction()/setLanguage() invalidate the cache since they change
that path; setRegion() does not, since region isn't part of it.
getLocation() results are cached the same way (no path params to key on).
Sample app: added a "Get Region" card and simplified "Get Jurisdiction"
to call the new getJurisdiction() method.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit ceda824. Configure here.
bugbot: buildConfigCacheKey collapsed null and "" into the same key component, but HeadlessApiClient.getFullConfiguration only checked `!= null` when deciding whether to append the env/jurisdiction/ language path segment. A blank (non-null) value and a null value hit different URLs there, so two requests could share a cache key while calling different endpoints. Fix at the source: introduce FullConfigurationRequest.configPathSegment() and .normalizedHash(), which treat blank the same as absent (matching ketch-tag). Both HeadlessApiClient's path builder and Ketch's cache key now derive from these same helpers, so they can never disagree about what "the same request" means.
…iction getRegion() already wrapped getLocation().location?.toRegionCode() with no other reason for an integrator to want the raw GeoIP payload off the Ketch instance, so fold the location cache/fetch directly into getRegion() and remove the redundant public accessor. KetchSdk.getLocation() (static) and HeadlessApiClient.getLocation() are unchanged — they're a different layer, predate this change, and KetchHeadlessIntegrationTest exercises the raw endpoint through them directly.
jboileau99
approved these changes
Jul 15, 2026
ethanpschoen
merged commit Jul 15, 2026
d63ed21
into
justin/feat/cross-activity-lifecycle
3 checks passed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Summary
getLocation()returns GeoIP region data (IPInfo), not a jurisdiction —HeadlessApiClient.kthitsGET /ip, which is purely geographic. Fixed the misleading doc comment.getRegion()(combined ISO code, e.g."US-CA") andgetJurisdiction()(resolvedjurisdiction.code ?: defaultJurisdictionCode) as clear, intention-revealing headless accessors.getFullConfiguration()cache on theKetchinstance, keyed on the config URL path (org/property/env/jurisdiction/language/hash).setJurisdiction()/setLanguage()invalidate it since they change that path;setRegion()does not, since region isn't part of it.getLocation()/getRegion()results are cached the same way (no path params to key on).getJurisdiction().Test plan
./gradlew clean :ketchsdk:assembleDebug :ketchsdk:testDebugUnitTest :sample-app-compose:assembleDebug— clean build, all tests pass (incl. newHeadlessModelsTestcoveringtoRegionCode()).sample-app-composeon an emulator:california(real resolved value)US-CA, consistent with the resolved jurisdiction🤖 Generated with Claude Code
Note
Medium Risk
Caching can serve stale jurisdiction/config after external changes until
setJurisdiction/setLanguageor a new request key; low blast radius but integrators should know cache semantics.Overview
Adds instance-level caching for headless
getLocation()andgetFullConfiguration()onKetch, plus convenience APIsgetRegion()(ISO-style code from GeoIP viaIPInfo.toRegionCode()) andgetJurisdiction()(resolved code from full config).getFullConfigurationcache keys on org/property/env/jurisdiction/language/hash;setJurisdiction/setLanguageclear that cache;setRegiondoes not (region is not part of the config URL path). Location is cached for the lifetime of the instance with no invalidation. Docs forgetLocation()are clarified as GeoIP, not jurisdiction.Sample Compose app wires Get Region and switches Get Jurisdiction to
getJurisdiction(). Unit tests covertoRegionCode()edge cases.Reviewed by Cursor Bugbot for commit ceda824. Configure here.