backend: enhance StorageProvider interface with recording metadata methods and type parameters

This commit is contained in:
Carlos Santos 2025-05-28 11:56:41 +02:00
parent 172e8edcfd
commit b9a11dd45d
2 changed files with 75 additions and 27 deletions

View File

@ -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<G extends GlobalPreferences = GlobalPreferences, R extends MeetRoom = MeetRoom>
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<G extends GlobalPreferences = GlobalPreferences,
*
* @param defaultPreferences - The default preferences to initialize with.
*/
async initialize(defaultPreferences: G): Promise<void> {
async initialize(defaultPreferences: GPrefs): Promise<void> {
try {
const existingPreferences = await this.getGlobalPreferences();
@ -77,14 +80,14 @@ export class S3StorageProvider<G extends GlobalPreferences = GlobalPreferences,
*
* @returns A promise that resolves to the global preferences or null if not found.
*/
async getGlobalPreferences(): Promise<G | null> {
async getGlobalPreferences(): Promise<GPrefs | null> {
try {
// Try to get preferences from Redis cache
let preferences: G | null = await this.getFromRedis<G>(RedisKeyName.GLOBAL_PREFERENCES);
let preferences: GPrefs | null = await this.getFromRedis<GPrefs>(RedisKeyName.GLOBAL_PREFERENCES);
if (!preferences) {
this.logger.debug('Global preferences not found in Redis. Fetching from S3...');
preferences = await this.getFromS3<G>(this.S3_GLOBAL_PREFERENCES_KEY);
preferences = await this.getFromS3<GPrefs>(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<G extends GlobalPreferences = GlobalPreferences,
* @returns The saved preferences.
* @throws Rethrows any error if saving fails.
*/
async saveGlobalPreferences(preferences: G): Promise<G> {
async saveGlobalPreferences(preferences: GPrefs): Promise<GPrefs> {
try {
const redisPayload = JSON.stringify(preferences);
@ -136,7 +139,7 @@ export class S3StorageProvider<G extends GlobalPreferences = GlobalPreferences,
* @returns The saved room if both operations succeed.
* @throws The error from the first failed operation.
*/
async saveMeetRoom(meetRoom: R): Promise<R> {
async saveMeetRoom(meetRoom: MRoom): Promise<MRoom> {
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<G extends GlobalPreferences = GlobalPreferences,
maxItems: number,
nextPageToken?: string
): Promise<{
rooms: R[];
rooms: MRoom[];
isTruncated: boolean;
nextPageToken?: string;
}> {
@ -208,7 +211,7 @@ export class S3StorageProvider<G extends GlobalPreferences = GlobalPreferences,
);
// Filter out null values
const validRooms = rooms.filter((room) => 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<G extends GlobalPreferences = GlobalPreferences,
}
}
async getMeetRoom(roomId: string): Promise<R | null> {
async getMeetRoom(roomId: string): Promise<MRoom | null> {
try {
// Try to get room preferences from Redis cache
const room: R | null = await this.getFromRedis<R>(roomId);
const room: MRoom | null = await this.getFromRedis<MRoom>(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<R>(s3RoomPath);
return await this.getFromS3<MRoom>(s3RoomPath);
}
this.logger.debug(`Room ${roomId} verified in Redis`);
@ -251,10 +254,10 @@ export class S3StorageProvider<G extends GlobalPreferences = GlobalPreferences,
}
}
async getArchivedRoomMetadata(roomId: string): Promise<Partial<R> | null> {
async getArchivedRoomMetadata(roomId: string): Promise<Partial<MRoom> | null> {
try {
const filePath = `${INTERNAL_CONFIG.S3_RECORDINGS_PREFIX}/.room_metadata/${roomId}/room_metadata.json`;
const roomMetadata = await this.getFromS3<Partial<R>>(filePath);
const roomMetadata = await this.getFromS3<Partial<MRoom>>(filePath);
if (!roomMetadata) {
this.logger.warn(`Room metadata not found for room ${roomId} in recordings bucket`);
@ -345,6 +348,28 @@ export class S3StorageProvider<G extends GlobalPreferences = GlobalPreferences,
}
}
async deleteArchivedRoomMetadata(roomId: string): Promise<void> {
//TODO : Implement this method to delete archived room metadata
this.logger.warn('deleteArchivedRoomMetadata is not implemented yet');
}
async getRecordingMetadata(recordingId: string): Promise<MRec | null> {
//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<MRec> {
//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<void> {
//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.

View File

@ -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<GPrefs extends GlobalPreferences = GlobalPreferences, MRoom extends MeetRoom = MeetRoom> {
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<GPrefs extends GlobalPreferences = GlobalPrefer
*/
updateArchivedRoomMetadata(roomId: string): Promise<void>;
//TODO:
// deleteArchivedRoomMetadata(roomId: string): Promise<void>;
/**
* Deletes the archived metadata for a specific room.
*
* @param roomId - The room ID to delete the archived metadata for.
*/
deleteArchivedRoomMetadata(roomId: string): Promise<void>;
//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<MRec>;
//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<MRec | null>;
//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<void>;
}