Skip to content

Conversation

@ggazzo
Copy link
Member

@ggazzo ggazzo commented Sep 15, 2025

Summary by CodeRabbit

  • New Features
    • Added federation endpoints to retrieve room state and state IDs, with request validation and structured responses for easier integration.
  • Breaking Changes
    • Make Join response updated: membership now supports multiple states (join, leave, invite, ban, knock) instead of only join.
    • Removed the origin field from the Make Join event payload.
  • Refactor
    • Improved typing and validation for federation routes to align responses and requests with updated schemas.
  • Chores
    • Expanded central DTO exports to include new federation state and state-ids modules.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 15, 2025

Walkthrough

Adds federation state DTOs for GetState and GetStateIds, re-exports them, updates a controller to import these DTOs and removes a type assertion, adjusts the MakeJoin response DTO (membership union, removes origin), and extends the StateStore type in the federation SDK with an _id: ObjectId field.

Changes

Cohort / File(s) Summary of Changes
Federation State DTOs (new)
packages/homeserver/src/dtos/federation/state-ids.dto.ts, packages/homeserver/src/dtos/federation/state.dto.ts
Introduces DTOs and types for GetStateIds and GetState endpoints, including params, query, and response schemas and their Static-derived TS types.
DTO Re-exports
packages/homeserver/src/dtos/index.ts
Re-exports the new federation state DTO modules (state-ids.dto, state.dto).
Controller Imports and Type Usage
packages/homeserver/src/controllers/federation/profiles.controller.ts
Imports new state-related DTOs; removes an as any assertion from the make_join handler return.
Profiles DTO Adjustment
packages/homeserver/src/dtos/federation/profiles.dto.ts
Changes membership to a union type and removes origin from MakeJoinResponseDto.event.
State Repository Type Update
packages/federation-sdk/src/repositories/state.repository.ts
Adds _id: ObjectId to the exported StateStore type.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant C as Remote HS (Client)
  participant API as Homeserver API (Profiles Controller)
  participant SVC as Profiles/State Service
  participant REPO as State Repository
  participant DB as Database

  Note over C,API: GET /_matrix/federation/v1/state_ids/:roomId?event_id=...
  C->>API: Request (validated by GetStateIds* DTOs)
  API->>SVC: getStateIds(roomId, event_id)
  SVC->>REPO: fetchStateIds(roomId, event_id)
  REPO->>DB: query (uses StateStore with _id)
  DB-->>REPO: pdu_ids, auth_chain_ids
  REPO-->>SVC: state ids
  SVC-->>API: GetStateIdsResponse
  API-->>C: 200 { pdu_ids, auth_chain_ids }

  rect rgba(200,235,255,0.25)
  Note right of API: New DTO-driven validation paths<br/>and repository type includes _id
  end
Loading
sequenceDiagram
  autonumber
  participant C as Remote HS (Client)
  participant API as Homeserver API (Profiles Controller)
  participant SVC as Profiles/State Service
  participant REPO as State Repository
  participant DB as Database

  Note over C,API: GET /_matrix/federation/v1/state/:roomId?event_id=...
  C->>API: Request (validated by GetState* DTOs)
  API->>SVC: getState(roomId, event_id)
  SVC->>REPO: fetchState(roomId, event_id)
  REPO->>DB: read state/auth chain events
  DB-->>REPO: events
  REPO-->>SVC: pdus, auth_chain
  SVC-->>API: GetStateResponse
  API-->>C: 200 { pdus, auth_chain }

  Note over API: make_join handler now returns typed value (no as any)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • feat: state endpoints #164 — Also modifies StateStore (_id: ObjectId) and adds federation state-related DTOs/endpoints, indicating overlapping work on federation state handling.

Poem

I nibble on specs in the moonlit byte,
New DTOs hop in, tidy and bright.
State IDs gathered, events in a row,
Memberships broaden, origins go.
With an ObjectId tucked in tight,
This rabbit approves—ship it tonight! 🐇✨

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title "chore: state management DTOs and update profiles controller types" accurately and concisely summarizes the main changes in the PR—adding state-related DTOs and adjusting types in the profiles controller—while avoiding unnecessary detail, so a teammate scanning history will understand the primary intent.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch chore/membership-types

Tip

👮 Agentic pre-merge checks are now available in preview!

Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.

  • Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
  • Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.

Please see the documentation for more information.

Example:

reviews:
  pre_merge_checks:
    custom_checks:
      - name: "Undocumented Breaking Changes"
        mode: "warning"
        instructions: |
          Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).

Please share your feedback with us on this Discord post.


Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov-commenter
Copy link

codecov-commenter commented Sep 15, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 80.41%. Comparing base (0c41fdf) to head (396d77f).

Additional details and impacted files
@@           Coverage Diff           @@
##             main     #195   +/-   ##
=======================================
  Coverage   80.41%   80.41%           
=======================================
  Files          58       58           
  Lines        4514     4514           
=======================================
  Hits         3630     3630           
  Misses        884      884           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@ggazzo ggazzo force-pushed the chore/membership-types branch from 48215d2 to 396d77f Compare September 15, 2025 12:52
@ggazzo ggazzo marked this pull request as ready for review September 15, 2025 12:53
@ggazzo ggazzo merged commit 4d647f3 into main Sep 15, 2025
2 of 3 checks passed
@ggazzo ggazzo deleted the chore/membership-types branch September 15, 2025 13:00
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
packages/federation-sdk/src/repositories/state.repository.ts (1)

37-41: "Latest" sort is reversed — should be descending by createdAt

The method name says "Latest" but the query sorts ascending, returning the earliest document.

Apply this diff:

-		return this.collection.findOne({ roomId }, { sort: { createdAt: 1 } });
+		return this.collection.findOne({ roomId }, { sort: { createdAt: -1 } });
packages/homeserver/src/dtos/federation/profiles.dto.ts (1)

57-69: Include Matrix room version '12' or derive versions from a central source (don’t hardcode 1..11)

packages/homeserver/src/dtos/federation/profiles.dto.ts:57–69 currently enumerates '1'..'11'; Matrix v12 became stable on 2025-08-11 — add '12' or read room versions from a central enum/config to avoid future drift.

🧹 Nitpick comments (10)
packages/federation-sdk/src/repositories/state.repository.ts (5)

33-35: Validate stateId before constructing ObjectId

Passing an invalid string to new ObjectId(...) throws. Return null or a typed error early.

Apply this diff:

-		return this.collection.findOne({ _id: new ObjectId(stateId) });
+		if (!ObjectId.isValid(stateId)) {
+			return null;
+		}
+		return this.collection.findOne({ _id: new ObjectId(stateId) });

55-61: Harden $in query against invalid ObjectId strings

Map guards will prevent runtime exceptions and accidental full scans.

Apply this diff:

-		return this.collection
-			.find({ _id: { $in: stateIds.map((id) => new ObjectId(id)) } })
-			.sort({ createdAt: 1 /* order as is saved */ });
+		const ids = stateIds
+			.filter((id) => ObjectId.isValid(id))
+			.map((id) => new ObjectId(id));
+		return this.collection
+			.find({ _id: { $in: ids } })
+			.sort({ createdAt: 1 /* chronological order */ });

37-47: Add supporting indexes for the queried patterns

Frequent queries sort/filter by roomId and createdAt. Add compound indexes to avoid COLLSCANs.

  • Create: { roomId: 1, createdAt: -1 } (covers latest and ascending scans)
  • Create: { roomId: 1, 'delta.identifier': 1 }
  • Optional: { 'delta.identifier': 1 } if cross-room lookups are common

101-103: Clarify the TODO and fix the typo

"whit" -> "with". Consider explaining the StateMapKey format (e.g., "type:state_key") to justify the colon requirement.

-		// TODO: why it must to end whit `:` ?
+		// TODO: Document why StateMapKey must end with ':' (format 'type:state_key'?).

14-16: Use StateStore (not WithId) for the collection and all return types

StateStore already declares _id; WithId is redundant. Update types consistently:

  • packages/federation-sdk/src/container.ts (lines 84,86): change register<Collection<WithId>> → register<Collection> and useValue: db.collection('states').
  • packages/federation-sdk/src/repositories/state.repository.ts: change private readonly collection: Collection<WithId> → Collection and replace all WithId occurrences (method returns, FindCursor, InsertOneResult) with StateStore. Affected lines: 31, 33, 39, 45, 51, 57, 66, 73, 91, 100.
-		private readonly collection: Collection<WithId<StateStore>>,
+		private readonly collection: Collection<StateStore>,

I can push a follow‑up diff updating all return types.

packages/homeserver/src/dtos/federation/state-ids.dto.ts (1)

8-10: Validate event_id with a dedicated EventIdDto

Stronger validation reduces bad queries early and aligns with other DTOs (you already have RoomIdDto).

Apply this diff:

-import { RoomIdDto } from '../common/validation.dto';
+import { RoomIdDto, EventIdDto } from '../common/validation.dto';
@@
 export const GetStateIdsQueryDto = t.Object({
-	event_id: t.String({ description: 'Event ID to get state at' }),
+	event_id: EventIdDto,
 });

Add this helper in common/validation.dto.ts (outside this file):

export const EventIdDto = t.String({
  pattern: '^\\$[A-Za-z0-9_=/\\.\\-\\+]+:(.+)$',
  description: 'Matrix event ID in format $event:server.com',
  examples: ['$abc123:example.com'],
});

I can open a follow-up PR to add EventIdDto and replace usages in state.dto.ts as well.

packages/homeserver/src/controllers/federation/profiles.controller.ts (2)

85-85: Let the service negotiate room versions (avoid forcing ['1'] fallback)

Defaulting to ['1'] risks negotiating an old version. Prefer passing ver as-is and letting ProfilesService choose sensible defaults.

-				return profilesService.makeJoin(roomId, userId, ver ?? ['1']);
+				return profilesService.makeJoin(roomId, userId, ver);

Confirm the third parameter is optional in ProfilesService.makeJoin; if not, prefer injecting a server-default list (e.g., from config).


2-2: Remove unused import: RoomVersion

-import { type RoomVersion } from '@hs/room';
packages/homeserver/src/dtos/federation/state.dto.ts (2)

8-10: Use EventIdDto for event_id (same rationale as state-ids DTO)

Keeps validation consistent and stricter.

-import { RoomIdDto } from '../common/validation.dto';
+import { RoomIdDto, EventIdDto } from '../common/validation.dto';
@@
 export const GetStateQueryDto = t.Object({
-	event_id: t.String({ description: 'Event ID to get state at' }),
+	event_id: EventIdDto,
 });

12-19: Consider reusing a canonical Event DTO instead of Record<string, any>

If you already define an event shape in common/event.dto, referencing it here improves type-safety and docs. If events are heterogeneous by design, ignore this.

Is there a suitable event DTO in common/event.dto that matches the PDU structure returned here?

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0c41fdf and 396d77f.

📒 Files selected for processing (6)
  • packages/federation-sdk/src/repositories/state.repository.ts (1 hunks)
  • packages/homeserver/src/controllers/federation/profiles.controller.ts (2 hunks)
  • packages/homeserver/src/dtos/federation/profiles.dto.ts (1 hunks)
  • packages/homeserver/src/dtos/federation/state-ids.dto.ts (1 hunks)
  • packages/homeserver/src/dtos/federation/state.dto.ts (1 hunks)
  • packages/homeserver/src/dtos/index.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
packages/homeserver/src/dtos/federation/state-ids.dto.ts (1)
packages/homeserver/src/dtos/common/validation.dto.ts (1)
  • RoomIdDto (9-13)
packages/homeserver/src/dtos/federation/state.dto.ts (1)
packages/homeserver/src/dtos/common/validation.dto.ts (1)
  • RoomIdDto (9-13)
🔇 Additional comments (3)
packages/homeserver/src/dtos/federation/state-ids.dto.ts (1)

12-17: LGTM — schema matches expected shapes (IDs only)

packages/homeserver/src/dtos/index.ts (1)

10-11: LGTM — central re-exports wired

packages/homeserver/src/controllers/federation/profiles.controller.ts (1)

79-86: Typed return without as any — good cleanup

Comment on lines +79 to +86
const MembershipDto = t.Union([
t.Literal('join'),
t.Literal('leave'),
t.Literal('invite'),
t.Literal('ban'),
t.Literal('knock'),
]);

Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Don’t widen membership for make_join; it should be 'join' only

The event template returned by make_join must carry membership 'join'. Allowing other values here weakens validation.

Apply this diff (keep MembershipDto for other contexts, but restrict this field):

-			membership: MembershipDto,
+			membership: t.Literal('join'),

Also applies to: 91-91


🌐 Web query:

Matrix Federation make_join response: confirm that the event content.membership in the template must be 'join' per the current spec.

💡 Result:

Yes. The make_join response's event.template content.membership is required to be "join". See the Server‑Server API /make_join response which specifies the event.content.membership must be the value "join". [1][2]

Sources:
[1] Matrix spec — Server‑Server API, GET /_matrix/federation/v1/make_join (response “event” / Membership Event Content). [2] Matrix legacy federation doc — /make_join (shows example and membership = "join").


Don’t widen membership for make_join; restrict to 'join'

Matrix /make_join requires event.template.content.membership === "join" — restrict the DTO accordingly.

File: packages/homeserver/src/dtos/federation/profiles.dto.ts (lines 79-86; also applies to line 91)

Apply this diff (keep MembershipDto for other contexts, but restrict this field):

-			membership: MembershipDto,
+			membership: t.Literal('join'),

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In packages/homeserver/src/dtos/federation/profiles.dto.ts around lines 79-86
(and also adjust usage at line 91), the membership type used for make_join is
too broad; create or use a narrowed type that only allows the literal 'join' for
the make_join path instead of the existing MembershipDto union. Keep the
existing MembershipDto for other contexts, then replace the membership field
used for make_join with a t.Literal('join') (or a new MakeJoinMembershipDto
alias) and update the type at line 91 accordingly so
event.template.content.membership is restricted to "join".

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.

3 participants