Skip to content

feat(implementations): migrate Android reference implementation to contentful.java CDA SDK (NT-3947) - #439

Merged
Felipe Mamud (fmamud) merged 3 commits into
mainfrom
fix/nt-3947-android-reference-implementation
Aug 19, 2026
Merged

feat(implementations): migrate Android reference implementation to contentful.java CDA SDK (NT-3947)#439
Felipe Mamud (fmamud) merged 3 commits into
mainfrom
fix/nt-3947-android-reference-implementation

Conversation

@fmamud

Copy link
Copy Markdown
Contributor

Summary

  • Migrates the Android reference implementation (compose and views shells) from hand-rolled HTTP/JSON Contentful fetching to Contentful's official CDA SDK (com.contentful.java:java-sdk), fixing a CDAClient concurrency hazard (Mutex) and a link-field-dropping bug in MockPreviewContentfulClient.
  • Follow-up refactor: favors the SDK's typed CTEntry over raw Map<String, Any> across the app-owned entry-rendering components (ContentEntryView, NestedContentEntryView, ContentEntryViewBinder, NestedContentEntryViewBinder, MainScreen, MainActivity), mirroring the SDK's own typed OptimizedEntry(entry: CDAEntry) entry point, and drops redundant nullable generic type arguments.
  • Fixes a regression introduced during that refactor: remember/LaunchedEffect keys in the Compose entry views were keyed on entryId (a String), which no longer changed across identify() calls and stopped merge-tag text from re-resolving. CTEntry has no structural equals/hashCode, so the keys now use entry.toMap() instead, restoring the previous (Map-based) re-resolution behavior.

Test plan

  • ./gradlew :compose:compileDebugKotlin :views:compileDebugKotlin :compose:lintDebug :views:lintDebug — passes
  • Maestro compose suite — 35/35 flows passing prior to the CTEntry refactor's keying fix; a follow-up re-run to reconfirm 35/35 after the keying fix is still pending (this PR is opened as draft until that's done)
  • Maestro views suite — re-run to confirm no regressions from the CTEntry refactor

🤖 Generated with Claude Code

@fmamud
Felipe Mamud (fmamud) force-pushed the fix/nt-3947-android-reference-implementation branch from d863c39 to c59f0ea Compare August 19, 2026 10:29
…ntentful.java CDA SDK (NT-3947)

Replace hand-rolled HTTP/JSON Contentful fetching in the Android reference
implementation with Contentful's official com.contentful.java:java-sdk
CDAClient, mirroring the iOS (NT-3946) migration.

- MockContentfulClient builds the shared CDAClient against the mock server
  and synthesizes the /locales and /content_types responses CDAClient
  requires (mock server only serves /entries), bundling real content-type
  definitions so Rich Text field resolution can find them.
- ContentfulFetcher now fetches by-ID entries through CDAClient (single
  locale, include=10) and decodes them with the SDK's typed entry APIs
  instead of hand-rolled JSON parsing/link resolution.
- MockPreviewContentfulClient wraps the same CDAClient for the preview
  panel's audience/experience fetch, fixing two regressions found while
  validating against the full Maestro suite:
  - Concurrent nt_audience/nt_experience fetches against the same shared
    CDAClient could interleave and drop entries (CDAClient mutates shared
    instance state while a call is in flight); calls are now serialized
    with a Mutex.
  - The mock's content-type-filtered entries endpoint never returns an
    includes section, so contentful.java silently drops any Link field it
    can't resolve rather than keeping it as a stub, breaking the preview
    panel's audience/experience grouping; raw link stubs are now restored
    from CDAEntry.rawFields() when contentful.java drops them.

Verified with the full Compose and Views Maestro suites (35/35 passing on
both), including all preview-panel-overrides scenarios.
…ference implementation

Rework the app-owned entry-rendering components (ContentEntryView,
NestedContentEntryView, ContentEntryViewBinder, NestedContentEntryViewBinder,
MainScreen, MainActivity) to accept and pass CTEntry instead of raw
Map<String, Any>, mirroring the SDK's own typed OptimizedEntry(entry: CDAEntry)
entry point. Also drops redundant nullable type arguments on getField calls.

Key CTEntry, having no structural equals/hashCode, are compared by reference:
remember/LaunchedEffect keys in ContentEntryView and NestedContentEntryView
now key on entry.toMap() (structurally comparable) instead of the entry
instance or its id, restoring correct re-resolution of merge-tag text after
identify() calls.
…roid's in-app synthesis

The mock CDA gained a real `/locales` route in #450 for iOS's contentful.swift
migration. Android's `LocalesInterceptor` still synthesized both `/locales` and
`/content_types` in-app because neither existed on the mock when it was written.

`/locales` is now redundant and removed outright: a real request succeeds.

`/content_types` is NOT redundant on its own — contentful.java's
`ResourceFactory.array()` unconditionally calls `RichTextFactory.resolveRichTextField`
on every array response, which unconditionally calls `ResourceUtils.ensureContentType`
for every entry, regardless of whether that entry has a Rich Text field, and throws
`CDAContentTypeNotFoundException` on a 404. The reference app's fixtures do have Rich
Text fields (RichText.kt is exercised by ContentEntryView/NestedContentEntryView), so
this path is genuinely load-bearing.

Rather than keep the in-app fixture shim, add a `/content_types` route to the mock
server that serves `ctfl-space-data.json`'s `contentTypes` array, mirroring the
`/locales` route's shape and #450's precedent of fixing the mock rather than shimming
around it in a file customers copy from. This lets Android's bundled
`content_types.json`/`ContentTypesFixture` be deleted entirely.

With both interceptor-synthesized routes gone, MockContentfulClient.kt no longer earns
its own file: consolidate the shared CDAClient and the fetch-by-ids loop into
ContentfulFetcher.kt, mirroring iOS's single ContentfulClient.swift shape.

Validated: `pnpm --filter mocks typecheck`, `pnpm lint`, `pnpm format:check` clean;
both APKs assemble; full Maestro suite passes 70/70 flows across Compose and Views.
@fmamud
Felipe Mamud (fmamud) merged commit 90d5d1c into main Aug 19, 2026
41 checks passed
@fmamud
Felipe Mamud (fmamud) deleted the fix/nt-3947-android-reference-implementation branch August 19, 2026 12:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants