From b9a11dd45d2a21c604ffebae3107975d1f58a785 Mon Sep 17 00:00:00 2001 From: Carlos Santos <4a.santos@gmail.com> Date: Wed, 28 May 2025 11:56:41 +0200 Subject: [PATCH] backend: enhance StorageProvider interface with recording metadata methods and type parameters --- .../storage/providers/s3-storage.provider.ts | 59 +++++++++++++------ .../src/services/storage/storage.interface.ts | 43 ++++++++++---- 2 files changed, 75 insertions(+), 27 deletions(-) diff --git a/backend/src/services/storage/providers/s3-storage.provider.ts b/backend/src/services/storage/providers/s3-storage.provider.ts index 8c4526b..4a961c1 100644 --- a/backend/src/services/storage/providers/s3-storage.provider.ts +++ b/backend/src/services/storage/providers/s3-storage.provider.ts @@ -17,14 +17,17 @@ import { LoggerService, RedisService, S3Service, StorageProvider } from '../../i * The storage operations are performed in parallel to both systems when writing data, * with transaction-like rollback behavior if one operation fails. * - * @template G - Type for global preferences data, defaults to GlobalPreferences - * @template R - Type for room data, defaults to MeetRoom + * @template GPrefs - Type for global preferences data, defaults to GlobalPreferences + * @template MRoom - Type for room data, defaults to MeetRoom * * @implements {StorageProvider} */ @injectable() -export class S3StorageProvider - implements StorageProvider +export class S3StorageProvider< + GPrefs extends GlobalPreferences = GlobalPreferences, + MRoom extends MeetRoom = MeetRoom, + MRec extends MeetRecordingInfo = MeetRecordingInfo +> implements StorageProvider { protected readonly S3_GLOBAL_PREFERENCES_KEY = `global-preferences.json`; constructor( @@ -39,7 +42,7 @@ export class S3StorageProvider { + async initialize(defaultPreferences: GPrefs): Promise { try { const existingPreferences = await this.getGlobalPreferences(); @@ -77,14 +80,14 @@ export class S3StorageProvider { + async getGlobalPreferences(): Promise { try { // Try to get preferences from Redis cache - let preferences: G | null = await this.getFromRedis(RedisKeyName.GLOBAL_PREFERENCES); + let preferences: GPrefs | null = await this.getFromRedis(RedisKeyName.GLOBAL_PREFERENCES); if (!preferences) { this.logger.debug('Global preferences not found in Redis. Fetching from S3...'); - preferences = await this.getFromS3(this.S3_GLOBAL_PREFERENCES_KEY); + preferences = await this.getFromS3(this.S3_GLOBAL_PREFERENCES_KEY); if (preferences) { this.logger.verbose('Fetched global preferences from S3. Caching them in Redis.'); @@ -112,7 +115,7 @@ export class S3StorageProvider { + async saveGlobalPreferences(preferences: GPrefs): Promise { try { const redisPayload = JSON.stringify(preferences); @@ -136,7 +139,7 @@ export class S3StorageProvider { + async saveMeetRoom(meetRoom: MRoom): Promise { const { roomId } = meetRoom; const s3Path = `${INTERNAL_CONFIG.S3_ROOMS_PREFIX}/${roomId}/${roomId}.json`; const redisPayload = JSON.stringify(meetRoom); @@ -173,7 +176,7 @@ export class S3StorageProvider { @@ -208,7 +211,7 @@ export class S3StorageProvider room !== null) as R[]; + const validRooms = rooms.filter((room) => room !== null) as MRoom[]; return { rooms: validRooms, isTruncated: !!IsTruncated, nextPageToken: NextContinuationToken }; } catch (error) { this.handleError(error, 'Error fetching Room preferences'); @@ -216,16 +219,16 @@ export class S3StorageProvider { + async getMeetRoom(roomId: string): Promise { try { // Try to get room preferences from Redis cache - const room: R | null = await this.getFromRedis(roomId); + const room: MRoom | null = await this.getFromRedis(roomId); if (!room) { const s3RoomPath = `${INTERNAL_CONFIG.S3_ROOMS_PREFIX}/${roomId}/${roomId}.json`; this.logger.debug(`Room ${roomId} not found in Redis. Fetching from S3 at ${s3RoomPath}...`); - return await this.getFromS3(s3RoomPath); + return await this.getFromS3(s3RoomPath); } this.logger.debug(`Room ${roomId} verified in Redis`); @@ -251,10 +254,10 @@ export class S3StorageProvider | null> { + async getArchivedRoomMetadata(roomId: string): Promise | null> { try { const filePath = `${INTERNAL_CONFIG.S3_RECORDINGS_PREFIX}/.room_metadata/${roomId}/room_metadata.json`; - const roomMetadata = await this.getFromS3>(filePath); + const roomMetadata = await this.getFromS3>(filePath); if (!roomMetadata) { this.logger.warn(`Room metadata not found for room ${roomId} in recordings bucket`); @@ -345,6 +348,28 @@ export class S3StorageProvider { + //TODO : Implement this method to delete archived room metadata + this.logger.warn('deleteArchivedRoomMetadata is not implemented yet'); + } + + async getRecordingMetadata(recordingId: string): Promise { + //TODO : Implement this method to retrieve recording metadata for a room + this.logger.warn('getRecordingMetadata is not implemented yet'); + return null; + } + + async saveRecordingMetadata(recordingInfo: MRec): Promise { + //TODO : Implement this method to save recording metadata for a room + this.logger.warn('saveMeetRecordingInfo is not implemented yet'); + return recordingInfo; + } + + async deleteRecordingMetadata(recordingId: string): Promise { + //TODO : Implement this method to delete recording metadata for a room + this.logger.warn('deleteRecordingMetadata is not implemented yet'); + } + /** * Retrieves an object of type U from Redis by the given key. * Returns null if the key is not found or an error occurs. diff --git a/backend/src/services/storage/storage.interface.ts b/backend/src/services/storage/storage.interface.ts index 660a1ae..2e287ca 100644 --- a/backend/src/services/storage/storage.interface.ts +++ b/backend/src/services/storage/storage.interface.ts @@ -1,4 +1,4 @@ -import { GlobalPreferences, MeetRoom } from '@typings-ce'; +import { GlobalPreferences, MeetRecordingInfo, MeetRoom } from '@typings-ce'; /** * An interface that defines the contract for storage providers in the OpenVidu Meet application. @@ -11,7 +11,11 @@ import { GlobalPreferences, MeetRoom } from '@typings-ce'; * of application settings and room information, which could be backed by * various storage solutions (database, file system, cloud storage, etc.). */ -export interface StorageProvider { +export interface StorageProvider< + GPrefs extends GlobalPreferences = GlobalPreferences, + MRoom extends MeetRoom = MeetRoom, + MRec extends MeetRecordingInfo = MeetRecordingInfo +> { /** * Initializes the storage with default preferences if they are not already set. * @@ -107,15 +111,34 @@ export interface StorageProvider; - //TODO: - // deleteArchivedRoomMetadata(roomId: string): Promise; + /** + * Deletes the archived metadata for a specific room. + * + * @param roomId - The room ID to delete the archived metadata for. + */ + deleteArchivedRoomMetadata(roomId: string): Promise; - //TODO: - // saveRecordingMetadata; + /** + * Saves the recording metadata. + * + * @param recordingInfo - The recording information to save. + * @returns A promise that resolves to the saved recording information. + */ + saveRecordingMetadata(recordingInfo: MRec): Promise; - //TODO: - // getRecordingMetadata; + /** + * Retrieves the recording metadata for a specific recording ID. + * + * @param recordingId - The unique identifier of the recording. + * @returns A promise that resolves to the recording metadata, or null if not found. + */ + getRecordingMetadata(recordingId: string): Promise; - //TODO: - // deleteRecordingMetadata; + /** + * Deletes the recording metadata for a specific recording ID. + * + * @param recordingId - The unique identifier of the recording to delete. + * @returns A promise that resolves when the deletion is complete. + */ + deleteRecordingMetadata(recordingId: string): Promise; }