backend: enhance StorageProvider interface with recording metadata methods and type parameters
This commit is contained in:
parent
172e8edcfd
commit
b9a11dd45d
@ -17,14 +17,17 @@ import { LoggerService, RedisService, S3Service, StorageProvider } from '../../i
|
|||||||
* The storage operations are performed in parallel to both systems when writing data,
|
* The storage operations are performed in parallel to both systems when writing data,
|
||||||
* with transaction-like rollback behavior if one operation fails.
|
* with transaction-like rollback behavior if one operation fails.
|
||||||
*
|
*
|
||||||
* @template G - Type for global preferences data, defaults to GlobalPreferences
|
* @template GPrefs - Type for global preferences data, defaults to GlobalPreferences
|
||||||
* @template R - Type for room data, defaults to MeetRoom
|
* @template MRoom - Type for room data, defaults to MeetRoom
|
||||||
*
|
*
|
||||||
* @implements {StorageProvider}
|
* @implements {StorageProvider}
|
||||||
*/
|
*/
|
||||||
@injectable()
|
@injectable()
|
||||||
export class S3StorageProvider<G extends GlobalPreferences = GlobalPreferences, R extends MeetRoom = MeetRoom>
|
export class S3StorageProvider<
|
||||||
implements StorageProvider
|
GPrefs extends GlobalPreferences = GlobalPreferences,
|
||||||
|
MRoom extends MeetRoom = MeetRoom,
|
||||||
|
MRec extends MeetRecordingInfo = MeetRecordingInfo
|
||||||
|
> implements StorageProvider
|
||||||
{
|
{
|
||||||
protected readonly S3_GLOBAL_PREFERENCES_KEY = `global-preferences.json`;
|
protected readonly S3_GLOBAL_PREFERENCES_KEY = `global-preferences.json`;
|
||||||
constructor(
|
constructor(
|
||||||
@ -39,7 +42,7 @@ export class S3StorageProvider<G extends GlobalPreferences = GlobalPreferences,
|
|||||||
*
|
*
|
||||||
* @param defaultPreferences - The default preferences to initialize with.
|
* @param defaultPreferences - The default preferences to initialize with.
|
||||||
*/
|
*/
|
||||||
async initialize(defaultPreferences: G): Promise<void> {
|
async initialize(defaultPreferences: GPrefs): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const existingPreferences = await this.getGlobalPreferences();
|
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.
|
* @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 {
|
||||||
// Try to get preferences from Redis cache
|
// 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) {
|
if (!preferences) {
|
||||||
this.logger.debug('Global preferences not found in Redis. Fetching from S3...');
|
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) {
|
if (preferences) {
|
||||||
this.logger.verbose('Fetched global preferences from S3. Caching them in Redis.');
|
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.
|
* @returns The saved preferences.
|
||||||
* @throws Rethrows any error if saving fails.
|
* @throws Rethrows any error if saving fails.
|
||||||
*/
|
*/
|
||||||
async saveGlobalPreferences(preferences: G): Promise<G> {
|
async saveGlobalPreferences(preferences: GPrefs): Promise<GPrefs> {
|
||||||
try {
|
try {
|
||||||
const redisPayload = JSON.stringify(preferences);
|
const redisPayload = JSON.stringify(preferences);
|
||||||
|
|
||||||
@ -136,7 +139,7 @@ export class S3StorageProvider<G extends GlobalPreferences = GlobalPreferences,
|
|||||||
* @returns The saved room if both operations succeed.
|
* @returns The saved room if both operations succeed.
|
||||||
* @throws The error from the first failed operation.
|
* @throws The error from the first failed operation.
|
||||||
*/
|
*/
|
||||||
async saveMeetRoom(meetRoom: R): Promise<R> {
|
async saveMeetRoom(meetRoom: MRoom): Promise<MRoom> {
|
||||||
const { roomId } = meetRoom;
|
const { roomId } = meetRoom;
|
||||||
const s3Path = `${INTERNAL_CONFIG.S3_ROOMS_PREFIX}/${roomId}/${roomId}.json`;
|
const s3Path = `${INTERNAL_CONFIG.S3_ROOMS_PREFIX}/${roomId}/${roomId}.json`;
|
||||||
const redisPayload = JSON.stringify(meetRoom);
|
const redisPayload = JSON.stringify(meetRoom);
|
||||||
@ -173,7 +176,7 @@ export class S3StorageProvider<G extends GlobalPreferences = GlobalPreferences,
|
|||||||
maxItems: number,
|
maxItems: number,
|
||||||
nextPageToken?: string
|
nextPageToken?: string
|
||||||
): Promise<{
|
): Promise<{
|
||||||
rooms: R[];
|
rooms: MRoom[];
|
||||||
isTruncated: boolean;
|
isTruncated: boolean;
|
||||||
nextPageToken?: string;
|
nextPageToken?: string;
|
||||||
}> {
|
}> {
|
||||||
@ -208,7 +211,7 @@ export class S3StorageProvider<G extends GlobalPreferences = GlobalPreferences,
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Filter out null values
|
// 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 };
|
return { rooms: validRooms, isTruncated: !!IsTruncated, nextPageToken: NextContinuationToken };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.handleError(error, 'Error fetching Room preferences');
|
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 {
|
||||||
// Try to get room preferences from Redis cache
|
// 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) {
|
if (!room) {
|
||||||
const s3RoomPath = `${INTERNAL_CONFIG.S3_ROOMS_PREFIX}/${roomId}/${roomId}.json`;
|
const s3RoomPath = `${INTERNAL_CONFIG.S3_ROOMS_PREFIX}/${roomId}/${roomId}.json`;
|
||||||
this.logger.debug(`Room ${roomId} not found in Redis. Fetching from S3 at ${s3RoomPath}...`);
|
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`);
|
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 {
|
try {
|
||||||
const filePath = `${INTERNAL_CONFIG.S3_RECORDINGS_PREFIX}/.room_metadata/${roomId}/room_metadata.json`;
|
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) {
|
if (!roomMetadata) {
|
||||||
this.logger.warn(`Room metadata not found for room ${roomId} in recordings bucket`);
|
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.
|
* Retrieves an object of type U from Redis by the given key.
|
||||||
* Returns null if the key is not found or an error occurs.
|
* Returns null if the key is not found or an error occurs.
|
||||||
|
|||||||
@ -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.
|
* 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
|
* of application settings and room information, which could be backed by
|
||||||
* various storage solutions (database, file system, cloud storage, etc.).
|
* 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.
|
* 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>;
|
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>;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user