Repository navigation
fix(messaging): deliver Appwrite push topic campaigns on the topic id - #14580
Conversation
A topic campaign sent through the Appwrite push provider was published on each subscribed target's identifier rather than the topic id, so it only reached devices when the target identifier happened to equal the topic id. Any other identifier failed with "Topic <identifier> does not exist". Carry the topic id through the recipient stream and publish an Appwrite topic page on it, once per message, so one Appwrite target with any identifier covers every topic it subscribes to. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Review complete. No blocking issues — approved ✅; 1 nitpick below. 🧹 Nitpicks (1) — 🟢 1 low
This PR reworks recipient streaming in the messaging worker so each yielded page carries its topic id, routing per-user and topic campaigns to the correct MQTT wire topic with a dedup map guarding against double publishes, and adds MQTT e2e tests plus CI lane wiring.
Reviewed commit: f864fa7 |
🟢 Tier S · Ready to merge
Messaging recipient pages retain topic IDs so Appwrite push campaigns publish to each topic ID once per message rather than to target identifiers. The worker also routes targetless users through an enabled Appwrite provider when another push provider is the default, and builds message search terms from providers actually used. MQTT e2e coverage exercises topic routing and provider selection, the CI path map includes messaging and push-adapter changes, and the console snippets workflow repins its Rust toolchain action. Latest changes: The new commits route targetless user sends through enabled Appwrite even when it is not the default, track only providers actually used in message search, and add MQTT e2e cases for enabled and disabled Appwrite-provider selection.
📂 Walkthrough · 4
Reviewed the commits since |
Security rulesNo new WARNING or ERROR findings from security rules. 19 existing findings tracked in
|
✨ Benchmark resultsComparing
Per-scenario breakdown & investigation detailsMetrics below reflect the current branch (after). Δ P95 compares against the base.
Top API waits (after) |
dtolnay/rust-toolchain rewrote its stable branch, leaving the pinned commit outside the repo's history, which fails the zizmor workflow audit on main. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…isn't the default A users[] push reached a user with no push target on users/<userId> only when the default (first enabled) push provider was Appwrite. With FCM or APNs set up first, the message failed with "No valid recipients found." even though an Appwrite provider was enabled. Resolve the enabled Appwrite push provider once per send and use it for the targetless fallback, whichever provider is the default. Users holding other push targets are unchanged, so no notification is sent twice. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The worker appended every provider in its resolved-provider cache to the message's search text and the message.providers span. That cache is seeded with the default provider, and now the enabled Appwrite provider, so a send delivered entirely through another provider was still searchable by providers it never used. Record the provider of each batch result, wake-ups included, and build the search and span from that set. The cache keeps its seeding. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…akes unobservable The messageSearch attribute filter recomputes a message's search text on every write from its id, description, status, title and type, so provider names are never stored and cannot be asserted through the search API. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…te-provider fix(messaging): reach targetless users through Appwrite push when it isn't the default
…y-topic-id # Conflicts: # .github/workflows/console-snippets-qa-server.yml
… into fix/mqtt-topic-send-by-topic-id

What does this PR do?
Topic campaigns sent through the Appwrite push (MQTT) provider were published on each subscribed target's
identifierinstead of the topic ID.streamRecipientskeyed topic pages by target identifier and dropped the topic ID. Topic sends skip the per-user branch, sorecipients = array_keys($identifiers). The Appwrite adapter then treats eachtoas a topic ID (Push/Appwrite.phppersist()→getDocument('topics', $to)). The result:identifier = <topicId>, which is what the e2e fixture did.ID.unique(), failed withTopic <identifier> does not existand counted as a failed delivery.Fix
streamRecipientsnow yields each page with its topic ID (nullfor user, target or email addressed pages) instead of aperUserbool.[$topicId]. The subscribed Appwrite targets only mark that the topic has MQTT subscribers.What each recipient source yields
$perUseris now$topicId === null. Only topic pages carry an ID:streamRecipients$perUsertopics: [...]: subscriber pages for each topic[$page, $topic->getId()]users: [...]: those users' push targets[$page, null]users: [...]with no live target: fallback ontousers/<id>(only when the default provider is Appwrite)[[$default->getId() => $identifiers], null]targets: [...]: explicitly named targets[$page, null]emails(email messages only)[[$default->getId() => ...], null]For an Appwrite provider page:
$perUsertrue: sends tousers/<userId>, unchanged.$perUserfalse: sends to[$topicId](new).FCM and APNs pages still send to device tokens. A mixed send such as
topics: ['news'], users: ['U']routes each page by its own value.One Appwrite target with any identifier now covers every topic it is subscribed to. Targets that already use
identifier = topicIdkeep working.Test Plan
E2E in
tests/e2e/Services/Mqtt/MqttServerTest.php:setupPushTopicfixture now uses a placeholder identifier, so every existing campaign, replay and QoS test runs against the fixed routing.testCampaignReachesEveryTopicOfOneTarget: one target subscribed to two topics gets the message exactly once on each topic ID, and the message endssent.testCampaignPublishesTopicOncePerMessage: two users on two different Appwrite providers each get the message exactly once.testCampaignIsNotDeliveredOnTargetIdentifier: subscribing to the target identifier is refused, and the only delivery is on the topic ID..github/ci/e2e.json:Workers/Messaging.phpandMessaging/Adapter/Push/now also trigger the Mqtt lane.Locally I ran
php -l, Pint and PHPStan on the changed files. PHPStan reports no new errors; the only ones are theUtopia\Database\Idnot-found errors thatmainalso shows because the local vendor is stale. E2E was not run locally; the CI e2e lanes ran it.CI
The earlier
rust-toolchainrepin is superseded by #14577 onmain. The merge frommaintakes itsv1pin, so this PR no longer changes that workflow.Related PRs and Issues
🤖 Generated with Claude Code