Skip to content

Commit 7e074bc

Browse files
committed
[STREAM-926] - Updated screen-recording metadata request to retry failed requests with exponential backoff.
1 parent ef30dbd commit 7e074bc

3 files changed

Lines changed: 159 additions & 22 deletions

File tree

changelog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
66
# [Unreleased](https://github.com/MyPureCloud/genesys-cloud-webrtc-sdk/compare/v11.3.0...HEAD)
77

88
# Changed
9+
* [STREAM-926](https://inindca.atlassian.net/browse/STREAM-926) - Updated screen recording metadata request to use `requestApiWithRetry` to retry failed requests with exponential backoff.
910
* [STREAM-912](https://inindca.atlassian.net/browse/STREAM-912) - Bump streaming-client to `19.4.0` to remove SDP answer payload from logging.
1011
* [NO-JIRA] - Bump axios to `1.12.2` to address Snyk vulnerability.
1112

src/sessions/screen-recording-session-handler.ts

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {
99
} from '../types/interfaces';
1010
import BaseSessionHandler from './base-session-handler';
1111
import { SessionTypes, SdkErrorTypes } from '../types/enums';
12-
import { createAndEmitSdkError, getBareJid, isPeerConnectionDisconnected, isScreenRecordingJid, requestApi } from '../utils';
12+
import { createAndEmitSdkError, getBareJid, isPeerConnectionDisconnected, isScreenRecordingJid, requestApiWithRetry } from '../utils';
1313
import { filter, fromEvent, take, takeWhile } from 'rxjs';
1414

1515
export class ScreenRecordingSessionHandler extends BaseSessionHandler {
@@ -95,7 +95,7 @@ export class ScreenRecordingSessionHandler extends BaseSessionHandler {
9595
}
9696

9797
private updateScreenRecordingMetadatas (session: ScreenRecordingMediaSession, metadatas: ScreenRecordingMetadata[]) {
98-
this.log('debug', 'sending screen metadatas', { conversationId: session.conversationId, sessionId: session.id, metadatas })
98+
this.log('info', 'sending screen metadatas', { conversationId: session.conversationId, sessionId: session.id, metadatas })
9999
metadatas.forEach((meta) => {
100100
// adding any here because I don't want to add this to the public interface
101101
(meta as any)._trackId = meta.trackId;
@@ -124,11 +124,28 @@ export class ScreenRecordingSessionHandler extends BaseSessionHandler {
124124
url += '/backgroundassistant';
125125
}
126126

127-
return requestApi.call(this.sdk, url, {
128-
method: 'post',
129-
authToken: accessToken || jwt,
130-
data
131-
});
127+
return requestApiWithRetry
128+
.call(this.sdk, url, {
129+
method: 'post',
130+
authToken: accessToken || jwt,
131+
data,
132+
})
133+
.promise.then(() => {
134+
this.log('info', 'Screen recording metadata sent.', {
135+
conversationId: session.conversationId,
136+
sessionId: session.id,
137+
metadatas,
138+
});
139+
})
140+
.catch((e) => {
141+
this.log('error', 'Failed to send screen recording metadata.', {
142+
error: e,
143+
conversationId: session.conversationId,
144+
sessionId: session.id,
145+
metadatas,
146+
});
147+
throw e;
148+
});
132149
}
133150
}
134151

test/unit/sessions/screen-recording-session-handler.test.ts

Lines changed: 134 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ describe('sendMetadataWhenSessionConnects', () => {
105105
pc.connectionState = 'disconnected';
106106
pc.dispatchEvent(new Event('connectionstatechange'));
107107
expect(spy).not.toHaveBeenCalled();
108-
108+
109109
// since the above state was disconnected, the observable should have ended and should no longer process changes
110110
pc.connectionState = 'connected';
111111
pc.dispatchEvent(new Event('connectionstatechange'));
@@ -169,7 +169,7 @@ describe('acceptSession', () => {
169169

170170
expect(metadatasSpy).not.toHaveBeenCalled();
171171
});
172-
172+
173173
it('should throw if no metadatas are empty', async () => {
174174
const metadatasSpy = jest.spyOn(utils, 'requestApi').mockResolvedValue(null);
175175
const session = {
@@ -242,7 +242,16 @@ describe('updateOutgoingMedia', () => {
242242
});
243243

244244
describe('updateScreenRecordingMetadatas', () => {
245-
it('should replace trackIds with mid', async () => {
245+
let mockRetryRequest: any;
246+
247+
beforeEach(() => {
248+
mockRetryRequest = {
249+
promise: Promise.resolve()
250+
};
251+
jest.spyOn(utils, 'requestApiWithRetry').mockReturnValue(mockRetryRequest);
252+
});
253+
254+
it('should replace trackIds with mid and use retry logic', async () => {
246255
const metas = [
247256
{
248257
trackId: 'trackId123'
@@ -276,10 +285,12 @@ describe('updateScreenRecordingMetadatas', () => {
276285
const session = {
277286
pc: {
278287
getTransceivers: jest.fn().mockReturnValue(transceivers)
279-
}
288+
},
289+
conversationId: 'conv123',
290+
id: 'session456'
280291
};
281292

282-
jest.spyOn(utils, 'requestApi').mockResolvedValue(null);
293+
const logSpy = jest.spyOn(handler as any, 'log');
283294

284295
await handler['updateScreenRecordingMetadatas'](session as any, metas as any);
285296

@@ -288,9 +299,16 @@ describe('updateScreenRecordingMetadatas', () => {
288299

289300
expect(metas[1].trackId).toBe('7');
290301
expect((metas[1] as any)._trackId).toBe('trackId456');
302+
303+
expect(utils.requestApiWithRetry).toHaveBeenCalled();
304+
expect(logSpy).toHaveBeenCalledWith('info', 'Screen recording metadata sent.', expect.objectContaining({
305+
conversationId: 'conv123',
306+
sessionId: 'session456',
307+
metadatas: metas
308+
}));
291309
});
292310

293-
it('should use the backgroundassistant url', async () => {
311+
it('should use the backgroundassistant url with retry logic', async () => {
294312
const metas = [
295313
{ trackId: 'trackId123' }
296314
];
@@ -307,23 +325,25 @@ describe('updateScreenRecordingMetadatas', () => {
307325
const session = {
308326
pc: {
309327
getTransceivers: jest.fn().mockReturnValue(transceivers)
310-
}
328+
},
329+
conversationId: 'conv123',
330+
id: 'session456'
311331
};
312332

313-
jest.spyOn(utils, 'requestApi').mockResolvedValue(null);
314-
315333
Object.defineProperty(mockSdk, 'isJwtAuth', { get: () => true });
316334
mockSdk._config.jwt = 'myjwt';
317335
mockSdk._config.accessToken = null;
318336

319337
await handler['updateScreenRecordingMetadatas'](session as any, metas as any);
320338

321-
expect(utils.requestApi).toHaveBeenCalledWith(
339+
expect(utils.requestApiWithRetry).toHaveBeenCalledWith(
322340
expect.stringContaining('backgroundassistant'),
323341
expect.objectContaining({
324-
authToken: 'myjwt'
325-
}
326-
));
342+
method: 'post',
343+
authToken: 'myjwt',
344+
data: expect.any(String)
345+
})
346+
);
327347
expect(metas[0].trackId).toBe('3');
328348
expect((metas[0] as any)._trackId).toBe('trackId123');
329349
});
@@ -340,14 +360,113 @@ describe('updateScreenRecordingMetadatas', () => {
340360
const session = {
341361
pc: {
342362
getTransceivers: jest.fn().mockReturnValue([])
343-
}
363+
},
364+
conversationId: 'conv123',
365+
id: 'session456'
344366
};
345367

346-
jest.spyOn(utils, 'requestApi').mockResolvedValue(null);
347368
const logSpy = jest.spyOn(handler as any, 'log');
348369

349370
await handler['updateScreenRecordingMetadatas'](session as any, metas as any);
350371

351372
expect(logSpy).toHaveBeenCalledWith('warn', expect.stringContaining('Failed to find transceiver'), expect.anything());
352373
});
374+
375+
it('should log error and re-throw when retry request fails', async () => {
376+
const error = new Error('Network failure after retries');
377+
mockRetryRequest.promise = Promise.reject(error);
378+
379+
const metas = [{ trackId: 'trackId123' }];
380+
const transceivers = [{
381+
mid: '3',
382+
sender: { track: { id: metas[0].trackId } }
383+
}];
384+
385+
const session = {
386+
pc: {
387+
getTransceivers: jest.fn().mockReturnValue(transceivers)
388+
},
389+
conversationId: 'conv123',
390+
id: 'session456'
391+
};
392+
393+
const logSpy = jest.spyOn(handler as any, 'log');
394+
395+
await expect(handler['updateScreenRecordingMetadatas'](session as any, metas as any))
396+
.rejects.toThrow('Network failure after retries');
397+
398+
expect(logSpy).toHaveBeenCalledWith('error', 'Failed to send screen recording metadata.', expect.objectContaining({
399+
error,
400+
conversationId: 'conv123',
401+
sessionId: 'session456',
402+
metadatas: metas
403+
}));
404+
expect(utils.requestApiWithRetry).toHaveBeenCalled();
405+
});
406+
407+
it('should use accessToken when not jwt auth', async () => {
408+
const metas = [{ trackId: 'trackId123' }];
409+
const transceivers = [{
410+
mid: '3',
411+
sender: { track: { id: metas[0].trackId } }
412+
}];
413+
414+
const session = {
415+
pc: {
416+
getTransceivers: jest.fn().mockReturnValue(transceivers)
417+
},
418+
conversationId: 'conv123',
419+
id: 'session456'
420+
};
421+
422+
Object.defineProperty(mockSdk, 'isJwtAuth', { get: () => false });
423+
mockSdk._config.accessToken = 'access123';
424+
mockSdk._config.jwt = null;
425+
426+
await handler['updateScreenRecordingMetadatas'](session as any, metas as any);
427+
428+
expect(utils.requestApiWithRetry).toHaveBeenCalledWith(
429+
'/recordings/screensessions/metadata',
430+
expect.objectContaining({
431+
method: 'post',
432+
authToken: 'access123',
433+
data: expect.any(String)
434+
})
435+
);
436+
});
437+
438+
it('should include correct payload in the request', async () => {
439+
const metas = [{ trackId: 'trackId123', someProperty: 'value' }];
440+
const transceivers = [{
441+
mid: '3',
442+
sender: { track: { id: metas[0].trackId } }
443+
}];
444+
445+
const session = {
446+
pc: {
447+
getTransceivers: jest.fn().mockReturnValue(transceivers)
448+
},
449+
conversationId: 'conv123',
450+
id: 'session456',
451+
originalRoomJid: 'room@conference.example.com'
452+
};
453+
454+
jest.spyOn(utils, 'getBareJid').mockReturnValue('user@example.com');
455+
456+
await handler['updateScreenRecordingMetadatas'](session as any, metas as any);
457+
458+
const requestCall = (utils.requestApiWithRetry as jest.Mock).mock.calls[0];
459+
const requestData = JSON.parse(requestCall[1].data);
460+
461+
expect(requestData).toEqual({
462+
participantJid: 'user@example.com',
463+
metaData: [expect.objectContaining({
464+
trackId: '3',
465+
someProperty: 'value',
466+
_trackId: 'trackId123'
467+
})],
468+
roomId: 'room@conference.example.com',
469+
conversationId: 'conv123'
470+
});
471+
});
353472
});

0 commit comments

Comments
 (0)