diff --git a/openvidu-testapp/src/app/services/test-feed.service.ts b/openvidu-testapp/src/app/services/test-feed.service.ts index d5c19a792..3948ea851 100755 --- a/openvidu-testapp/src/app/services/test-feed.service.ts +++ b/openvidu-testapp/src/app/services/test-feed.service.ts @@ -3,134 +3,7 @@ import { Subject } from "rxjs"; import * as stringify from "json-stringify-safe"; -import { - // Base classes - Session, - Stream, - Connection, - StreamManager, - Publisher, - Subscriber, - - // Base Event - Event, - - // Session Events - ConnectionEvent, - SessionDisconnectedEvent, - SignalEvent, - StreamEvent, - StreamPropertyChangedEvent, - ConnectionPropertyChangedEvent, // Missed previously - NetworkQualityLevelChangedEvent, // Missed previously - SpeechToTextEvent, // Missed previously - ExceptionEvent, - - // StreamManager / Publisher / Subscriber Events - StreamManagerEvent, - VideoElementEvent, - PublisherSpeakingEvent, - RecordingEvent, - FilterEvent, // Missed previously -} from "openvidu-browser-v2compatibility"; - -const API_ALLOWLIST = new Map([ - // =========================================================================== - // 1. CORE ENTITIES - // =========================================================================== - - // https://docs.openvidu.io/en/2.32.0/api/openvidu-browser/classes/Session.html - [Session, ["sessionId", "connection", "capabilities", "streamManagers"]], - - // https://docs.openvidu.io/en/2.32.0/api/openvidu-browser/classes/Connection.html - [Connection, ["connectionId", "creationTime", "data", "record", "role"]], - - // https://docs.openvidu.io/en/2.32.0/api/openvidu-browser/classes/Stream.html - [ - Stream, - [ - "streamId", - "creationTime", - "hasAudio", - "hasVideo", - "audioActive", - "videoActive", - "typeOfVideo", - "frameRate", - "videoDimensions", - "connection", - "filter", - ], - ], - - // https://docs.openvidu.io/en/2.32.0/api/openvidu-browser/classes/StreamManager.html - [StreamManager, ["stream", "id", "remote", "videos"]], - - // https://docs.openvidu.io/en/2.32.0/api/openvidu-browser/classes/Publisher.html - [Publisher, ["stream", "id", "remote", "videos", "accessAllowed"]], - - // https://docs.openvidu.io/en/2.32.0/api/openvidu-browser/classes/Subscriber.html - [Subscriber, ["stream", "id", "remote", "videos"]], - - // =========================================================================== - // 2. BASE EVENT - // =========================================================================== - // These are merged into all specific events - // https://docs.openvidu.io/en/2.32.0/api/openvidu-browser/classes/Event.html - [Event, ["type", "cancelable", "target"]], - - // =========================================================================== - // 3. SPECIFIC EVENTS (Alphabetical Order) - // =========================================================================== - - // https://docs.openvidu.io/en/2.32.0/api/openvidu-browser/classes/ConnectionEvent.html - [ConnectionEvent, ["connection", "reason"]], - - // https://docs.openvidu.io/en/2.32.0/api/openvidu-browser/classes/ConnectionPropertyChangedEvent.html - [ - ConnectionPropertyChangedEvent, - ["connection", "changedProperty", "oldValue", "newValue", "reason"], - ], - - // https://docs.openvidu.io/en/2.32.0/api/openvidu-browser/classes/ExceptionEvent.html - [ExceptionEvent, ["name", "message", "data"]], - - // https://docs.openvidu.io/en/2.32.0/api/openvidu-browser/classes/FilterEvent.html - [FilterEvent, ["filter", "eventType", "data"]], - - // https://docs.openvidu.io/en/2.32.0/api/openvidu-browser/classes/NetworkQualityLevelChangedEvent.html - [NetworkQualityLevelChangedEvent, ["connection", "newValue", "oldValue"]], - - // https://docs.openvidu.io/en/2.32.0/api/openvidu-browser/classes/PublisherSpeakingEvent.html - [PublisherSpeakingEvent, ["connection", "streamId", "reason"]], - - // https://docs.openvidu.io/en/2.32.0/api/openvidu-browser/classes/RecordingEvent.html - [RecordingEvent, ["id", "name", "reason"]], - - // https://docs.openvidu.io/en/2.32.0/api/openvidu-browser/classes/SessionDisconnectedEvent.html - [SessionDisconnectedEvent, ["reason"]], - - // https://docs.openvidu.io/en/2.32.0/api/openvidu-browser/classes/SignalEvent.html - [SignalEvent, ["type", "data", "from"]], - - // https://docs.openvidu.io/en/2.32.0/api/openvidu-browser/classes/SpeechToTextEvent.html - [SpeechToTextEvent, ["connection", "text", "reason", "timestamp", "raw"]], - - // https://docs.openvidu.io/en/2.32.0/api/openvidu-browser/classes/StreamEvent.html - [StreamEvent, ["stream", "reason"]], - - // https://docs.openvidu.io/en/2.32.0/api/openvidu-browser/classes/StreamManagerEvent.html - [StreamManagerEvent, ["stream", "reason"]], - - // https://docs.openvidu.io/en/2.32.0/api/openvidu-browser/classes/StreamPropertyChangedEvent.html - [ - StreamPropertyChangedEvent, - ["stream", "changedProperty", "oldValue", "newValue", "reason"], - ], - - // https://docs.openvidu.io/en/2.32.0/api/openvidu-browser/classes/VideoElementEvent.html - [VideoElementEvent, ["element"]], -]); +import { Event } from "openvidu-browser-v2compatibility"; @Injectable() export class TestFeedService { @@ -152,56 +25,139 @@ export class TestFeedService { return this.cleanEvent(event); } - cleanEvent(root: any): string { - const seen = new WeakSet(); + cleanEvent(event: any): string { + // 1. GLOBAL BLOCKLIST + const globalBlocklist = new Set([ + "ee", + "openvidu", + "userHandlerArrowHandler", + "handlers", + "reliableMessageBuffer", + "socket", + "loggerOptions", + "log", + "closingLock", + "connectionLock", + "offerLock", + "remoteOfferLock", + "disconnectLock", + "dataProcessLock", + "taskMutex", + "requestQueue", + "mutex", + "lock", + "publisherConnectionPromise", + "signalConnectedFuture", + "sifTrailer", + "enabledCodecs", + "supportedCodecs", + "enabledPublishCodecs", + "enabledPublishVideoCodecs", + "videoCaptureDefaults", + "audioCaptureDefaults", + "publishDefaults", + "reconnectPolicy", + "permissions", + "engine", + "client", + "signalClient", + "pcManager", + "ws", + "internalEmitter", + "publicEmmiter", + "codecs", + "layers", + "encodings", + "prevStats", + "monitorInterval", + "volumeMap", + "midToTrackId", + "audioContext", + "rtcConfig", + "options", + "connectOptions", + ]); - const getAllowedKeys = (obj: any): string[] | null => { - for (const [ClassConstructor, keys] of API_ALLOWLIST.entries()) { - if (obj instanceof ClassConstructor) { - // If instance of specific Event, merge with base Event keys - if (obj instanceof Event && ClassConstructor !== Event) { - return [...(API_ALLOWLIST.get(Event) || []), ...keys]; - } - return keys; - } - } - return null; + // 2. SCOPED BLOCKLIST (Parent -> Child removal) + const scopedBlocklist: Record> = { + session: new Set(["room", "openvidu"]), + target: new Set(["stream"]), + streamManagers: new Set(["stream"]) }; - const traverse = (current: any): any => { - if (current === null || current === undefined) return current; - if (typeof current === "bigint") return current.toString(); - if (typeof current !== "object") return current; + const seen = new WeakSet(); - if (seen.has(current)) return undefined; - seen.add(current); - - if (Array.isArray(current)) { - return current.map(traverse).filter((x) => x !== undefined); + // 3. RECURSIVE WALKER + const traverse = ( + current: any, + nodeName: string | null, + parentName: string | null + ): any => { + // JSON.stringify cannot handle BigInt. Convert it to string. + if (typeof current === "bigint") { + return current.toString(); } - const allowedKeys = getAllowedKeys(current); - const copy: any = {}; + // A. Handle Primitives + if (typeof current !== "object" || current === null) { + return current; + } - if (allowedKeys) { - // Known Class: Filter strictly - for (const key of allowedKeys) { - if (key in current) { - const val = traverse(current[key]); - if (val !== undefined) copy[key] = val; + // B. Handle Circular References + if (seen.has(current)) { + return undefined; + } + seen.add(current); + + // C. Handle Arrays + if (Array.isArray(current)) { + return current + .map((item) => traverse(item, null, nodeName)) + .filter((item) => item !== undefined); + } + + // D. Handle Objects + const copy: any = {}; + const effectiveParent = nodeName || parentName; + + for (const [childKey, childValue] of Object.entries(current)) { + // Rule 1: Global Blocklist and Private Properties (start with _) + if (globalBlocklist.has(childKey) || childKey.startsWith("_")) { + continue; + } + + // Rule 2: Scoped Blocklist + if ( + effectiveParent && + scopedBlocklist[effectiveParent]?.has(childKey) + ) { + continue; + } + + // Rule 3: Metadata parsing + if (childKey === "metadata" && typeof childValue === "string") { + try { + const parsed = JSON.parse(childValue); + copy[childKey] = { + role: parsed.role, + clientData: parsed.clientData, + }; + continue; + } catch { + /* ignore */ } } - } else { - // Plain Object: Copy recursive - for (const [key, value] of Object.entries(current)) { - const val = traverse(value); - if (val !== undefined) copy[key] = val; + + const cleanedValue = traverse(childValue, childKey, nodeName); + if (cleanedValue !== undefined) { + copy[childKey] = cleanedValue; } } return copy; }; - return JSON.stringify(traverse(root)); + const cleanedObject = traverse(event, "root", null); + return JSON.stringify(cleanedObject); } }