Skip to content

Conversation

@debdutdeb
Copy link
Member

@debdutdeb debdutdeb commented Oct 7, 2025

Proposed changes (including videos or screenshots)

Issue(s)

Steps to test or reproduce

Further comments

Summary by CodeRabbit

  • Bug Fixes
    • Improves reliability of member joins after invitations in federated rooms by ensuring join events are properly propagated.
    • Uses the latest room state to avoid issues caused by outdated state during invite handling.
  • Chores
    • Updated federation SDK dependency to the latest patch version for compatibility and stability improvements.

@dionisio-bot
Copy link
Contributor

dionisio-bot bot commented Oct 7, 2025

Looks like this PR is not ready to merge, because of the following issues:

  • This PR is missing the 'stat: QA assured' label
  • This PR is missing the required milestone or project

Please fix the issues and try again

If you have any trouble, please check the PR guidelines

@changeset-bot
Copy link

changeset-bot bot commented Oct 7, 2025

⚠️ No Changeset found

Latest commit: 7c07dcb

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 7, 2025

Walkthrough

Adds a new emitJoin method to FederationMatrix and exposes it via IFederationMatrixService. Updates invite handler to use latest room state retrieval and emit join membership events for existing members. Bumps @rocket.chat/federation-sdk dependency. Imports expanded for new typing.

Changes

Cohort / File(s) Summary
Federation Matrix service: emitJoin support
ee/packages/federation-matrix/src/FederationMatrix.ts, packages/core-services/src/types/IFederationMatrixService.ts
Introduces IFederationMatrixService.emitJoin(membershipEvent) and implements FederationMatrix.emitJoin(...) emitting homeserver.matrix.membership with membership: 'join'; adds types PersistentEventBase and RoomVersion to imports.
Invite flow adjustments
ee/packages/federation-matrix/src/api/_matrix/invite.ts
Imports FederationMatrix service; switches state retrieval from getFullRoomState2 to getLatestRoomState2; after adding the user, iterates matrixRoom.getMemberJoinEvents() and calls FederationMatrix.emitJoin for each, with debug logging.
Dependency bump
ee/packages/federation-matrix/package.json
Bumps dependency @rocket.chat/federation-sdk from 0.1.16 to 0.1.17.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant InviteAPI as Invite Handler
  participant MatrixRoom as Room State
  participant FedSvc as FederationMatrix
  participant HS as Homeserver Services

  User->>InviteAPI: Accepts/Processes Invite
  InviteAPI->>MatrixRoom: Add user to room
  Note right of InviteAPI: Retrieve latest room state

  loop For each existing member join event
    InviteAPI->>MatrixRoom: getMemberJoinEvents()
    MatrixRoom-->>InviteAPI: membershipEvent
    InviteAPI->>FedSvc: emitJoin(membershipEvent)
    alt Homeserver services available
      FedSvc->>HS: emit homeserver.matrix.membership (membership: join)
    else Unavailable
      FedSvc-->>InviteAPI: warn and return
    end
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • sampaiodiego

Poem

Hop hop, a join event pops,
In matrix halls it gently hops.
Invites trimmed, latest state in line,
Emit the joins—how neat, how fine.
With packets light and logs that sing,
This rabbit stamps a federated ring. 🐇✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title “fix: process joins from full state” concisely and accurately captures the central change of adding logic to handle membership join events derived from the full room state, matching the code updates that emit join events after retrieving the complete state. It clearly indicates a bug fix and specifies the scope of the modification without unnecessary detail.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/process-joins-from-full-state

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@codecov
Copy link

codecov bot commented Oct 7, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 67.43%. Comparing base (ae1e270) to head (7c07dcb).
⚠️ Report is 1 commits behind head on chore/fed-branded.

Additional details and impacted files

Impacted file tree graph

@@                  Coverage Diff                  @@
##           chore/fed-branded   #37156      +/-   ##
=====================================================
- Coverage              67.46%   67.43%   -0.03%     
=====================================================
  Files                   3331     3331              
  Lines                 113527   113527              
  Branches               20615    20611       -4     
=====================================================
- Hits                   76593    76562      -31     
- Misses                 34312    34343      +31     
  Partials                2622     2622              
Flag Coverage Δ
e2e 57.27% <ø> (-0.01%) ⬇️
unit 71.20% <ø> (-0.04%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

🚀 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 fix/process-joins-from-full-state branch from e6e4893 to 6e96765 Compare October 7, 2025 14:41
@ggazzo ggazzo force-pushed the fix/process-joins-from-full-state branch from 6e96765 to 7c07dcb Compare October 7, 2025 14:46
@ggazzo ggazzo marked this pull request as ready for review October 7, 2025 14:46
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: 3

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between ae1e270 and 7c07dcb.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (4)
  • ee/packages/federation-matrix/package.json (1 hunks)
  • ee/packages/federation-matrix/src/FederationMatrix.ts (2 hunks)
  • ee/packages/federation-matrix/src/api/_matrix/invite.ts (3 hunks)
  • packages/core-services/src/types/IFederationMatrixService.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
ee/packages/federation-matrix/src/api/_matrix/invite.ts (1)
ee/packages/federation-matrix/src/FederationMatrix.ts (1)
  • FederationMatrix (145-999)
🔇 Additional comments (4)
ee/packages/federation-matrix/src/api/_matrix/invite.ts (2)

1-1: LGTM!

The import of FederationMatrix is necessary for the new emitJoin functionality added at lines 269-272.


175-175: Confirm use of getLatestRoomState2 vs getFullRoomState2
The call at line 175 was switched from state.getFullRoomState2 to state.getLatestRoomState2, altering which room state is fetched and conflicting with the PR title “process joins from full state.” Verify this change is intentional and doesn’t break invite processing or omit needed state.

ee/packages/federation-matrix/src/FederationMatrix.ts (1)

12-20: LGTM!

The expanded imports of PersistentEventBase and RoomVersion are necessary for the strongly-typed emitJoin method signature.

ee/packages/federation-matrix/package.json (1)

41-41: Version 0.1.17 of @rocket.chat/federation-sdk is available on npm and has no known security advisories.

Comment on lines +269 to +272
for await (const event of matrixRoom.getMemberJoinEvents()) {
console.log('emitting another join', event.toStrippedJson());
await FederationMatrix.emitJoin(event);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Replace console.log with proper logger and add error handling.

The loop has several issues:

  1. Line 270 uses console.log instead of a proper logger
  2. No error handling for FederationMatrix.emitJoin - if it fails, remaining events won't be processed
  3. The log message "emitting another join" is informal

Apply this diff to use proper logging and add error handling:

 	await Room.addUserToRoom(internalRoomId, { _id: user._id }, { _id: senderUserId, username: inviteEvent.sender });
 
 	for await (const event of matrixRoom.getMemberJoinEvents()) {
-		console.log('emitting another join', event.toStrippedJson());
-		await FederationMatrix.emitJoin(event);
+		try {
+			await FederationMatrix.emitJoin(event);
+		} catch (error) {
+			console.error('Failed to emit join event', { eventId: event.eventId, error });
+		}
 	}
 }

Note: If a proper logger instance is available in this scope, replace console.error with the logger.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
for await (const event of matrixRoom.getMemberJoinEvents()) {
console.log('emitting another join', event.toStrippedJson());
await FederationMatrix.emitJoin(event);
}
await Room.addUserToRoom(internalRoomId, { _id: user._id }, { _id: senderUserId, username: inviteEvent.sender });
for await (const event of matrixRoom.getMemberJoinEvents()) {
- console.log('emitting another join', event.toStrippedJson());
try {
await FederationMatrix.emitJoin(event);
} catch (error) {
console.error('Failed to emit join event', { eventId: event.eventId, error });
}
}
}
🤖 Prompt for AI Agents
In ee/packages/federation-matrix/src/api/_matrix/invite.ts around lines 269 to
272, replace the informal console.log with the module logger (or console.error
only if no logger in scope) and wrap the await FederationMatrix.emitJoin(event)
call in a try/catch so a failure for one event does not stop processing the
rest; on success use a clear info/debug log (include context like room id,
member and event id/stripped JSON) and on error log an error-level message
including the caught error and the same context, then continue to the next
event.

Comment on lines +983 to +998
async emitJoin(membershipEvent: PersistentEventBase<RoomVersion, 'm.room.member'>) {
if (!this.homeserverServices) {
this.logger.warn('Homeserver services not available, skipping user role room scoped');
return;
}

this.homeserverServices.emitter.emit('homeserver.matrix.membership', {
event_id: membershipEvent.eventId,
event: membershipEvent.event,
room_id: membershipEvent.roomId,
state_key: membershipEvent.stateKey,
content: { membership: 'join' },
sender: membershipEvent.sender,
origin_server_ts: Date.now(),
});
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Fix warning message and preserve original event data.

The method has several issues:

  1. Line 985: The warning message is copy-pasted from another method and says "skipping user role room scoped" instead of something relevant like "skipping join event emission"
  2. Line 994: The content hardcodes membership: 'join', but this should preserve the original membership state from the event parameter
  3. Line 996: Using Date.now() for origin_server_ts loses the original event timestamp

Apply this diff to fix these issues:

 	async emitJoin(membershipEvent: PersistentEventBase<RoomVersion, 'm.room.member'>) {
 		if (!this.homeserverServices) {
-			this.logger.warn('Homeserver services not available, skipping user role room scoped');
+			this.logger.warn('Homeserver services not available, skipping join event emission');
 			return;
 		}
 
 		this.homeserverServices.emitter.emit('homeserver.matrix.membership', {
 			event_id: membershipEvent.eventId,
 			event: membershipEvent.event,
 			room_id: membershipEvent.roomId,
 			state_key: membershipEvent.stateKey,
-			content: { membership: 'join' },
+			content: membershipEvent.event.content,
 			sender: membershipEvent.sender,
-			origin_server_ts: Date.now(),
+			origin_server_ts: membershipEvent.event.origin_server_ts,
 		});
 	}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async emitJoin(membershipEvent: PersistentEventBase<RoomVersion, 'm.room.member'>) {
if (!this.homeserverServices) {
this.logger.warn('Homeserver services not available, skipping user role room scoped');
return;
}
this.homeserverServices.emitter.emit('homeserver.matrix.membership', {
event_id: membershipEvent.eventId,
event: membershipEvent.event,
room_id: membershipEvent.roomId,
state_key: membershipEvent.stateKey,
content: { membership: 'join' },
sender: membershipEvent.sender,
origin_server_ts: Date.now(),
});
}
async emitJoin(membershipEvent: PersistentEventBase<RoomVersion, 'm.room.member'>) {
if (!this.homeserverServices) {
this.logger.warn('Homeserver services not available, skipping join event emission');
return;
}
this.homeserverServices.emitter.emit('homeserver.matrix.membership', {
event_id: membershipEvent.eventId,
event: membershipEvent.event,
room_id: membershipEvent.roomId,
state_key: membershipEvent.stateKey,
content: membershipEvent.event.content,
sender: membershipEvent.sender,
origin_server_ts: membershipEvent.event.origin_server_ts,
});
}
🤖 Prompt for AI Agents
In ee/packages/federation-matrix/src/FederationMatrix.ts around lines 983 to
998, fix the incorrect warning text and preserve original event data when
emitting joins: change the logger.warn text to something like "Homeserver
services not available, skipping join event emission"; set the emitted
content.membership to the membership value from the incoming event (e.g.
membershipEvent.event.content.membership) instead of hardcoding 'join'; and set
origin_server_ts to the original event timestamp (e.g.
membershipEvent.event.origin_server_ts or equivalent field on the event) rather
than using Date.now() so the original event time is preserved.

inviteUsersToRoom(room: IRoomFederated, usersUserName: string[], inviter: IUser): Promise<void>;
notifyUserTyping(rid: string, user: string, isTyping: boolean): Promise<void>;
verifyMatrixIds(matrixIds: string[]): Promise<{ [key: string]: string }>;
emitJoin(membershipEvent: any): Promise<void>;
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Replace any with strongly-typed parameter.

The interface uses any for the membershipEvent parameter, but the implementation in FederationMatrix.ts (lines 983-998) uses the specific type PersistentEventBase<RoomVersion, 'm.room.member'>. This type mismatch defeats TypeScript's type safety.

Apply this diff to align the interface with the implementation:

+import type { PersistentEventBase, RoomVersion } from '@rocket.chat/federation-sdk';
+
 export interface IFederationMatrixService {
 	createRoom(room: IRoomFederated, owner: IUser, members: string[]): Promise<{ room_id: string; event_id: string }>;
 	ensureFederatedUsersExistLocally(members: string[]): Promise<void>;
 	createDirectMessageRoom(room: IRoomFederated, members: IUser[], creatorId: IUser['_id']): Promise<void>;
 	sendMessage(message: IMessage, room: IRoomFederated, user: IUser): Promise<void>;
 	deleteMessage(matrixRoomId: string, message: IMessage): Promise<void>;
 	sendReaction(messageId: string, reaction: string, user: IUser): Promise<void>;
 	removeReaction(messageId: string, reaction: string, user: IUser, oldMessage: IMessage): Promise<void>;
 	getEventById(eventId: string): Promise<any | null>;
 	leaveRoom(rid: IRoomFederated['_id'], user: IUser): Promise<void>;
 	kickUser(room: IRoomNativeFederated, removedUser: IUser, userWhoRemoved: IUser): Promise<void>;
 	updateMessage(room: IRoomNativeFederated, message: IMessage): Promise<void>;
 	updateRoomName(rid: string, displayName: string, user: IUser): Promise<void>;
 	updateRoomTopic(room: IRoomNativeFederated, topic: string, user: IUser): Promise<void>;
 	addUserRoleRoomScoped(
 		room: IRoomNativeFederated,
 		senderId: string,
 		userId: string,
 		role: 'moderator' | 'owner' | 'leader' | 'user',
 	): Promise<void>;
 	inviteUsersToRoom(room: IRoomFederated, usersUserName: string[], inviter: IUser): Promise<void>;
 	notifyUserTyping(rid: string, user: string, isTyping: boolean): Promise<void>;
 	verifyMatrixIds(matrixIds: string[]): Promise<{ [key: string]: string }>;
-	emitJoin(membershipEvent: any): Promise<void>;
+	emitJoin(membershipEvent: PersistentEventBase<RoomVersion, 'm.room.member'>): Promise<void>;
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
emitJoin(membershipEvent: any): Promise<void>;
import type { PersistentEventBase, RoomVersion } from '@rocket.chat/federation-sdk';
export interface IFederationMatrixService {
createRoom(room: IRoomFederated, owner: IUser, members: string[]): Promise<{ room_id: string; event_id: string }>;
ensureFederatedUsersExistLocally(members: string[]): Promise<void>;
createDirectMessageRoom(room: IRoomFederated, members: IUser[], creatorId: IUser['_id']): Promise<void>;
sendMessage(message: IMessage, room: IRoomFederated, user: IUser): Promise<void>;
deleteMessage(matrixRoomId: string, message: IMessage): Promise<void>;
sendReaction(messageId: string, reaction: string, user: IUser): Promise<void>;
removeReaction(messageId: string, reaction: string, user: IUser, oldMessage: IMessage): Promise<void>;
getEventById(eventId: string): Promise<any | null>;
leaveRoom(rid: IRoomFederated['_id'], user: IUser): Promise<void>;
kickUser(room: IRoomNativeFederated, removedUser: IUser, userWhoRemoved: IUser): Promise<void>;
updateMessage(room: IRoomNativeFederated, message: IMessage): Promise<void>;
updateRoomName(rid: string, displayName: string, user: IUser): Promise<void>;
updateRoomTopic(room: IRoomNativeFederated, topic: string, user: IUser): Promise<void>;
addUserRoleRoomScoped(
room: IRoomNativeFederated,
senderId: string,
userId: string,
role: 'moderator' | 'owner' | 'leader' | 'user',
): Promise<void>;
inviteUsersToRoom(room: IRoomFederated, usersUserName: string[], inviter: IUser): Promise<void>;
notifyUserTyping(rid: string, user: string, isTyping: boolean): Promise<void>;
verifyMatrixIds(matrixIds: string[]): Promise<{ [key: string]: string }>;
emitJoin(membershipEvent: PersistentEventBase<RoomVersion, 'm.room.member'>): Promise<void>;
}
🤖 Prompt for AI Agents
In packages/core-services/src/types/IFederationMatrixService.ts around line 26,
the emitJoin(membershipEvent: any) signature uses any but the implementation
expects PersistentEventBase<RoomVersion, 'm.room.member'>; change the parameter
type to PersistentEventBase<RoomVersion, 'm.room.member'> and add the necessary
imports (PersistentEventBase and RoomVersion) from their source module so the
interface matches FederationMatrix.ts implementation and preserves TypeScript
type safety.

@ggazzo ggazzo merged commit 9669ff6 into chore/fed-branded Oct 7, 2025
71 of 73 checks passed
@ggazzo ggazzo deleted the fix/process-joins-from-full-state branch October 7, 2025 16:35
ggazzo added a commit that referenced this pull request Oct 8, 2025
ggazzo added a commit that referenced this pull request Oct 8, 2025
ggazzo added a commit that referenced this pull request Oct 8, 2025
ggazzo added a commit that referenced this pull request Oct 8, 2025
ggazzo added a commit that referenced this pull request Oct 8, 2025
ggazzo added a commit that referenced this pull request Oct 8, 2025
ricardogarim pushed a commit that referenced this pull request Oct 8, 2025
sampaiodiego added a commit that referenced this pull request Oct 9, 2025
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