Skip to content

fix(android): compiling examples, shaded HiveMQ with no packaging excludes, shared-topic unsubscribe - #1960

Merged
ChiragAgg5k merged 3 commits into
mainfrom
fix/android-example-literals
Oct 3, 2026
Merged

ChiragAgg5k merged 3 commits into
mainfrom
fix/android-example-literals

Conversation

@ChiragAgg5k

@ChiragAgg5k ChiragAgg5k commented Oct 3, 2026 •

Copy link
Copy Markdown
Member

Fixes the review findings on appwrite/sdk-for-android#137. All of them come from the generator.

Examples that don't compile (Kotlin and Java)

  • Unescaped literals: object and array examples were emitted without escaping. A quote inside a value ended the string ("mutation { accountUpdateName(name: "Walter") ... }" in both GraphQL mutation examples), and Kotlin interpolated "$id" in the DocumentsDB examples. Kotlin.php now builds every example string through one literal helper. It escapes \, " and newlines, plus $ in Kotlin; Java leaves $ alone.
  • Wrong argument names: Kotlin examples named arguments with the spec's snake_case names (grant_id = ... in every new OAuth2 example). The service methods declare the camelCase names, so the Android and Kotlin example templates now apply caseCamel the same way the service templates do.

No string example in the 2.3.x client spec contains $, " or \, so plain string examples render the same as before. Only object and array examples change.

testExamplesUseKotlinAndJavaLiterals covers both, for the kotlin and android targets, following testObjectArrayExamplesUseSwiftLiterals. The fixture gains generalSnakeCaseParams with a grant_id query parameter. It fails on main and passes here. Rendered against the real 2.3.x client spec, the three flagged files now read grantId = "<GRANT_ID>", name: \"Walter\" and "\$id" to "example1".

Shaded HiveMQ: no packaging excludes for apps

The plain hivemq-mqtt-client pulls Netty in as about six separate jars, each with its own META-INF/INDEX.LIST and META-INF/io.netty.versions.properties. Android's packager refuses duplicate resources, and only the app can exclude them, so every app had to add excludes, whether or not it used Push. I built the generated example app without them and it failed at :example:mergeDebugJavaResource with 6 files found with path 'META-INF/INDEX.LIST'.

Android, Flutter and React Native now depend on com.hivemq:hivemq-mqtt-client-shaded:1.3.6, which relocates Netty and JCTools under com.hivemq.client.internal.shaded into one jar.

  • R8 rules: the shared push/consumer-rules.pro now names the relocated packages. Run against the shaded jar, the old rules fail R8 with 52 missing classes; the new rules pass.
  • Removed: the excludes are gone from the Android README, the Android example app and the Flutter README (which keeps its desugaring setup). The React Native Expo config plugin is gone too: adding the excludes was its only job, and it was never published.
  • Checked locally: the example app assembles with no excludes, and lint plus R8 shrinking of the fixture SDK pass.

Push: an unsubscribed callback kept firing on a shared topic

HiveMQ keeps a subscribe callback attached to its filter until the filter itself is unsubscribed. The SDK only sends that UNSUBSCRIBE once no other subscription uses the filter. So with two in-process subscriptions on the same topic, unsubscribing one left its callback receiving messages until the other went away or close() ran. Callbacks now look their subscription up by id when a message arrives, so a removed subscription's callback does nothing. That also covers a message already in flight when unsubscribe() returns.

The Android e2e adds Push shared topic unsubscribe: it subscribes twice to e2e-push, unsubscribes one, triggers another publish, and asserts only the remaining subscription receives it. Android5Java17 without the fix fails on exactly that line (223 of 224 assertions pass); with the fix it passes.

Related: appwrite-labs/cloud#6299

Object and array examples were emitted without escaping, so a quote inside a value ended the string (the GraphQL mutation examples) and Kotlin interpolated "$id". Kotlin examples also named arguments with the spec's snake_case names (grant_id) instead of the camelCase names the service declares.
@hansi-codes

hansi-codes Bot commented Oct 3, 2026 •

Copy link
Copy Markdown

🔵 Tier A · Mergeable after minor fixes

Two less-common compatibility cases still need attention: carriage-return examples and existing Expo plugin registrations.

The change centralizes Kotlin/Java example-string escaping and camel-cases Kotlin named arguments, with generation coverage for both Kotlin and Android. It also switches Android, Flutter, and React Native to shaded HiveMQ dependencies, removes the packaging-exclude setup and Expo plugin, and guards Android push callbacks after a shared-topic subscription is removed.

Verdict New comments Fixed Still open
✅ Approved 2 0 0
Finding Where
🟡 Escape carriage returns in string literals too src/SDK/Language/Kotlin.php:169
🟡 Preserve compatibility with existing Expo plugin registrations templates/react-native/package.json.twig:14
Fix with agent prompt
### Issue 1
src/SDK/Language/Kotlin.php:169
**Escape carriage returns in string literals too**

An example containing `\r\n` still emits a raw carriage return, which is a line terminator in Java and breaks the generated quoted literal. Please escape `\r` alongside `\n` so Windows-style multiline examples compile too.

```suggestion
        $escaped = \str_replace(['\\', '"', "\n", "\r"], ['\\\\', '\\"', '\\n', '\\r'], $value);
```

### Issue 2
templates/react-native/package.json.twig:14
**Preserve compatibility with existing Expo plugin registrations**

Apps following the previous README still have the SDK in `expo.plugins`; after this removal, Expo prebuild cannot resolve its config plugin and fails even though packaging excludes are no longer needed. Keeping a no-op plugin, or documenting that users must remove the existing registration when upgrading, would avoid leaving those integrations broken.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.
📂 Walkthrough · 11
File Change
src/SDK/Language/Kotlin.php Adds a shared string-literal helper for string, map, and array examples.
templates/android/docs/kotlin/example.md.twig, templates/kotlin/docs/kotlin/example.md.twig Uses camelCase parameter names in Kotlin examples.
templates/android/README.md.twig, templates/android/example/build.gradle.kts.twig Removes Netty packaging-exclude instructions and example configuration.
templates/android/library/build.gradle.kts.twig, templates/flutter/android/build.gradle.twig, templates/react-native/android/build.gradle.twig Uses the shaded HiveMQ MQTT client.
templates/android/push/consumer-rules.pro Updates Netty and JCTools shrinker rules to relocated namespaces.
templates/android/library/src/main/java/io/package/services/Push.kt.twig Looks up live subscriptions before delivering in-process callbacks.
templates/flutter/README.md.twig Retains desugaring instructions while removing packaging excludes.
src/SDK/Language/ReactNative.php, templates/react-native/app.plugin.js.twig, templates/react-native/README.md.twig Removes Expo config-plugin generation and setup instructions.
templates/react-native/package.json.twig, templates/react-native/package-lock.json.twig, templates/react-native/eslint.config.mjs Removes plugin packaging, peer dependency, and lint exclusion.
tests/e2e/Android16Java17Test.php, tests/e2e/Android5Java17Test.php, tests/e2e/Base.php, tests/e2e/languages/android/Tests.kt Adds shared-topic unsubscribe regression coverage.
tests/generation/GenerationTest.php, tests/resources/spec-openapi3.json Checks escaped literals and snake_case-to-camelCase example arguments.

Reviewed 553a6fc · Details · Comment @hansi-codes review to re-run, or mention @hansi-codes with a question.

@hansi-codes hansi-codes Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟢 Tier S · Looks good to merge. Summary

…excludes

The plain client pulls Netty in as separate jars, each with its own META-INF/INDEX.LIST and io.netty.versions.properties, so every app's mergeJavaResource failed until it excluded them, whether or not it used Push. The shaded artifact relocates Netty and JCTools into one jar. Move the consumer R8 rules to the relocated packages and drop the excludes from the Android and Flutter READMEs, the Android example app and the React Native config plugin, whose only job was adding them.
HiveMQ keeps a subscribe callback on its filter until the filter is unsubscribed, and the SDK keeps the filter while another subscription uses it. A removed subscription's callback therefore kept firing. Look the subscription up by id on delivery instead of capturing it.
@ChiragAgg5k
ChiragAgg5k force-pushed the fix/android-example-literals branch from cfa04b5 to 553a6fc Compare October 3, 2026 07:53
@ChiragAgg5k ChiragAgg5k changed the title fix(kotlin): generate examples that compile; require Netty excludes for every Android app fix(android): compiling examples, shaded HiveMQ with no packaging excludes, shared-topic unsubscribe Oct 3, 2026

@hansi-codes hansi-codes Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔵 Tier A · Looks good to merge. Summary

*/
protected function getStringLiteral(string $value, string $lang): string
{
$escaped = \str_replace(['\\', '"', "\n"], ['\\\\', '\\"', '\\n'], $value);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Escape carriage returns in string literals too

An example containing \r\n still emits a raw carriage return, which is a line terminator in Java and breaks the generated quoted literal. Please escape \r alongside \n so Windows-style multiline examples compile too.

Suggested change
$escaped = \str_replace(['\\', '"', "\n"], ['\\\\', '\\"', '\\n'], $value);
$escaped = \str_replace(['\\', '"', "\n", "\r"], ['\\\\', '\\"', '\\n', '\\r'], $value);
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/SDK/Language/Kotlin.php
Line: 169

Comment:
**Escape carriage returns in string literals too**

An example containing `\r\n` still emits a raw carriage return, which is a line terminator in Java and breaks the generated quoted literal. Please escape `\r` alongside `\n` so Windows-style multiline examples compile too.

```suggestion
        $escaped = \str_replace(['\\', '"', "\n", "\r"], ['\\\\', '\\"', '\\n', '\\r'], $value);
```

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

🟡 Minor · bug · Reply if this doesn't apply.

"types": "./types/index.d.ts"
},
"./app.plugin.js": "./app.plugin.js",
"./package.json": "./package.json"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Preserve compatibility with existing Expo plugin registrations

Apps following the previous README still have the SDK in expo.plugins; after this removal, Expo prebuild cannot resolve its config plugin and fails even though packaging excludes are no longer needed. Keeping a no-op plugin, or documenting that users must remove the existing registration when upgrading, would avoid leaving those integrations broken.

Prompt To Fix With AI
This is a comment left during a code review.
Path: templates/react-native/package.json.twig
Line: 14

Comment:
**Preserve compatibility with existing Expo plugin registrations**

Apps following the previous README still have the SDK in `expo.plugins`; after this removal, Expo prebuild cannot resolve its config plugin and fails even though packaging excludes are no longer needed. Keeping a no-op plugin, or documenting that users must remove the existing registration when upgrading, would avoid leaving those integrations broken.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

🟡 Minor · bug · Reply if this doesn't apply.

@ChiragAgg5k
ChiragAgg5k merged commit f4791fb into main Oct 3, 2026
61 checks passed
@ChiragAgg5k
ChiragAgg5k deleted the fix/android-example-literals branch October 4, 2026 15:35
@ChiragAgg5k ChiragAgg5k self-assigned this Oct 5, 2026
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.

1 participant