backend: Enhance recording service to support field filtering in getRecording and getAllRecordings methods
This commit is contained in:
parent
18e0fe6a64
commit
a0b7d42002
@ -70,10 +70,12 @@ export const getRecording = async (req: Request, res: Response) => {
|
|||||||
const logger = container.get(LoggerService);
|
const logger = container.get(LoggerService);
|
||||||
const recordingService = container.get(RecordingService);
|
const recordingService = container.get(RecordingService);
|
||||||
const recordingId = req.params.recordingId;
|
const recordingId = req.params.recordingId;
|
||||||
|
const fields = req.query.fields as string | undefined;
|
||||||
|
|
||||||
logger.info(`Getting recording ${recordingId}`);
|
logger.info(`Getting recording ${recordingId}`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const recordingInfo = await recordingService.getRecording(recordingId);
|
const recordingInfo = await recordingService.getRecording(recordingId, fields);
|
||||||
return res.status(200).json(recordingInfo);
|
return res.status(200).json(recordingInfo);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof OpenViduMeetError) {
|
if (error instanceof OpenViduMeetError) {
|
||||||
|
|||||||
@ -1,18 +1,33 @@
|
|||||||
|
import { MeetRecordingInfo, MeetRoom } from '@typings-ce';
|
||||||
|
|
||||||
export class UtilsHelper {
|
export class UtilsHelper {
|
||||||
private constructor() {
|
// Prevent instantiation of this utility class.
|
||||||
// Prevent instantiation of this utility class
|
private constructor() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filters the fields of an object based on a list of keys.
|
||||||
|
*
|
||||||
|
* @param obj - The object to filter (it can be a MeetRoom or MeetRecordingInfo).
|
||||||
|
* @param fields - A comma-separated string or an array of field names to keep.
|
||||||
|
* @returns A new object containing only the specified keys.
|
||||||
|
*/
|
||||||
|
static filterObjectFields<T extends MeetRecordingInfo | MeetRoom>(obj: T, fields?: string | string[]): Partial<T> {
|
||||||
|
// If no fields are provided, return the full object.
|
||||||
|
if (!fields || (typeof fields === 'string' && fields.trim().length === 0)) {
|
||||||
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
static filterObjectFields = (obj: Record<string, unknown>, fields: string[]): Record<string, any> => {
|
// Convert the string to an array if necessary.
|
||||||
return fields.reduce(
|
const fieldsArray = Array.isArray(fields) ? fields : fields.split(',').map((f) => f.trim());
|
||||||
(acc, field) => {
|
|
||||||
|
// Reduce the object by only including the specified keys.
|
||||||
|
return fieldsArray.reduce((acc, field) => {
|
||||||
if (Object.prototype.hasOwnProperty.call(obj, field)) {
|
if (Object.prototype.hasOwnProperty.call(obj, field)) {
|
||||||
acc[field] = obj[field];
|
// Use keyof T to properly type the field access
|
||||||
|
acc[field as keyof T] = obj[field as keyof T];
|
||||||
}
|
}
|
||||||
|
|
||||||
return acc;
|
return acc;
|
||||||
},
|
}, {} as Partial<T>);
|
||||||
{} as Record<string, unknown>
|
}
|
||||||
);
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -52,7 +52,8 @@ const GetRecordingsFiltersSchema: z.ZodType<MeetRecordingFilters> = z.object({
|
|||||||
.default(10),
|
.default(10),
|
||||||
// status: z.string().optional(),
|
// status: z.string().optional(),
|
||||||
roomId: z.string().optional(),
|
roomId: z.string().optional(),
|
||||||
nextPageToken: z.string().optional()
|
nextPageToken: z.string().optional(),
|
||||||
|
fields: z.string().optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const withValidStartRecordingRequest = (req: Request, res: Response, next: NextFunction) => {
|
export const withValidStartRecordingRequest = (req: Request, res: Response, next: NextFunction) => {
|
||||||
@ -77,7 +78,7 @@ export const withValidRecordingId = (req: Request, res: Response, next: NextFunc
|
|||||||
next();
|
next();
|
||||||
};
|
};
|
||||||
|
|
||||||
export const withValidGetRecordingsRequest = (req: Request, res: Response, next: NextFunction) => {
|
export const withValidRecordingFiltersRequest = (req: Request, res: Response, next: NextFunction) => {
|
||||||
const { success, error, data } = GetRecordingsFiltersSchema.safeParse(req.query);
|
const { success, error, data } = GetRecordingsFiltersSchema.safeParse(req.query);
|
||||||
|
|
||||||
if (!success) {
|
if (!success) {
|
||||||
|
|||||||
@ -8,7 +8,7 @@ import {
|
|||||||
tokenAndRoleValidator,
|
tokenAndRoleValidator,
|
||||||
withRecordingEnabled,
|
withRecordingEnabled,
|
||||||
withCorrectPermissions,
|
withCorrectPermissions,
|
||||||
withValidGetRecordingsRequest,
|
withValidRecordingFiltersRequest,
|
||||||
withValidRecordingBulkDeleteRequest,
|
withValidRecordingBulkDeleteRequest,
|
||||||
withValidRecordingId,
|
withValidRecordingId,
|
||||||
withValidStartRecordingRequest,
|
withValidStartRecordingRequest,
|
||||||
@ -35,7 +35,7 @@ recordingRouter.get(
|
|||||||
recordingRouter.get(
|
recordingRouter.get(
|
||||||
'/',
|
'/',
|
||||||
withAuth(apiKeyValidator, tokenAndRoleValidator(UserRole.ADMIN)),
|
withAuth(apiKeyValidator, tokenAndRoleValidator(UserRole.ADMIN)),
|
||||||
withValidGetRecordingsRequest,
|
withValidRecordingFiltersRequest,
|
||||||
recordingCtrl.getRecordings
|
recordingCtrl.getRecordings
|
||||||
);
|
);
|
||||||
recordingRouter.delete(
|
recordingRouter.delete(
|
||||||
|
|||||||
@ -37,6 +37,7 @@ import { MeetLock } from '../helpers/redis.helper.js';
|
|||||||
import { IScheduledTask, TaskSchedulerService } from './task-scheduler.service.js';
|
import { IScheduledTask, TaskSchedulerService } from './task-scheduler.service.js';
|
||||||
import { SystemEventService } from './system-event.service.js';
|
import { SystemEventService } from './system-event.service.js';
|
||||||
import { SystemEventType } from '../models/system-event.model.js';
|
import { SystemEventType } from '../models/system-event.model.js';
|
||||||
|
import { UtilsHelper } from '../helpers/utils.helper.js';
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class RecordingService {
|
export class RecordingService {
|
||||||
@ -228,10 +229,10 @@ export class RecordingService {
|
|||||||
* @param recordingId - The unique identifier of the recording.
|
* @param recordingId - The unique identifier of the recording.
|
||||||
* @returns A promise that resolves to a MeetRecordingInfo object.
|
* @returns A promise that resolves to a MeetRecordingInfo object.
|
||||||
*/
|
*/
|
||||||
async getRecording(recordingId: string): Promise<MeetRecordingInfo> {
|
async getRecording(recordingId: string, fields?: string): Promise<MeetRecordingInfo> {
|
||||||
const { recordingInfo } = await this.getMeetRecordingInfoFromMetadata(recordingId);
|
const { recordingInfo } = await this.getMeetRecordingInfoFromMetadata(recordingId);
|
||||||
|
|
||||||
return recordingInfo;
|
return UtilsHelper.filterObjectFields(recordingInfo, fields) as MeetRecordingInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -245,7 +246,7 @@ export class RecordingService {
|
|||||||
* - `nextPageToken`: (Optional) A token to retrieve the next page of results, if available.
|
* - `nextPageToken`: (Optional) A token to retrieve the next page of results, if available.
|
||||||
* @throws Will throw an error if there is an issue retrieving the recordings.
|
* @throws Will throw an error if there is an issue retrieving the recordings.
|
||||||
*/
|
*/
|
||||||
async getAllRecordings({ maxItems, nextPageToken, roomId }: MeetRecordingFilters): Promise<{
|
async getAllRecordings({ maxItems, nextPageToken, roomId, fields }: MeetRecordingFilters): Promise<{
|
||||||
recordings: MeetRecordingInfo[];
|
recordings: MeetRecordingInfo[];
|
||||||
isTruncated: boolean;
|
isTruncated: boolean;
|
||||||
nextPageToken?: string;
|
nextPageToken?: string;
|
||||||
@ -274,7 +275,9 @@ export class RecordingService {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const recordings: MeetRecordingInfo[] = await Promise.all(promises);
|
let recordings: MeetRecordingInfo[] = await Promise.all(promises);
|
||||||
|
|
||||||
|
recordings = recordings.map((rec) => UtilsHelper.filterObjectFields(rec, fields)) as MeetRecordingInfo[];
|
||||||
|
|
||||||
this.logger.info(`Retrieved ${recordings.length} recordings.`);
|
this.logger.info(`Retrieved ${recordings.length} recordings.`);
|
||||||
// Return the paginated list of recordings
|
// Return the paginated list of recordings
|
||||||
|
|||||||
@ -89,7 +89,7 @@ export class RoomService {
|
|||||||
metadata: JSON.stringify({
|
metadata: JSON.stringify({
|
||||||
createdBy: MEET_NAME_ID,
|
createdBy: MEET_NAME_ID,
|
||||||
roomOptions: MeetRoomHelper.toOpenViduOptions(meetRoom)
|
roomOptions: MeetRoomHelper.toOpenViduOptions(meetRoom)
|
||||||
}),
|
})
|
||||||
//TODO: Uncomment this when bug in LiveKit is fixed
|
//TODO: Uncomment this when bug in LiveKit is fixed
|
||||||
// When it is defined, the room will be closed although there are participants
|
// When it is defined, the room will be closed although there are participants
|
||||||
// emptyTimeout: ms('20s') / 1000,
|
// emptyTimeout: ms('20s') / 1000,
|
||||||
@ -129,13 +129,8 @@ export class RoomService {
|
|||||||
}> {
|
}> {
|
||||||
const response = await this.storageService.getMeetRooms(maxItems, nextPageToken);
|
const response = await this.storageService.getMeetRooms(maxItems, nextPageToken);
|
||||||
|
|
||||||
if (fields && fields.length > 0) {
|
const filteredRooms = response.rooms.map((room) => UtilsHelper.filterObjectFields(room, fields));
|
||||||
const fieldsArray = Array.isArray(fields) ? fields : fields.split(',').map((f) => f.trim());
|
|
||||||
const filteredRooms = response.rooms.map((room) =>
|
|
||||||
UtilsHelper.filterObjectFields(room as unknown as Record<string, unknown>, fieldsArray)
|
|
||||||
);
|
|
||||||
response.rooms = filteredRooms as MeetRoom[];
|
response.rooms = filteredRooms as MeetRoom[];
|
||||||
}
|
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
@ -149,16 +144,7 @@ export class RoomService {
|
|||||||
async getMeetRoom(roomId: string, fields?: string): Promise<MeetRoom> {
|
async getMeetRoom(roomId: string, fields?: string): Promise<MeetRoom> {
|
||||||
const meetRoom = await this.storageService.getMeetRoom(roomId);
|
const meetRoom = await this.storageService.getMeetRoom(roomId);
|
||||||
|
|
||||||
if (fields && fields.length > 0) {
|
return UtilsHelper.filterObjectFields(meetRoom, fields) as MeetRoom;
|
||||||
const fieldsArray = Array.isArray(fields) ? fields : fields.split(',').map((f) => f.trim());
|
|
||||||
const filteredRoom = UtilsHelper.filterObjectFields(
|
|
||||||
meetRoom as unknown as Record<string, unknown>,
|
|
||||||
fieldsArray
|
|
||||||
);
|
|
||||||
return filteredRoom as MeetRoom;
|
|
||||||
}
|
|
||||||
|
|
||||||
return meetRoom;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user