Skip to content

feat(contrail): RSVP support for Contrail-only ATProto events#575

Closed
tompscanlan wants to merge 8 commits into
mainfrom
feature/contrail-rsvp
Closed

feat(contrail): RSVP support for Contrail-only ATProto events#575
tompscanlan wants to merge 8 commits into
mainfrom
feature/contrail-rsvp

Conversation

@tompscanlan
Copy link
Copy Markdown
Contributor

Summary

  • Add createRsvpByUri() and deleteRsvpByUri() to BlueskyRsvpService for publishing RSVPs using raw AT URIs (no EventEntity required)
  • Branch attendEvent() and cancelAttendingEvent() to detect Contrail slugs (did:plc:xxx~rkey) and route to ATProto-native RSVP flow — publish/delete directly on user's PDS
  • Extend dashboard "Attending" tab to query Contrail's RSVP table by user DID and merge results with tenant DB events

Closes om-76gq. Believed to be the last feature gap before Contrail changes can move to prod.

How it works

When a user RSVPs to a Contrail-only event (no tenant DB row):

  1. Slug is parsed as ATProto format (did~rkey)
  2. Event is validated via contrailQueryService.findByUri()
  3. RSVP record is published to user's PDS as community.lexicon.calendar.rsvp
  4. Contrail ingests the RSVP via firehose — no tenant DB attendee row needed
  5. Dashboard queries Contrail RSVP table by user DID, deduplicates against tenant results

Test plan

  • 36 new unit tests across 3 test files (all passing)
  • Full test suite: 149/149 suites, 2109/2109 tests passing
  • TypeScript: zero new compiler errors
  • Lint: clean
  • e2e: RSVP to a Contrail-only event via API, verify PDS record created
  • e2e: Cancel RSVP on Contrail-only event, verify PDS record deleted
  • e2e: Verify Contrail-only RSVPs appear on dashboard Attending tab
  • e2e: Verify user without ATProto account gets clear error message

Support RSVP operations for Contrail-only ATProto events that have no
tenant DB EventEntity row. These methods accept a raw AT URI instead
of an EventEntity, use uri-only subject references (no CID lookup),
and derive rkeys deterministically from the event URI.
When an event slug is not found in the tenant DB, check if it's an
ATProto slug (did~rkey format). If so, validate against Contrail,
verify user has ATProto identity, and publish RSVP directly to PDS
via BlueskyRsvpService.createRsvpByUri().

- Add AtprotoEnrichmentService, ContrailQueryService, BlueskyRsvpService deps
- Add attendContrailEvent() private method
- Return API-compatible response shaped like normal attendee
- 4 new tests covering happy path, missing event, no DID, non-ATProto slug
When cancelAttendingEvent receives a slug not found in the local DB,
check if it parses as a Contrail ATProto slug (did~rkey format). If so,
delete the RSVP directly from the user's PDS via BlueskyRsvpService.
Query Contrail's RSVP table by user DID when rendering the dashboard
Attending tab, so events RSVPed to via PDS (Contrail-only) appear
alongside tenant DB results. Deduplicates by atprotoUri and handles
Contrail errors gracefully.
The integration test for EventManagementService was missing mock
providers for AtprotoEnrichmentService, ContrailQueryService, and
BlueskyRsvpService added in the Contrail RSVP feature.
Replace socialId + BlueskyService.resumeSession with
PdsSessionService.getSessionForUser which properly resolves the user's
ATProto identity from userAtprotoIdentities table and gets an
authenticated PDS agent. This fixes RSVP for users whose DID is stored
in the identity table rather than user.socialId.
Cancel attendance now publishes an RSVP update with status #notgoing
via putRecord (idempotent overwrite) instead of deleting the record.
This preserves the RSVP on the user's PDS. Also passes event CID
from Contrail record into the StrongRef subject as required by the
community.lexicon.calendar.rsvp lexicon.
…e tests

The dashboard Attending tab used user.socialId to find the user's DID,
but socialId is not reliable for users who linked ATProto after signup.
Use UserAtprotoIdentityService.findByUserUlid() as the canonical DID
source, falling back to socialId for backwards compatibility. Also fix
missing tenantId in getUserById call.

Add e2e test covering RSVP to Contrail-only events, cancel, error
cases (404 for missing events, 400 for users without ATProto identity).
@tompscanlan tompscanlan marked this pull request as draft April 11, 2026 14:34
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