backend: remove currentParticipantIdentity from room member schema and refactor RoomMemberService to use meberId as participant identity when joining a meeting. Update related tests
This commit is contained in:
parent
f61fa6183c
commit
70ca7a0fa9
@ -95,10 +95,6 @@ const MeetRoomMemberSchema = new Schema<MeetRoomMemberDocument>(
|
||||
permissionsUpdatedAt: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
currentParticipantIdentity: {
|
||||
type: String,
|
||||
required: false
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@ -158,9 +158,7 @@ export class LivekitWebhookService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Handles the 'participant_joined' event by gathering relevant room and participant information,
|
||||
* updating the room member's currentParticipantIdentity if applicable,
|
||||
* and sending a room status signal to OpenVidu components.
|
||||
* @param room - Information about the room where the participant joined.
|
||||
* @param participant - Information about the newly joined participant.
|
||||
@ -169,23 +167,6 @@ export class LivekitWebhookService {
|
||||
// Skip if the participant is not a standard participant
|
||||
if (!this.livekitService.isStandardParticipant(participant)) return;
|
||||
|
||||
// Update room member's currentParticipantIdentity if this is an identified member
|
||||
if (participant.metadata) {
|
||||
try {
|
||||
const metadata = JSON.parse(participant.metadata);
|
||||
|
||||
if (metadata.memberId) {
|
||||
await this.roomMemberService.updateCurrentParticipantIdentity(
|
||||
room.name,
|
||||
metadata.memberId,
|
||||
participant.identity
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
this.logger.warn(`Failed to set room member currentParticipantIdentity:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const { recordings } = await this.recordingService.getAllRecordings({ roomId: room.name });
|
||||
await this.frontendEventService.sendRoomStatusSignalToOpenViduComponents(
|
||||
@ -200,7 +181,6 @@ export class LivekitWebhookService {
|
||||
|
||||
/**
|
||||
* Handles the 'participant_left' event by gathering relevant room and participant information,
|
||||
* clearing the participant identity from the room member if applicable,
|
||||
* and releasing any reserved participant names.
|
||||
* @param room - Information about the room where the participant left.
|
||||
* @param participant - Information about the participant who left.
|
||||
@ -209,23 +189,6 @@ export class LivekitWebhookService {
|
||||
// Skip if the participant is not a standard participant
|
||||
if (!this.livekitService.isStandardParticipant(participant)) return;
|
||||
|
||||
// Clear room member's currentParticipantIdentity if this is an identified member
|
||||
if (participant.metadata) {
|
||||
try {
|
||||
const metadata = JSON.parse(participant.metadata);
|
||||
|
||||
if (metadata.memberId) {
|
||||
await this.roomMemberService.updateCurrentParticipantIdentity(
|
||||
room.name,
|
||||
metadata.memberId,
|
||||
undefined
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
this.logger.warn(`Failed to clear room member currentParticipantIdentity:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
// Release the participant's reserved name
|
||||
await this.roomMemberService.releaseParticipantName(room.name, participant.name);
|
||||
|
||||
@ -29,7 +29,8 @@ import {
|
||||
errorRoomMemberCannotBeOwnerOrAdmin,
|
||||
errorRoomMemberNotFound,
|
||||
errorUnauthorized,
|
||||
errorUserNotFound
|
||||
errorUserNotFound,
|
||||
OpenViduMeetError
|
||||
} from '../models/error.model.js';
|
||||
import { RoomMemberRepository } from '../repositories/room-member.repository.js';
|
||||
import { FrontendEventService } from './frontend-event.service.js';
|
||||
@ -228,27 +229,11 @@ export class RoomMemberService {
|
||||
|
||||
const updatedMember = await this.roomMemberRepository.update(member);
|
||||
|
||||
// If member is currently in a meeting, check if they still have permission to join
|
||||
if (updatedMember.currentParticipantIdentity) {
|
||||
const effectivePermissions = updatedMember.effectivePermissions;
|
||||
|
||||
if (!effectivePermissions.canJoinMeeting) {
|
||||
// Member lost permission to join meeting, kick them out
|
||||
try {
|
||||
await this.kickParticipantFromMeeting(roomId, updatedMember.currentParticipantIdentity);
|
||||
this.logger.info(
|
||||
`Kicked participant '${updatedMember.currentParticipantIdentity}' from meeting after losing canJoinMeeting permission (member '${memberId}' in room '${roomId}')`
|
||||
);
|
||||
} catch (error) {
|
||||
this.logger.warn(
|
||||
`Failed to kick participant '${updatedMember.currentParticipantIdentity}' from meeting after permission update:`,
|
||||
error
|
||||
);
|
||||
// Don't throw error, update was already saved
|
||||
}
|
||||
} else {
|
||||
// TODO: Notify participant of role/permission changes if currently in a meeting
|
||||
}
|
||||
// If member lost permission to join meeting, kick them out
|
||||
if (!updatedMember.effectivePermissions.canJoinMeeting) {
|
||||
await this.kickMembersFromMeetingInBatches(roomId, [memberId]);
|
||||
} else {
|
||||
// TODO: Notify participant of role/permission changes if currently in a meeting
|
||||
}
|
||||
|
||||
return updatedMember;
|
||||
@ -267,9 +252,10 @@ export class RoomMemberService {
|
||||
this.logger.verbose(`Updating effective permissions for all members in room '${roomId}'`);
|
||||
|
||||
const BATCH_SIZE = 20; // Process members in smaller batches
|
||||
let batchNumber = 0;
|
||||
let nextPageToken: string | undefined;
|
||||
let totalUpdated = 0;
|
||||
let batchNumber = 0;
|
||||
const totalMembers: MeetRoomMember[] = [];
|
||||
|
||||
do {
|
||||
batchNumber++;
|
||||
@ -283,6 +269,7 @@ export class RoomMemberService {
|
||||
maxItems: BATCH_SIZE,
|
||||
nextPageToken
|
||||
});
|
||||
totalMembers.push(...members);
|
||||
|
||||
if (members.length === 0) {
|
||||
break;
|
||||
@ -331,33 +318,14 @@ export class RoomMemberService {
|
||||
return;
|
||||
}
|
||||
|
||||
this.logger.info(`Successfully updated effective permissions for ${totalUpdated} members in room '${roomId}'`);
|
||||
}
|
||||
// Kick members who lost canJoinMeeting permission
|
||||
const membersToKick = totalMembers.filter((m) => !m.effectivePermissions.canJoinMeeting).map((m) => m.memberId);
|
||||
|
||||
/**
|
||||
* Updates the currentParticipantIdentity for a room member.
|
||||
*
|
||||
* @param roomId - The ID of the room
|
||||
* @param memberId - The ID of the member
|
||||
* @param participantIdentity - The participant identity to set (or undefined to clear it)
|
||||
*/
|
||||
async updateCurrentParticipantIdentity(
|
||||
roomId: string,
|
||||
memberId: string,
|
||||
participantIdentity: string | undefined
|
||||
): Promise<void> {
|
||||
const member = await this.getRoomMember(roomId, memberId);
|
||||
|
||||
if (!member) {
|
||||
this.logger.warn(
|
||||
`Cannot update currentParticipantIdentity: member '${memberId}' not found in room '${roomId}'`
|
||||
);
|
||||
return;
|
||||
if (membersToKick.length > 0) {
|
||||
await this.kickMembersFromMeetingInBatches(roomId, membersToKick);
|
||||
}
|
||||
|
||||
member.currentParticipantIdentity = participantIdentity;
|
||||
await this.roomMemberRepository.update(member);
|
||||
this.logger.info(`Updated currentParticipantIdentity for member '${memberId}' in room '${roomId}'`);
|
||||
this.logger.info(`Successfully updated effective permissions for ${totalUpdated} members in room '${roomId}'`);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -374,17 +342,7 @@ export class RoomMemberService {
|
||||
}
|
||||
|
||||
// If member is currently in a meeting, kick them out first
|
||||
if (member.currentParticipantIdentity) {
|
||||
try {
|
||||
await this.kickParticipantFromMeeting(roomId, member.currentParticipantIdentity);
|
||||
this.logger.info(
|
||||
`Kicked participant '${member.currentParticipantIdentity}' from meeting before deleting member '${memberId}'`
|
||||
);
|
||||
} catch (error) {
|
||||
this.logger.warn(`Failed to kick participant from meeting during member deletion:`, error);
|
||||
// Continue with deletion even if kick fails
|
||||
}
|
||||
}
|
||||
await this.kickMembersFromMeetingInBatches(roomId, [memberId]);
|
||||
|
||||
return this.roomMemberRepository.deleteByRoomAndMemberId(roomId, memberId);
|
||||
}
|
||||
@ -415,40 +373,8 @@ export class RoomMemberService {
|
||||
.map((id) => ({ memberId: id, error: 'Room member not found' }));
|
||||
|
||||
if (foundMemberIds.length > 0) {
|
||||
// Kick participants that are currently in a meeting
|
||||
const membersInMeeting = membersToDelete.filter((m) => m.currentParticipantIdentity);
|
||||
|
||||
if (membersInMeeting.length > 0) {
|
||||
const KICK_BATCH_SIZE = 10;
|
||||
|
||||
// Process kicks in batches to avoid overwhelming the system
|
||||
for (let i = 0; i < membersInMeeting.length; i += KICK_BATCH_SIZE) {
|
||||
const batch = membersInMeeting.slice(i, i + KICK_BATCH_SIZE);
|
||||
|
||||
await Promise.allSettled(
|
||||
batch.map(async (member) => {
|
||||
try {
|
||||
await this.kickParticipantFromMeeting(roomId, member.currentParticipantIdentity!);
|
||||
this.logger.info(
|
||||
`Kicked participant '${member.currentParticipantIdentity}' from meeting before deleting member '${member.memberId}'`
|
||||
);
|
||||
} catch (error) {
|
||||
this.logger.warn(
|
||||
`Failed to kick participant '${member.currentParticipantIdentity}' from meeting during bulk deletion:`,
|
||||
error
|
||||
);
|
||||
// Continue with deletion even if kick fails
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
this.logger.verbose(`Processed batch of ${batch.length} participant kicks for room '${roomId}'`);
|
||||
}
|
||||
|
||||
this.logger.info(
|
||||
`Kicked ${membersInMeeting.length} participant(s) from meeting before bulk deletion in room '${roomId}'`
|
||||
);
|
||||
}
|
||||
// Kick participants that are currently in a meeting before deletion
|
||||
await this.kickMembersFromMeetingInBatches(roomId, foundMemberIds);
|
||||
|
||||
await this.roomMemberRepository.deleteByRoomIdAndMemberIds(roomId, foundMemberIds);
|
||||
}
|
||||
@ -473,6 +399,7 @@ export class RoomMemberService {
|
||||
let customPermissions: Partial<MeetRoomMemberPermissions> | undefined = undefined;
|
||||
let effectivePermissions: MeetRoomMemberPermissions;
|
||||
let memberId: string | undefined;
|
||||
let userId: string | undefined;
|
||||
|
||||
if (secret) {
|
||||
// Case 1: Secret provided (Anonymous access or External Member)
|
||||
@ -510,6 +437,8 @@ export class RoomMemberService {
|
||||
throw errorUnauthorized();
|
||||
}
|
||||
|
||||
userId = user.userId;
|
||||
|
||||
// Check if user is admin or owner
|
||||
const isOwner = await this.roomService.isRoomOwner(roomId, user.userId);
|
||||
|
||||
@ -540,7 +469,8 @@ export class RoomMemberService {
|
||||
participantName,
|
||||
participantIdentity,
|
||||
customPermissions,
|
||||
memberId
|
||||
memberId,
|
||||
userId
|
||||
);
|
||||
}
|
||||
|
||||
@ -558,7 +488,8 @@ export class RoomMemberService {
|
||||
participantName: string,
|
||||
participantIdentity?: string,
|
||||
customPermissions?: Partial<MeetRoomMemberPermissions>,
|
||||
memberId?: string
|
||||
memberId?: string,
|
||||
userId?: string
|
||||
): Promise<string> {
|
||||
// Check that room is open
|
||||
const room = await this.roomService.getMeetRoom(roomId);
|
||||
@ -592,9 +523,16 @@ export class RoomMemberService {
|
||||
// Create the Livekit room if it doesn't exist
|
||||
await this.roomService.createLivekitRoom(roomId);
|
||||
|
||||
// Create a unique participant identity based on the participant name
|
||||
const identityPrefix = this.createParticipantIdentityPrefixFromName(participantName) || 'participant';
|
||||
participantIdentity = `${identityPrefix}-${uid(15)}`;
|
||||
if (memberId || userId) {
|
||||
// Use memberId as participant identity for identified members
|
||||
// (registered users or external members with a record in the database)
|
||||
// Use userId as participant identity for registered users without a member record
|
||||
participantIdentity = memberId || userId;
|
||||
} else {
|
||||
// For anonymous users, create a unique participant identity based on the provided participant name
|
||||
const identityPrefix = this.createParticipantIdentityPrefixFromName(participantName) || 'participant';
|
||||
participantIdentity = `${identityPrefix}-${uid(15)}`;
|
||||
}
|
||||
} else {
|
||||
// REFRESH MODE
|
||||
this.logger.verbose(
|
||||
@ -764,6 +702,73 @@ export class RoomMemberService {
|
||||
return livekitPermissions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Kicks multiple members from a meeting in batches.
|
||||
* This method processes the kicks in parallel batches to avoid overwhelming the system.
|
||||
*
|
||||
* @param roomId - The ID of the room
|
||||
* @param memberIds - Array of member IDs to kick from the meeting
|
||||
* @param batchSize - Number of kicks to process in parallel (default: 10)
|
||||
*/
|
||||
protected async kickMembersFromMeetingInBatches(
|
||||
roomId: string,
|
||||
memberIds: string[],
|
||||
batchSize = 10
|
||||
): Promise<void> {
|
||||
if (memberIds.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
let kickedCount = 0;
|
||||
let failedCount = 0;
|
||||
|
||||
// Process kicks in batches to avoid overwhelming the system
|
||||
for (let i = 0; i < memberIds.length; i += batchSize) {
|
||||
const batch = memberIds.slice(i, i + batchSize);
|
||||
|
||||
const results = await Promise.all(
|
||||
batch.map(async (memberId) => {
|
||||
try {
|
||||
await this.kickParticipantFromMeeting(roomId, memberId);
|
||||
this.logger.verbose(`Kicked participant '${memberId}' from meeting in room '${roomId}'`);
|
||||
return true;
|
||||
} catch (error) {
|
||||
const isParticipantNotFound = error instanceof OpenViduMeetError && error.statusCode === 404;
|
||||
|
||||
if (!isParticipantNotFound) {
|
||||
// Real error, log warning
|
||||
this.logger.warn(
|
||||
`Failed to kick participant '${memberId}' from meeting in room '${roomId}':`,
|
||||
error
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Participant not in meeting, nothing to do
|
||||
return true;
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
// Count only successful kicks and real failures (not "participant not found")
|
||||
results.forEach((result) => {
|
||||
if (result) {
|
||||
kickedCount++;
|
||||
} else {
|
||||
failedCount++;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (kickedCount > 0) {
|
||||
this.logger.info(`Kicked ${kickedCount} participant(s) from meeting in room '${roomId}'`);
|
||||
}
|
||||
|
||||
if (failedCount > 0) {
|
||||
this.logger.warn(`Failed to kick ${failedCount} participant(s) from meeting in room '${roomId}'`);
|
||||
}
|
||||
}
|
||||
|
||||
async kickParticipantFromMeeting(roomId: string, participantIdentity: string): Promise<void> {
|
||||
this.logger.verbose(`Kicking participant '${participantIdentity}' from room '${roomId}'`);
|
||||
return this.livekitService.deleteParticipant(roomId, participantIdentity);
|
||||
|
||||
@ -663,7 +663,9 @@ export const expectValidRoomMemberTokenResponse = (
|
||||
expect(decodedToken).toHaveProperty('name', participantName);
|
||||
expect(decodedToken).toHaveProperty('sub');
|
||||
|
||||
if (participantIdentityPrefix) {
|
||||
if (memberId) {
|
||||
expect(decodedToken.sub).toBe(memberId);
|
||||
} else if (participantIdentityPrefix) {
|
||||
expect(decodedToken.sub?.startsWith(participantIdentityPrefix)).toBe(true);
|
||||
}
|
||||
|
||||
|
||||
@ -1,11 +1,8 @@
|
||||
import { afterAll, beforeAll, describe, expect, it } from '@jest/globals';
|
||||
import { MeetRoomMember, MeetRoomMemberRole, MeetUserRole } from '@openvidu-meet/typings';
|
||||
import { container } from '../../../../src/config/dependency-injector.config.js';
|
||||
import { MEET_ENV } from '../../../../src/environment.js';
|
||||
import { OpenViduMeetError } from '../../../../src/models/error.model.js';
|
||||
import { RoomMemberRepository } from '../../../../src/repositories/room-member.repository.js';
|
||||
import { LiveKitService } from '../../../../src/services/livekit.service.js';
|
||||
import { TokenService } from '../../../../src/services/token.service.js';
|
||||
import { expectValidationError } from '../../../helpers/assertion-helpers.js';
|
||||
import {
|
||||
bulkDeleteRoomMembers,
|
||||
@ -15,12 +12,10 @@ import {
|
||||
deleteAllRooms,
|
||||
deleteAllUsers,
|
||||
disconnectFakeParticipants,
|
||||
generateRoomMemberTokenRequest,
|
||||
getRoomMember,
|
||||
getUser,
|
||||
joinFakeParticipant,
|
||||
startTestServer,
|
||||
updateParticipantMetadata
|
||||
startTestServer
|
||||
} from '../../../helpers/request-helpers.js';
|
||||
|
||||
describe('Bulk Delete Room Members API Tests', () => {
|
||||
@ -190,52 +185,13 @@ describe('Bulk Delete Room Members API Tests', () => {
|
||||
const member1 = member1Response.body as MeetRoomMember;
|
||||
const member2 = member2Response.body as MeetRoomMember;
|
||||
|
||||
// Generate tokens and join meeting for both
|
||||
const token1Response = await generateRoomMemberTokenRequest(roomId, {
|
||||
secret: member1.memberId,
|
||||
joinMeeting: true,
|
||||
participantName: 'Meeting Member 1'
|
||||
});
|
||||
const token2Response = await generateRoomMemberTokenRequest(roomId, {
|
||||
secret: member2.memberId,
|
||||
joinMeeting: true,
|
||||
participantName: 'Meeting Member 2'
|
||||
});
|
||||
// Participant identity is the same as memberId for members
|
||||
const participantIdentity1 = member1.memberId;
|
||||
const participantIdentity2 = member2.memberId;
|
||||
|
||||
// Get participant identities from tokens
|
||||
const tokenService = container.get(TokenService);
|
||||
const decodedToken1 = await tokenService.verifyToken(token1Response.body.token);
|
||||
const decodedToken2 = await tokenService.verifyToken(token2Response.body.token);
|
||||
const participantIdentity1 = decodedToken1.sub!;
|
||||
const participantIdentity2 = decodedToken2.sub!;
|
||||
|
||||
// Join fake participants and update metadata
|
||||
// Join fake participants to the room to simulate real join
|
||||
await joinFakeParticipant(roomId, participantIdentity1);
|
||||
await updateParticipantMetadata(roomId, participantIdentity1, {
|
||||
iat: Date.now(),
|
||||
livekitUrl: MEET_ENV.LIVEKIT_URL,
|
||||
roomId,
|
||||
memberId: member1.memberId,
|
||||
baseRole: MeetRoomMemberRole.SPEAKER,
|
||||
effectivePermissions: member1.effectivePermissions
|
||||
});
|
||||
|
||||
await joinFakeParticipant(roomId, participantIdentity2);
|
||||
await updateParticipantMetadata(roomId, participantIdentity2, {
|
||||
iat: Date.now(),
|
||||
livekitUrl: MEET_ENV.LIVEKIT_URL,
|
||||
roomId,
|
||||
memberId: member2.memberId,
|
||||
baseRole: MeetRoomMemberRole.MODERATOR,
|
||||
effectivePermissions: member2.effectivePermissions
|
||||
});
|
||||
|
||||
// Update room members currentParticipantIdentity
|
||||
const roomMemberRepository = container.get(RoomMemberRepository);
|
||||
member1.currentParticipantIdentity = participantIdentity1;
|
||||
member2.currentParticipantIdentity = participantIdentity2;
|
||||
await roomMemberRepository.update(member1);
|
||||
await roomMemberRepository.update(member2);
|
||||
|
||||
// Verify both participants exist
|
||||
const livekitService = container.get(LiveKitService);
|
||||
|
||||
@ -3,9 +3,7 @@ import { MeetRoomMember, MeetRoomMemberRole, MeetUserRole } from '@openvidu-meet
|
||||
import { container } from '../../../../src/config/dependency-injector.config.js';
|
||||
import { MEET_ENV } from '../../../../src/environment.js';
|
||||
import { OpenViduMeetError } from '../../../../src/models/error.model.js';
|
||||
import { RoomMemberRepository } from '../../../../src/repositories/room-member.repository.js';
|
||||
import { LiveKitService } from '../../../../src/services/livekit.service.js';
|
||||
import { TokenService } from '../../../../src/services/token.service.js';
|
||||
import {
|
||||
createRoom,
|
||||
createRoomMember,
|
||||
@ -14,7 +12,6 @@ import {
|
||||
deleteAllUsers,
|
||||
deleteRoomMember,
|
||||
disconnectFakeParticipants,
|
||||
generateRoomMemberTokenRequest,
|
||||
getRoomMember,
|
||||
getUser,
|
||||
joinFakeParticipant,
|
||||
@ -110,20 +107,8 @@ describe('Room Members API Tests', () => {
|
||||
const member = createResponse.body as MeetRoomMember;
|
||||
const memberId = member.memberId;
|
||||
|
||||
// Generate room member token for joining meeting
|
||||
const tokenResponse = await generateRoomMemberTokenRequest(roomId, {
|
||||
secret: memberId,
|
||||
joinMeeting: true,
|
||||
participantName: 'Test Member'
|
||||
});
|
||||
const roomMemberToken = tokenResponse.body.token;
|
||||
|
||||
// Get participant identity from token
|
||||
const tokenService = container.get(TokenService);
|
||||
const decodedToken = await tokenService.verifyToken(roomMemberToken);
|
||||
const participantIdentity = decodedToken.sub!;
|
||||
|
||||
// Join fake participant to the room and update metadata to simulate real join
|
||||
// Join fake participant to the room to simulate real join
|
||||
const participantIdentity = memberId; // Participant identity is the same as memberId for members
|
||||
await joinFakeParticipant(roomId, participantIdentity);
|
||||
await updateParticipantMetadata(roomId, participantIdentity, {
|
||||
iat: Date.now(),
|
||||
@ -134,11 +119,6 @@ describe('Room Members API Tests', () => {
|
||||
effectivePermissions: member.effectivePermissions
|
||||
});
|
||||
|
||||
// Update room member currentParticipantIdentity manually to simulate real join
|
||||
const roomMemberRepository = container.get(RoomMemberRepository);
|
||||
member.currentParticipantIdentity = participantIdentity;
|
||||
await roomMemberRepository.update(member);
|
||||
|
||||
// Verify participant exists before deletion
|
||||
const livekitService = container.get(LiveKitService);
|
||||
const participant = await livekitService.getParticipant(roomId, participantIdentity);
|
||||
|
||||
@ -1,11 +1,8 @@
|
||||
import { afterAll, beforeAll, describe, expect, it } from '@jest/globals';
|
||||
import { MeetRoomMember, MeetRoomMemberRole, MeetRoomRoles, MeetUserRole } from '@openvidu-meet/typings';
|
||||
import { container } from '../../../../src/config/dependency-injector.config.js';
|
||||
import { MEET_ENV } from '../../../../src/environment.js';
|
||||
import { OpenViduMeetError } from '../../../../src/models/error.model.js';
|
||||
import { RoomMemberRepository } from '../../../../src/repositories/room-member.repository.js';
|
||||
import { LiveKitService } from '../../../../src/services/livekit.service.js';
|
||||
import { TokenService } from '../../../../src/services/token.service.js';
|
||||
import { expectValidationError } from '../../../helpers/assertion-helpers.js';
|
||||
import {
|
||||
createRoom,
|
||||
@ -14,12 +11,10 @@ import {
|
||||
deleteAllRooms,
|
||||
deleteAllUsers,
|
||||
disconnectFakeParticipants,
|
||||
generateRoomMemberTokenRequest,
|
||||
getRoomMember,
|
||||
joinFakeParticipant,
|
||||
sleep,
|
||||
startTestServer,
|
||||
updateParticipantMetadata,
|
||||
updateRoomMember
|
||||
} from '../../../helpers/request-helpers.js';
|
||||
|
||||
@ -260,34 +255,9 @@ describe('Room Members API Tests', () => {
|
||||
const member = createResponse.body as MeetRoomMember;
|
||||
const memberId = member.memberId;
|
||||
|
||||
// Generate room member token for joining meeting
|
||||
const tokenResponse = await generateRoomMemberTokenRequest(roomId, {
|
||||
secret: memberId,
|
||||
joinMeeting: true,
|
||||
participantName: 'Test Member'
|
||||
});
|
||||
const roomMemberToken = tokenResponse.body.token;
|
||||
|
||||
// Get participant identity from token
|
||||
const tokenService = container.get(TokenService);
|
||||
const decodedToken = await tokenService.verifyToken(roomMemberToken);
|
||||
const participantIdentity = decodedToken.sub!;
|
||||
|
||||
// Join fake participant to the room and update metadata to simulate real join
|
||||
// Join fake participant to the room to simulate real join
|
||||
const participantIdentity = memberId; // Participant identity is the same as memberId for members
|
||||
await joinFakeParticipant(roomId, participantIdentity);
|
||||
await updateParticipantMetadata(roomId, participantIdentity, {
|
||||
iat: Date.now(),
|
||||
livekitUrl: MEET_ENV.LIVEKIT_URL,
|
||||
roomId,
|
||||
memberId,
|
||||
baseRole: MeetRoomMemberRole.SPEAKER,
|
||||
effectivePermissions: roomRoles.speaker.permissions
|
||||
});
|
||||
|
||||
// Update room member currentParticipantIdentity manually to simulate real join
|
||||
const roomMemberRepository = container.get(RoomMemberRepository);
|
||||
member.currentParticipantIdentity = participantIdentity;
|
||||
await roomMemberRepository.update(member);
|
||||
|
||||
// Verify participant exists before deletion
|
||||
const livekitService = container.get(LiveKitService);
|
||||
|
||||
@ -25,7 +25,6 @@ export interface MeetRoomMember {
|
||||
customPermissions?: Partial<MeetRoomMemberPermissions>; // Custom permissions for the member (if any)
|
||||
effectivePermissions: MeetRoomMemberPermissions; // Effective permissions for the member (base role + custom permissions)
|
||||
permissionsUpdatedAt: number; // Timestamp when the effective permissions were last updated
|
||||
currentParticipantIdentity?: string; // The participant identity if the member is currently in a meeting, undefined otherwise
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user