genesys-cloud-streaming-client 16.1.3 → 16.1.4-develop.69

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,7 +4,7 @@ import { IQ } from 'stanza/protocol';
4
4
  import { SessionManager } from 'stanza/jingle';
5
5
  import { SessionOpts } from 'stanza/jingle/Session';
6
6
  import { Client } from './client';
7
- import { ExtendedRTCIceServer, IClientOptions, SessionTypes, IPendingSession, StreamingClientExtension } from './types/interfaces';
7
+ import { ExtendedRTCIceServer, IClientOptions, SessionTypes, IPendingSession, StreamingClientExtension, NRProxyStat, InsightAction } from './types/interfaces';
8
8
  import { NamedAgent } from './types/named-agent';
9
9
  import { StanzaMediaSession } from './types/stanza-media-session';
10
10
  import { IMediaSession } from './types/media-session';
@@ -54,6 +54,9 @@ export declare class WebrtcExtension extends EventEmitter implements StreamingCl
54
54
  handleGenesysWebrtcStanza(iq: IQ): Promise<boolean | void>;
55
55
  prepareSession(options: SessionOpts): StanzaMediaSession | undefined;
56
56
  proxyStatsForSession(session: IMediaSession): void;
57
+ addStatToQueue<T extends {
58
+ _eventType: string;
59
+ }>(stat: InsightAction<T>): void;
57
60
  getLogDetailsForPendingSessionId(sessionId: string): {
58
61
  conversationId?: string;
59
62
  sessionId: string;
@@ -89,6 +92,7 @@ export declare class WebrtcExtension extends EventEmitter implements StreamingCl
89
92
  getSessionTypeByJid(jid: string): SessionTypes;
90
93
  getSessionManager(): SessionManager | undefined;
91
94
  getAllSessions(): IMediaSession[];
95
+ proxyNRStat(stat: NRProxyStat): void;
92
96
  get expose(): WebrtcExtensionAPI;
93
97
  }
94
98
  export interface WebrtcExtensionAPI {
@@ -108,4 +112,5 @@ export interface WebrtcExtensionAPI {
108
112
  getSessionTypeByJid(jid: string): SessionTypes;
109
113
  getSessionManager: () => SessionManager | undefined;
110
114
  getAllSessions: () => IMediaSession[];
115
+ proxyNRStat: (stat: NRProxyStat) => void;
111
116
  }
package/dist/es/webrtc.js CHANGED
@@ -9,7 +9,7 @@ import { isFirefox } from 'browserama';
9
9
  import { definitions } from './stanza-definitions/webrtc-signaling';
10
10
  import { isAcdJid, isScreenRecordingJid, isSoftphoneJid, isVideoJid, calculatePayloadSize, retryPromise } from './utils';
11
11
  import { Client } from './client';
12
- import { formatStatsEvent } from './stats-formatter';
12
+ import { deepFlatten, formatStatsEvent } from './stats-formatter';
13
13
  import { SessionTypes } from './types/interfaces';
14
14
  import { StanzaMediaSession } from './types/stanza-media-session';
15
15
  import { GenesysCloudMediaSession } from './types/genesys-cloud-media-session';
@@ -160,6 +160,7 @@ export class WebrtcExtension extends EventEmitter {
160
160
  }
161
161
  const session = new GenesysCloudMediaSession(this, mediaSessionParams);
162
162
  yield session.setRemoteDescription(params.sdp);
163
+ this.proxyStatsForSession(session);
163
164
  session.on('sendIq', (iq) => { var _a; return (_a = this.stanzaInstance) === null || _a === void 0 ? void 0 : _a.sendIQ(iq); });
164
165
  session.on('terminated', () => {
165
166
  this.webrtcSessions = this.webrtcSessions.filter(s => s.id !== session.id);
@@ -271,25 +272,48 @@ export class WebrtcExtension extends EventEmitter {
271
272
  }
272
273
  const statsCopy = JSON.parse(JSON.stringify(statsEvent));
273
274
  const extraDetails = {
274
- conference: session.conversationId,
275
- session: session.id,
275
+ conversationId: session.conversationId,
276
+ sessionId: session.id,
276
277
  sessionType: session.sessionType
277
278
  };
278
279
  // format the event to what the api expects
279
280
  const event = formatStatsEvent(statsCopy, extraDetails);
280
- const currentEventSize = calculatePayloadSize(event);
281
- // Check if the size of the current event plus the size of the previous stats exceeds max size.
282
- const exceedsMaxStatSize = this.statBuffer + currentEventSize > this.currentMaxStatSize;
283
- this.statsArr.push(event);
284
- this.statBuffer += currentEventSize;
285
- // If it exceeds max size, don't append just send current payload.
286
- if (exceedsMaxStatSize) {
287
- this.flushStats();
288
- }
289
- else {
290
- this.throttledSendStats();
281
+ this.addStatToQueue(event);
282
+ });
283
+ }
284
+ // this fn checks to see if the new stat fits inside the buffer. If not, send the queue;
285
+ addStatToQueue(stat) {
286
+ if (this.config.optOutOfWebrtcStatsTelemetry) {
287
+ return;
288
+ }
289
+ if (!stat.details._appId) {
290
+ stat.details._appId = this.logger.clientId;
291
+ stat.details._appName = 'streamingclient';
292
+ stat.details._appVersion = Client.version;
293
+ }
294
+ stat.details._originAppId = this.client.config.appId;
295
+ // nr only accepts single level objects so we must flatten everything just in case
296
+ const flattenedDetails = deepFlatten(stat.details);
297
+ // new relic doesn't accept booleans so we convert them to strings
298
+ Object.keys(flattenedDetails).forEach((key) => {
299
+ const val = flattenedDetails[key];
300
+ if (typeof val === 'boolean') {
301
+ flattenedDetails[key] = `${val}`;
291
302
  }
292
303
  });
304
+ const formattedStat = Object.assign(Object.assign({}, stat), { details: flattenedDetails });
305
+ const currentEventSize = calculatePayloadSize(formattedStat);
306
+ // Check if the size of the current event plus the size of the previous stats exceeds max size.
307
+ const exceedsMaxStatSize = this.statBuffer + currentEventSize > this.currentMaxStatSize;
308
+ this.statsArr.push(formattedStat);
309
+ this.statBuffer += currentEventSize;
310
+ // If it exceeds max size, don't append just send current payload.
311
+ if (exceedsMaxStatSize) {
312
+ this.flushStats();
313
+ }
314
+ else {
315
+ this.throttledSendStats();
316
+ }
293
317
  }
294
318
  getLogDetailsForPendingSessionId(sessionId) {
295
319
  const logDetails = {
@@ -400,6 +424,19 @@ export class WebrtcExtension extends EventEmitter {
400
424
  const loggingParams = { sessionId: sessionId, conversationId: msg.propose.conversationId, sessionType, isDuplicatePropose };
401
425
  this.logger.info('propose received', loggingParams);
402
426
  if (!isDuplicatePropose) {
427
+ const { appId } = this.client.config;
428
+ const proposeStat = {
429
+ actionName: 'WebrtcStats',
430
+ details: {
431
+ _eventTimestamp: new Date().toISOString(),
432
+ _eventType: 'firstPropose',
433
+ conversationId: loggingParams.conversationId,
434
+ sdpViaXmppRequested: !!msg.propose.sdpOverXmpp,
435
+ sessionId: sessionId,
436
+ sessionType: sessionType,
437
+ }
438
+ };
439
+ this.addStatToQueue(proposeStat);
403
440
  // TODO: is ofrom used?
404
441
  // const roomJid = (msg.ofrom && msg.ofrom.full) || msg.from.full || msg.from;
405
442
  const fromJid = msg.from;
@@ -731,6 +768,9 @@ export class WebrtcExtension extends EventEmitter {
731
768
  const stanzaSessions = stanzaSessionsObj && Object.values(stanzaSessionsObj) || [];
732
769
  return [...stanzaSessions, ...this.webrtcSessions];
733
770
  }
771
+ proxyNRStat(stat) {
772
+ this.addStatToQueue(stat);
773
+ }
734
774
  get expose() {
735
775
  return {
736
776
  on: this.on.bind(this),
@@ -748,7 +788,8 @@ export class WebrtcExtension extends EventEmitter {
748
788
  initiateRtcSession: this.initiateRtcSession.bind(this),
749
789
  getSessionTypeByJid: this.getSessionTypeByJid.bind(this),
750
790
  getSessionManager: this.getSessionManager.bind(this),
751
- getAllSessions: this.getAllSessions.bind(this)
791
+ getAllSessions: this.getAllSessions.bind(this),
792
+ proxyNRStat: this.proxyNRStat.bind(this)
752
793
  };
753
794
  }
754
795
  }
@@ -5,6 +5,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5
5
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
7
  # [Unreleased](https://github.com/purecloudlabs/genesys-cloud-streaming-client/compare/v16.1.2...HEAD)
8
+ ### Added
9
+ * [PCM-2276](https://inindca.atlassian.net/browse/PCM-2276) Added an function to proxy stats to nr from other apps like the webrtc sdk; Fixed some issues with nr stats gathering.
8
10
 
9
11
  # [v16.1.2](https://github.com/purecloudlabs/genesys-cloud-streaming-client/compare/v16.1.1...v16.1.2)
10
12
  ### Fixed
@@ -48,7 +48,8 @@ class Client extends events_1.default {
48
48
  jidResource: options.jidResource,
49
49
  channelId: null,
50
50
  appName: options.appName,
51
- appVersion: options.appVersion
51
+ appVersion: options.appVersion,
52
+ appId: options.appId
52
53
  };
53
54
  this.backgroundAssistantMode = this.checkIsBackgroundAssistant();
54
55
  this.isGuest = !this.backgroundAssistantMode && !options.authToken;
@@ -433,7 +434,7 @@ class Client extends events_1.default {
433
434
  return Client.version;
434
435
  }
435
436
  static get version() {
436
- return '16.1.3';
437
+ return '16.1.4';
437
438
  }
438
439
  }
439
440
  exports.Client = Client;
@@ -1,7 +1,6 @@
1
1
  import { StatsEvent } from 'webrtc-stats-gatherer';
2
- export declare function formatStatsEvent(event: StatsEvent, extraDetails?: any): {
3
- actionName: string;
4
- actionDate: number;
5
- details: any;
6
- };
2
+ import { FlatObject, InsightAction } from './types/interfaces';
3
+ export declare function formatStatsEvent(event: StatsEvent, extraDetails?: FlatObject): InsightAction<{
4
+ _eventType: string;
5
+ } & FlatObject>;
7
6
  export declare function deepFlatten(obj: any, prefix?: string): any;
@@ -1,43 +1,33 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.deepFlatten = exports.formatStatsEvent = void 0;
4
+ function isGetStatsEvent(event) {
5
+ return event.name === 'getStats';
6
+ }
7
+ function prepGetStatsEvent(event) {
8
+ let details = {};
9
+ Object.assign(details, deepFlatten(event.tracks, 'localTrack'));
10
+ delete event.tracks;
11
+ Object.assign(details, deepFlatten(event.remoteTracks, `remoteTrack`));
12
+ delete event.remoteTracks;
13
+ return details;
14
+ }
4
15
  function formatStatsEvent(event, extraDetails = {}) {
5
- let details;
6
- const eventType = event.name;
7
- if (event.name === 'connect') {
8
- const e = event;
9
- details = e;
10
- Object.assign(details, deepFlatten(e.candidatePairDetails, 'candidatePairDetails'));
11
- delete details.candidatePairDetails;
12
- }
13
- else if (event.name === 'getStats') {
14
- const e = event;
15
- details = e;
16
- Object.assign(details, deepFlatten(e.tracks, 'localTrack'));
17
- delete details.tracks;
18
- Object.assign(details, deepFlatten(e.remoteTracks, `remoteTrack`));
19
- delete details.remoteTracks;
20
- }
21
- else {
22
- details = {};
23
- if (event.name !== 'failure') {
24
- // TODO: log this out when we get genesys-cloud-client-logger in place (allows logging from anywhere)
25
- }
26
- Object.assign(details, deepFlatten(event));
16
+ const details = {
17
+ _eventType: event.name,
18
+ _eventTimestamp: new Date().toISOString(),
19
+ ...extraDetails
20
+ };
21
+ // anything that needs to be renamed or massaged
22
+ if (isGetStatsEvent(event)) {
23
+ Object.assign(details, prepGetStatsEvent(event));
27
24
  }
25
+ // general case
26
+ Object.assign(details, deepFlatten(event));
28
27
  delete details.name;
29
- Object.assign(details, extraDetails, { '_eventType': eventType });
30
- // new relic doesn't accept booleans so we convert them to strings
31
- Object.keys(details).forEach((key) => {
32
- const val = details[key];
33
- if (typeof val === 'boolean') {
34
- details[key] = `${val}`;
35
- }
36
- });
37
28
  const formattedEvent = {
38
29
  actionName: 'WebrtcStats',
39
- actionDate: Date.now(),
40
- details
30
+ details,
41
31
  };
42
32
  return formattedEvent;
43
33
  }
@@ -186,3 +186,60 @@ export declare type HeadsetControlsRequest = TypedJsonRpcMessage<'headsetControl
186
186
  export declare type HeadsetControlsRejection = TypedJsonRpcMessage<'headsetControlsRejection', HeadsetControlsRejectionParams>;
187
187
  export declare type HeadsetControlsChanged = TypedJsonRpcMessage<'headsetControlsChanged', HeadsetControlsChangedParams>;
188
188
  export declare type GenesysMediaMessage = HeadsetControlsRequest | HeadsetControlsRejection | HeadsetControlsChanged;
189
+ export declare type FlatObject = {
190
+ [key: string]: string | number | boolean | null | Date;
191
+ };
192
+ export declare type GenericAction = {
193
+ _eventType: string;
194
+ };
195
+ export declare type InsightReport = {
196
+ appName: string;
197
+ appVersion: string;
198
+ originAppName?: string;
199
+ originAppVersion?: string;
200
+ actions: InsightAction<any>[];
201
+ };
202
+ export declare type InsightAction<T extends {
203
+ _eventType: string;
204
+ }> = {
205
+ actionName: 'WebrtcStats';
206
+ details: InsightActionDetails<T>;
207
+ };
208
+ export declare type InsightActionDetails<K extends {
209
+ _eventType: string;
210
+ }> = {
211
+ _eventType: K['_eventType'];
212
+ /**
213
+ * This should be an ISO string
214
+ */
215
+ _eventTimestamp: string;
216
+ _appId?: string;
217
+ _appName?: string;
218
+ _appVersion?: string;
219
+ } & K;
220
+ export declare type FirstProposeStat = InsightAction<{
221
+ _eventType: 'firstPropose';
222
+ sdpViaXmppRequested: boolean;
223
+ sessionType: SessionTypesAsStrings;
224
+ originAppId?: string;
225
+ conversationId: string;
226
+ sessionId: string;
227
+ }>;
228
+ export declare type FirstAlertingConversationStat = InsightAction<{
229
+ _eventType: 'firstAlertingConversationUpdate';
230
+ conversationId: string;
231
+ participantId: string;
232
+ }>;
233
+ export declare type MediaStat = InsightAction<{
234
+ _eventType: 'mediaRequested' | 'mediaStarted' | 'mediaError';
235
+ requestId?: string;
236
+ message?: string;
237
+ audioRequested: boolean;
238
+ videoRequested: boolean;
239
+ displayRequested: boolean;
240
+ conversationId?: string;
241
+ sessionType?: SessionTypesAsStrings;
242
+ sessionId?: string;
243
+ elapsedMsFromInitialRequest?: number;
244
+ }>;
245
+ export declare type NRProxyStat = FirstAlertingConversationStat | MediaStat;
@@ -4,7 +4,7 @@ import { IQ } from 'stanza/protocol';
4
4
  import { SessionManager } from 'stanza/jingle';
5
5
  import { SessionOpts } from 'stanza/jingle/Session';
6
6
  import { Client } from './client';
7
- import { ExtendedRTCIceServer, IClientOptions, SessionTypes, IPendingSession, StreamingClientExtension } from './types/interfaces';
7
+ import { ExtendedRTCIceServer, IClientOptions, SessionTypes, IPendingSession, StreamingClientExtension, NRProxyStat, InsightAction } from './types/interfaces';
8
8
  import { NamedAgent } from './types/named-agent';
9
9
  import { StanzaMediaSession } from './types/stanza-media-session';
10
10
  import { IMediaSession } from './types/media-session';
@@ -54,6 +54,9 @@ export declare class WebrtcExtension extends EventEmitter implements StreamingCl
54
54
  handleGenesysWebrtcStanza(iq: IQ): Promise<boolean | void>;
55
55
  prepareSession(options: SessionOpts): StanzaMediaSession | undefined;
56
56
  proxyStatsForSession(session: IMediaSession): void;
57
+ addStatToQueue<T extends {
58
+ _eventType: string;
59
+ }>(stat: InsightAction<T>): void;
57
60
  getLogDetailsForPendingSessionId(sessionId: string): {
58
61
  conversationId?: string;
59
62
  sessionId: string;
@@ -89,6 +92,7 @@ export declare class WebrtcExtension extends EventEmitter implements StreamingCl
89
92
  getSessionTypeByJid(jid: string): SessionTypes;
90
93
  getSessionManager(): SessionManager | undefined;
91
94
  getAllSessions(): IMediaSession[];
95
+ proxyNRStat(stat: NRProxyStat): void;
92
96
  get expose(): WebrtcExtensionAPI;
93
97
  }
94
98
  export interface WebrtcExtensionAPI {
@@ -108,4 +112,5 @@ export interface WebrtcExtensionAPI {
108
112
  getSessionTypeByJid(jid: string): SessionTypes;
109
113
  getSessionManager: () => SessionManager | undefined;
110
114
  getAllSessions: () => IMediaSession[];
115
+ proxyNRStat: (stat: NRProxyStat) => void;
111
116
  }
@@ -162,6 +162,7 @@ class WebrtcExtension extends events_1.EventEmitter {
162
162
  }
163
163
  const session = new genesys_cloud_media_session_1.GenesysCloudMediaSession(this, mediaSessionParams);
164
164
  await session.setRemoteDescription(params.sdp);
165
+ this.proxyStatsForSession(session);
165
166
  session.on('sendIq', (iq) => { var _a; return (_a = this.stanzaInstance) === null || _a === void 0 ? void 0 : _a.sendIQ(iq); });
166
167
  session.on('terminated', () => {
167
168
  this.webrtcSessions = this.webrtcSessions.filter(s => s.id !== session.id);
@@ -262,25 +263,51 @@ class WebrtcExtension extends events_1.EventEmitter {
262
263
  }
263
264
  const statsCopy = JSON.parse(JSON.stringify(statsEvent));
264
265
  const extraDetails = {
265
- conference: session.conversationId,
266
- session: session.id,
266
+ conversationId: session.conversationId,
267
+ sessionId: session.id,
267
268
  sessionType: session.sessionType
268
269
  };
269
270
  // format the event to what the api expects
270
271
  const event = stats_formatter_1.formatStatsEvent(statsCopy, extraDetails);
271
- const currentEventSize = utils_1.calculatePayloadSize(event);
272
- // Check if the size of the current event plus the size of the previous stats exceeds max size.
273
- const exceedsMaxStatSize = this.statBuffer + currentEventSize > this.currentMaxStatSize;
274
- this.statsArr.push(event);
275
- this.statBuffer += currentEventSize;
276
- // If it exceeds max size, don't append just send current payload.
277
- if (exceedsMaxStatSize) {
278
- this.flushStats();
279
- }
280
- else {
281
- this.throttledSendStats();
272
+ this.addStatToQueue(event);
273
+ });
274
+ }
275
+ // this fn checks to see if the new stat fits inside the buffer. If not, send the queue;
276
+ addStatToQueue(stat) {
277
+ if (this.config.optOutOfWebrtcStatsTelemetry) {
278
+ return;
279
+ }
280
+ if (!stat.details._appId) {
281
+ stat.details._appId = this.logger.clientId;
282
+ stat.details._appName = 'streamingclient';
283
+ stat.details._appVersion = client_1.Client.version;
284
+ }
285
+ stat.details._originAppId = this.client.config.appId;
286
+ // nr only accepts single level objects so we must flatten everything just in case
287
+ const flattenedDetails = stats_formatter_1.deepFlatten(stat.details);
288
+ // new relic doesn't accept booleans so we convert them to strings
289
+ Object.keys(flattenedDetails).forEach((key) => {
290
+ const val = flattenedDetails[key];
291
+ if (typeof val === 'boolean') {
292
+ flattenedDetails[key] = `${val}`;
282
293
  }
283
294
  });
295
+ const formattedStat = {
296
+ ...stat,
297
+ details: flattenedDetails
298
+ };
299
+ const currentEventSize = utils_1.calculatePayloadSize(formattedStat);
300
+ // Check if the size of the current event plus the size of the previous stats exceeds max size.
301
+ const exceedsMaxStatSize = this.statBuffer + currentEventSize > this.currentMaxStatSize;
302
+ this.statsArr.push(formattedStat);
303
+ this.statBuffer += currentEventSize;
304
+ // If it exceeds max size, don't append just send current payload.
305
+ if (exceedsMaxStatSize) {
306
+ this.flushStats();
307
+ }
308
+ else {
309
+ this.throttledSendStats();
310
+ }
284
311
  }
285
312
  getLogDetailsForPendingSessionId(sessionId) {
286
313
  const logDetails = {
@@ -388,6 +415,19 @@ class WebrtcExtension extends events_1.EventEmitter {
388
415
  const loggingParams = { sessionId: sessionId, conversationId: msg.propose.conversationId, sessionType, isDuplicatePropose };
389
416
  this.logger.info('propose received', loggingParams);
390
417
  if (!isDuplicatePropose) {
418
+ const { appId } = this.client.config;
419
+ const proposeStat = {
420
+ actionName: 'WebrtcStats',
421
+ details: {
422
+ _eventTimestamp: new Date().toISOString(),
423
+ _eventType: 'firstPropose',
424
+ conversationId: loggingParams.conversationId,
425
+ sdpViaXmppRequested: !!msg.propose.sdpOverXmpp,
426
+ sessionId: sessionId,
427
+ sessionType: sessionType,
428
+ }
429
+ };
430
+ this.addStatToQueue(proposeStat);
391
431
  // TODO: is ofrom used?
392
432
  // const roomJid = (msg.ofrom && msg.ofrom.full) || msg.from.full || msg.from;
393
433
  const fromJid = msg.from;
@@ -706,6 +746,9 @@ class WebrtcExtension extends events_1.EventEmitter {
706
746
  const stanzaSessions = stanzaSessionsObj && Object.values(stanzaSessionsObj) || [];
707
747
  return [...stanzaSessions, ...this.webrtcSessions];
708
748
  }
749
+ proxyNRStat(stat) {
750
+ this.addStatToQueue(stat);
751
+ }
709
752
  get expose() {
710
753
  return {
711
754
  on: this.on.bind(this),
@@ -723,7 +766,8 @@ class WebrtcExtension extends events_1.EventEmitter {
723
766
  initiateRtcSession: this.initiateRtcSession.bind(this),
724
767
  getSessionTypeByJid: this.getSessionTypeByJid.bind(this),
725
768
  getSessionManager: this.getSessionManager.bind(this),
726
- getAllSessions: this.getAllSessions.bind(this)
769
+ getAllSessions: this.getAllSessions.bind(this),
770
+ proxyNRStat: this.proxyNRStat.bind(this)
727
771
  };
728
772
  }
729
773
  }