genesys-cloud-streaming-client 19.3.1 → 19.4.0-release.2
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.
- package/README.md +3 -0
- package/dist/cjs/client.js +1 -1
- package/dist/cjs/index.d.ts +1 -1
- package/dist/cjs/index.js +2 -1
- package/dist/cjs/notifications.d.ts +26 -7
- package/dist/cjs/notifications.js +60 -9
- package/dist/cjs/types/genesys-cloud-media-session.js +2 -1
- package/dist/cjs/types/interfaces.d.ts +2 -0
- package/dist/cjs/utils.d.ts +6 -0
- package/dist/cjs/utils.js +10 -1
- package/dist/deploy-info.json +3 -3
- package/dist/es/client.js +1 -1
- package/dist/es/index.bundle.js +87 -28
- package/dist/es/index.d.ts +1 -1
- package/dist/es/index.js +1 -1
- package/dist/es/notifications.d.ts +26 -7
- package/dist/es/notifications.js +76 -24
- package/dist/es/types/genesys-cloud-media-session.js +2 -1
- package/dist/es/types/interfaces.d.ts +2 -0
- package/dist/es/utils.d.ts +6 -0
- package/dist/es/utils.js +8 -0
- package/dist/manifest.json +3 -3
- package/dist/npm/CHANGELOG.md +9 -1
- package/dist/npm/client.js +1 -1
- package/dist/npm/index.d.ts +1 -1
- package/dist/npm/index.js +2 -1
- package/dist/npm/module.js +1 -1
- package/dist/npm/notifications.d.ts +26 -7
- package/dist/npm/notifications.js +60 -9
- package/dist/npm/types/genesys-cloud-media-session.js +2 -1
- package/dist/npm/types/interfaces.d.ts +2 -0
- package/dist/npm/utils.d.ts +6 -0
- package/dist/npm/utils.js +10 -1
- package/dist/streaming-client.browser.js +6 -6
- package/dist/v19/streaming-client.browser.js +6 -6
- package/dist/v19.4.0/streaming-client.browser.js +19 -0
- package/package.json +3 -1
- package/dist/v19.3.1/streaming-client.browser.js +0 -19
package/dist/es/notifications.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { __awaiter } from "tslib";
|
|
2
|
-
|
|
2
|
+
import debounce from 'debounce-promise';
|
|
3
3
|
import { splitIntoIndividualTopics } from './utils';
|
|
4
|
+
import { StreamingSubscriptionError } from './';
|
|
4
5
|
const PUBSUB_HOST_DEFAULT = 'notifications.mypurecloud.com';
|
|
5
6
|
const MAX_SUBSCRIBABLE_TOPICS = 1000;
|
|
6
7
|
const DROPPED_TOPICS_DISPLAY_COUNT = 20;
|
|
@@ -9,11 +10,13 @@ function mergeAndDedup(arr1, arr2) {
|
|
|
9
10
|
return [...arr1, ...arr2].filter((t, i, arr) => arr.indexOf(t) === i);
|
|
10
11
|
}
|
|
11
12
|
export class Notifications {
|
|
12
|
-
constructor(client) {
|
|
13
|
+
constructor(client, options) {
|
|
14
|
+
var _a;
|
|
13
15
|
this.subscriptions = {};
|
|
14
16
|
this.bulkSubscriptions = {};
|
|
15
17
|
this.topicPriorities = {};
|
|
16
18
|
this.client = client;
|
|
19
|
+
this.enablePartialBulkResubscribe = (_a = options === null || options === void 0 ? void 0 : options.enablePartialBulkResubscribe) !== null && _a !== void 0 ? _a : false;
|
|
17
20
|
client.on('pubsub:event', this.pubsubEvent.bind(this));
|
|
18
21
|
client.on('connected', this.subscriptionsKeepAlive.bind(this));
|
|
19
22
|
this.debouncedResubscribe = debounce(this.resubscribe.bind(this), 100);
|
|
@@ -35,7 +38,7 @@ export class Notifications {
|
|
|
35
38
|
this.stanzaInstance = stanza;
|
|
36
39
|
if (needsToResub) {
|
|
37
40
|
this.client.logger.info('resubscribing due to hard reconnect');
|
|
38
|
-
this.debouncedResubscribe();
|
|
41
|
+
void this.debouncedResubscribe();
|
|
39
42
|
}
|
|
40
43
|
}
|
|
41
44
|
topicHandlers(topic) {
|
|
@@ -178,7 +181,11 @@ export class Notifications {
|
|
|
178
181
|
logger: this.client.logger
|
|
179
182
|
};
|
|
180
183
|
const channelId = this.stanzaInstance.channelId;
|
|
181
|
-
|
|
184
|
+
let path = `notifications/channels/${channelId}/subscriptions`;
|
|
185
|
+
if (this.enablePartialBulkResubscribe) {
|
|
186
|
+
path += '?ignoreErrors=true';
|
|
187
|
+
}
|
|
188
|
+
return this.client.http.requestApi(path, requestOptions);
|
|
182
189
|
}
|
|
183
190
|
createSubscription(topic, handler) {
|
|
184
191
|
const topics = splitIntoIndividualTopics(topic);
|
|
@@ -234,7 +241,7 @@ export class Notifications {
|
|
|
234
241
|
/* if we don't have bulk or individual subs, we don't need to resubscribe */
|
|
235
242
|
const noTopics = bulkSubs.length + this.getActiveIndividualTopics().length === 0;
|
|
236
243
|
if (noTopics) {
|
|
237
|
-
return Promise.resolve();
|
|
244
|
+
return Promise.resolve({});
|
|
238
245
|
}
|
|
239
246
|
/* only pass in bulk subs with the replace flag – bulkSubscribe() will handle merging our individual topics (see PCM-1846) */
|
|
240
247
|
return this.bulkSubscribe(bulkSubs, { replace: true });
|
|
@@ -290,24 +297,38 @@ export class Notifications {
|
|
|
290
297
|
});
|
|
291
298
|
}
|
|
292
299
|
subscribe(topic, handler, immediate, priority) {
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
300
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
301
|
+
if (priority) {
|
|
302
|
+
this.setTopicPriorities({ [topic]: priority });
|
|
303
|
+
}
|
|
304
|
+
let promise;
|
|
305
|
+
if (!immediate) {
|
|
306
|
+
// let this and any other subscribe/unsubscribe calls roll in, then trigger a whole resubscribe
|
|
307
|
+
promise = this.debouncedResubscribe();
|
|
308
|
+
}
|
|
309
|
+
else {
|
|
310
|
+
promise = this.xmppSubscribe(topic);
|
|
311
|
+
}
|
|
312
|
+
if (handler) {
|
|
313
|
+
this.createSubscription(topic, handler);
|
|
314
|
+
}
|
|
315
|
+
else {
|
|
316
|
+
this.bulkSubscriptions[topic] = true;
|
|
317
|
+
}
|
|
318
|
+
const result = yield promise;
|
|
319
|
+
// Assume topic subscription succeeded if promise is resolved...
|
|
320
|
+
let topicResult = { topic, state: 'Permitted' };
|
|
321
|
+
// ... but if partial bulk resubscribe is enabled, use topic's individual result from the API response.
|
|
322
|
+
if (this.enablePartialBulkResubscribe && result && typeof result === 'object' && isTopicSubscribeResult(result[topic])) {
|
|
323
|
+
topicResult = result[topic];
|
|
324
|
+
}
|
|
325
|
+
// Topic result other than state=Permitted becomes a StreamingSubscriptionError promise rejection.
|
|
326
|
+
if (topicResult.state !== 'Permitted') {
|
|
327
|
+
const message = topicResult.rejectionReason || `Failed to subscribe topic ${topic}`;
|
|
328
|
+
throw new StreamingSubscriptionError(message, topic, 'subscribe');
|
|
329
|
+
}
|
|
330
|
+
return topicResult;
|
|
331
|
+
});
|
|
311
332
|
}
|
|
312
333
|
unsubscribe(topic, handler, immediate) {
|
|
313
334
|
if (handler) {
|
|
@@ -336,13 +357,35 @@ export class Notifications {
|
|
|
336
357
|
// if it's a forcible bulk subscribe, wipe out individual subscriptions
|
|
337
358
|
this.subscriptions = {};
|
|
338
359
|
}
|
|
339
|
-
yield this.makeBulkSubscribeRequest(toSubscribe, options);
|
|
360
|
+
const response = yield this.makeBulkSubscribeRequest(toSubscribe, options);
|
|
361
|
+
let topicResponseEntities = [];
|
|
362
|
+
if (response && response.data && 'entities' in response.data && Array.isArray(response.data.entities)) {
|
|
363
|
+
topicResponseEntities = response.data.entities;
|
|
364
|
+
}
|
|
365
|
+
const topicResponsesById = {};
|
|
366
|
+
for (const topicEntity of topicResponseEntities) {
|
|
367
|
+
topicResponsesById[topicEntity.id] = topicEntity;
|
|
368
|
+
}
|
|
369
|
+
const result = {};
|
|
340
370
|
if (options.replace) {
|
|
341
371
|
this.bulkSubscriptions = {};
|
|
342
372
|
}
|
|
343
373
|
topics.forEach(topic => {
|
|
344
374
|
this.bulkSubscriptions[topic] = true;
|
|
375
|
+
if (this.enablePartialBulkResubscribe) {
|
|
376
|
+
if (topic in topicResponsesById) {
|
|
377
|
+
const { state, rejectionReason } = topicResponsesById[topic];
|
|
378
|
+
result[topic] = { topic, state, rejectionReason };
|
|
379
|
+
}
|
|
380
|
+
else {
|
|
381
|
+
result[topic] = { topic, state: 'Unknown' };
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
else {
|
|
385
|
+
result[topic] = { topic, state: 'Permitted' };
|
|
386
|
+
}
|
|
345
387
|
});
|
|
388
|
+
return result;
|
|
346
389
|
});
|
|
347
390
|
}
|
|
348
391
|
get expose() {
|
|
@@ -353,3 +396,12 @@ export class Notifications {
|
|
|
353
396
|
};
|
|
354
397
|
}
|
|
355
398
|
}
|
|
399
|
+
function isTopicSubscribeResult(value) {
|
|
400
|
+
let hasTopic = false;
|
|
401
|
+
let hasValidState = false;
|
|
402
|
+
if (value && typeof value === 'object') {
|
|
403
|
+
hasTopic = 'topic' in value && typeof value.topic === 'string';
|
|
404
|
+
hasValidState = 'state' in value && ['Permitted', 'Rejected', 'Unknown'].includes(value.state);
|
|
405
|
+
}
|
|
406
|
+
return hasTopic && hasValidState;
|
|
407
|
+
}
|
|
@@ -305,7 +305,8 @@ export class GenesysCloudMediaSession {
|
|
|
305
305
|
sdp: answer.sdp,
|
|
306
306
|
sessionId: this.id
|
|
307
307
|
};
|
|
308
|
-
|
|
308
|
+
// Do not log the SDP payload to avoid logging sensitive information.
|
|
309
|
+
this.logger.info('sending sdp answer', { sessionId: this.id, conversationId: this.conversationId });
|
|
309
310
|
return this.sendGenesysWebrtc({
|
|
310
311
|
jsonrpc: '2.0',
|
|
311
312
|
method: 'answer',
|
|
@@ -22,6 +22,8 @@ export interface IClientOptions {
|
|
|
22
22
|
appVersion?: string;
|
|
23
23
|
appId?: string;
|
|
24
24
|
customHeaders?: ICustomHeader;
|
|
25
|
+
/** Allow bulk topic resubscribe to succeed or fail per-topic rather than all or nothing */
|
|
26
|
+
enablePartialBulkResubscribe?: boolean;
|
|
25
27
|
}
|
|
26
28
|
export interface ICustomHeader {
|
|
27
29
|
[header: string]: string;
|
package/dist/es/utils.d.ts
CHANGED
|
@@ -4,6 +4,12 @@ export declare class StreamingClientError extends Error {
|
|
|
4
4
|
details?: unknown;
|
|
5
5
|
constructor(type: StreamingClientErrorTypes | null, messageOrError: string | Error, details?: unknown);
|
|
6
6
|
}
|
|
7
|
+
export declare class StreamingSubscriptionError extends Error {
|
|
8
|
+
readonly topic?: string | undefined;
|
|
9
|
+
readonly operation?: "subscribe" | "unsubscribe" | undefined;
|
|
10
|
+
name: string;
|
|
11
|
+
constructor(message: string, topic?: string | undefined, operation?: "subscribe" | "unsubscribe" | undefined);
|
|
12
|
+
}
|
|
7
13
|
export declare function timeoutPromise(fn: Function, timeoutMs: number, msg: string, details?: any): Promise<any>;
|
|
8
14
|
export declare function delay(ms: number): Promise<void>;
|
|
9
15
|
export declare function splitIntoIndividualTopics(topicString: string): string[];
|
package/dist/es/utils.js
CHANGED
|
@@ -19,6 +19,14 @@ export class StreamingClientError extends Error {
|
|
|
19
19
|
this.details = details;
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
|
+
export class StreamingSubscriptionError extends Error {
|
|
23
|
+
constructor(message, topic, operation) {
|
|
24
|
+
super(message);
|
|
25
|
+
this.topic = topic;
|
|
26
|
+
this.operation = operation;
|
|
27
|
+
this.name = 'StreamingSubscriptionError';
|
|
28
|
+
}
|
|
29
|
+
}
|
|
22
30
|
/* istanbul ignore next */
|
|
23
31
|
export function timeoutPromise(fn, timeoutMs, msg, details) {
|
|
24
32
|
return new Promise(function (resolve, reject) {
|
package/dist/manifest.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "
|
|
2
|
+
"version": "release/v19.4.0",
|
|
3
3
|
"build": "2",
|
|
4
|
-
"buildDate": "2025-
|
|
4
|
+
"buildDate": "2025-09-19T03:18:00.531Z",
|
|
5
5
|
"indexFiles": [
|
|
6
6
|
{
|
|
7
|
-
"file": "v19.
|
|
7
|
+
"file": "v19.4.0/streaming-client.browser.js"
|
|
8
8
|
},
|
|
9
9
|
{
|
|
10
10
|
"file": "v19/streaming-client.browser.js"
|
package/dist/npm/CHANGELOG.md
CHANGED
|
@@ -4,7 +4,15 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
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
|
-
# [Unreleased](https://github.com/purecloudlabs/genesys-cloud-streaming-client/compare/v19.
|
|
7
|
+
# [Unreleased](https://github.com/purecloudlabs/genesys-cloud-streaming-client/compare/v19.4.0...HEAD)
|
|
8
|
+
|
|
9
|
+
# [v19.4.0](https://github.com/purecloudlabs/genesys-cloud-streaming-client/compare/v19.3.1...v19.4.0)
|
|
10
|
+
### Added
|
|
11
|
+
* [STREAM-865](https://inindca.atlassian.net/browse/STREAM-865) - Generate a test report in JUnit.xml format.
|
|
12
|
+
* [STREAM-892](https://inindca.atlassian.net/browse/STREAM-892) - Add `enablePartialBulkResubscribe` client option to make notifications bulk subscription changes succeed or fail each topic independently rather than a single failed topic causing the whole bulk operation to fail.
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
* [STREAM-865](https://inindca.atlassian.net/browse/STREAM-865) - Remove the SDP payload from SDP answer logs.
|
|
8
16
|
|
|
9
17
|
# [v19.3.1](https://github.com/purecloudlabs/genesys-cloud-streaming-client/compare/v19.3.0...v19.3.1)
|
|
10
18
|
* [STREAM-801](https://inindca.atlassian.net/browse/STREAM-801) - Update genesys-cloud-client-logger and axios to address Snyk vulnerability.
|
package/dist/npm/client.js
CHANGED
package/dist/npm/index.d.ts
CHANGED
|
@@ -6,5 +6,5 @@ export * from './types/media-session';
|
|
|
6
6
|
export * from './types/interfaces';
|
|
7
7
|
export * from './messenger';
|
|
8
8
|
export { HttpClient } from './http-client';
|
|
9
|
-
export { StreamingClientError, parseJwt } from './utils';
|
|
9
|
+
export { StreamingClientError, StreamingSubscriptionError, parseJwt } from './utils';
|
|
10
10
|
export default Client;
|
package/dist/npm/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.parseJwt = exports.StreamingClientError = exports.HttpClient = void 0;
|
|
3
|
+
exports.parseJwt = exports.StreamingSubscriptionError = exports.StreamingClientError = exports.HttpClient = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
/// <reference path="types/libs.ts" />
|
|
6
6
|
const client_1 = require("./client");
|
|
@@ -13,5 +13,6 @@ var http_client_1 = require("./http-client");
|
|
|
13
13
|
Object.defineProperty(exports, "HttpClient", { enumerable: true, get: function () { return http_client_1.HttpClient; } });
|
|
14
14
|
var utils_1 = require("./utils");
|
|
15
15
|
Object.defineProperty(exports, "StreamingClientError", { enumerable: true, get: function () { return utils_1.StreamingClientError; } });
|
|
16
|
+
Object.defineProperty(exports, "StreamingSubscriptionError", { enumerable: true, get: function () { return utils_1.StreamingSubscriptionError; } });
|
|
16
17
|
Object.defineProperty(exports, "parseJwt", { enumerable: true, get: function () { return utils_1.parseJwt; } });
|
|
17
18
|
exports.default = client_1.Client;
|
package/dist/npm/module.js
CHANGED
|
@@ -6,5 +6,5 @@ export * from './types/media-session';
|
|
|
6
6
|
export * from './types/interfaces';
|
|
7
7
|
export * from './messenger';
|
|
8
8
|
export { HttpClient } from './http-client';
|
|
9
|
-
export { StreamingClientError, parseJwt } from './utils';
|
|
9
|
+
export { StreamingClientError, StreamingSubscriptionError, parseJwt } from './utils';
|
|
10
10
|
export default Client;
|
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
import { PubsubEvent, PubsubSubscription, PubsubSubscriptionWithOptions } from 'stanza/protocol';
|
|
2
2
|
import { Client } from './client';
|
|
3
|
-
import { StreamingClientExtension } from './types/interfaces';
|
|
3
|
+
import { IClientOptions, StreamingClientExtension } from './types/interfaces';
|
|
4
4
|
import { NamedAgent } from './types/named-agent';
|
|
5
|
+
import { AxiosResponse } from 'axios';
|
|
5
6
|
export declare class Notifications implements StreamingClientExtension {
|
|
6
7
|
client: Client;
|
|
7
8
|
stanzaInstance?: NamedAgent;
|
|
8
9
|
subscriptions: any;
|
|
9
10
|
bulkSubscriptions: any;
|
|
10
11
|
topicPriorities: any;
|
|
11
|
-
debouncedResubscribe:
|
|
12
|
-
|
|
12
|
+
debouncedResubscribe: () => Promise<BulkSubscribeResult>;
|
|
13
|
+
enablePartialBulkResubscribe: boolean;
|
|
14
|
+
constructor(client: any, options?: IClientOptions);
|
|
13
15
|
get pubsubHost(): string;
|
|
14
16
|
handleStanzaInstanceChange(stanza: NamedAgent): void;
|
|
15
17
|
topicHandlers(topic: string): Array<(obj?: any) => void>;
|
|
@@ -32,23 +34,23 @@ export declare class Notifications implements StreamingClientExtension {
|
|
|
32
34
|
}>): Array<{
|
|
33
35
|
id: string;
|
|
34
36
|
}>;
|
|
35
|
-
makeBulkSubscribeRequest(topics: string[], options: any): Promise<
|
|
37
|
+
makeBulkSubscribeRequest(topics: string[], options: any): Promise<AxiosResponse<ChannelTopicsEntityListing>>;
|
|
36
38
|
createSubscription(topic: string, handler: (obj?: any) => void): void;
|
|
37
39
|
removeSubscription(topic: string, handler: (obj?: any) => void): void;
|
|
38
40
|
removeTopicPriority(topic: string): void;
|
|
39
41
|
getActiveIndividualTopics(): string[];
|
|
40
|
-
resubscribe(): Promise<
|
|
42
|
+
resubscribe(): Promise<BulkSubscribeResult>;
|
|
41
43
|
subscriptionsKeepAlive(): void;
|
|
42
44
|
getTopicParts(topic: string): {
|
|
43
45
|
prefix: string;
|
|
44
46
|
postfixes: string[];
|
|
45
47
|
};
|
|
46
48
|
setTopicPriorities(priorities?: {}): void;
|
|
47
|
-
subscribe(topic: string, handler?: (..._: any[]) => void, immediate?: boolean, priority?: number): Promise<
|
|
49
|
+
subscribe(topic: string, handler?: (..._: any[]) => void, immediate?: boolean, priority?: number): Promise<TopicSubscribeResult>;
|
|
48
50
|
unsubscribe(topic: string, handler?: (..._: any[]) => void, immediate?: boolean): Promise<any>;
|
|
49
51
|
bulkSubscribe(topics: string[], options?: BulkSubscribeOpts, priorities?: {
|
|
50
52
|
[topicName: string]: number;
|
|
51
|
-
}): Promise<
|
|
53
|
+
}): Promise<BulkSubscribeResult>;
|
|
52
54
|
get expose(): NotificationsAPI;
|
|
53
55
|
}
|
|
54
56
|
export interface NotificationsAPI {
|
|
@@ -62,3 +64,20 @@ export interface BulkSubscribeOpts {
|
|
|
62
64
|
replace?: boolean;
|
|
63
65
|
force?: boolean;
|
|
64
66
|
}
|
|
67
|
+
export interface BulkSubscribeResult {
|
|
68
|
+
[topic: string]: TopicSubscribeResult;
|
|
69
|
+
}
|
|
70
|
+
export interface TopicSubscribeResult {
|
|
71
|
+
topic: string;
|
|
72
|
+
state: 'Permitted' | 'Rejected' | 'Unknown';
|
|
73
|
+
rejectionReason?: string;
|
|
74
|
+
}
|
|
75
|
+
export interface ChannelTopicResponseEntity {
|
|
76
|
+
id: string;
|
|
77
|
+
state: 'Permitted' | 'Rejected';
|
|
78
|
+
rejectionReason?: string;
|
|
79
|
+
selfUri?: string;
|
|
80
|
+
}
|
|
81
|
+
export interface ChannelTopicsEntityListing {
|
|
82
|
+
entities: ChannelTopicResponseEntity[];
|
|
83
|
+
}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Notifications = void 0;
|
|
4
|
-
const
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const debounce_promise_1 = tslib_1.__importDefault(require("debounce-promise"));
|
|
5
6
|
const utils_1 = require("./utils");
|
|
7
|
+
const _1 = require("./");
|
|
6
8
|
const PUBSUB_HOST_DEFAULT = 'notifications.mypurecloud.com';
|
|
7
9
|
const MAX_SUBSCRIBABLE_TOPICS = 1000;
|
|
8
10
|
const DROPPED_TOPICS_DISPLAY_COUNT = 20;
|
|
@@ -11,14 +13,16 @@ function mergeAndDedup(arr1, arr2) {
|
|
|
11
13
|
return [...arr1, ...arr2].filter((t, i, arr) => arr.indexOf(t) === i);
|
|
12
14
|
}
|
|
13
15
|
class Notifications {
|
|
14
|
-
constructor(client) {
|
|
16
|
+
constructor(client, options) {
|
|
17
|
+
var _a;
|
|
15
18
|
this.subscriptions = {};
|
|
16
19
|
this.bulkSubscriptions = {};
|
|
17
20
|
this.topicPriorities = {};
|
|
18
21
|
this.client = client;
|
|
22
|
+
this.enablePartialBulkResubscribe = (_a = options === null || options === void 0 ? void 0 : options.enablePartialBulkResubscribe) !== null && _a !== void 0 ? _a : false;
|
|
19
23
|
client.on('pubsub:event', this.pubsubEvent.bind(this));
|
|
20
24
|
client.on('connected', this.subscriptionsKeepAlive.bind(this));
|
|
21
|
-
this.debouncedResubscribe =
|
|
25
|
+
this.debouncedResubscribe = debounce_promise_1.default(this.resubscribe.bind(this), 100);
|
|
22
26
|
}
|
|
23
27
|
get pubsubHost() {
|
|
24
28
|
try {
|
|
@@ -37,7 +41,7 @@ class Notifications {
|
|
|
37
41
|
this.stanzaInstance = stanza;
|
|
38
42
|
if (needsToResub) {
|
|
39
43
|
this.client.logger.info('resubscribing due to hard reconnect');
|
|
40
|
-
this.debouncedResubscribe();
|
|
44
|
+
void this.debouncedResubscribe();
|
|
41
45
|
}
|
|
42
46
|
}
|
|
43
47
|
topicHandlers(topic) {
|
|
@@ -178,7 +182,11 @@ class Notifications {
|
|
|
178
182
|
logger: this.client.logger
|
|
179
183
|
};
|
|
180
184
|
const channelId = this.stanzaInstance.channelId;
|
|
181
|
-
|
|
185
|
+
let path = `notifications/channels/${channelId}/subscriptions`;
|
|
186
|
+
if (this.enablePartialBulkResubscribe) {
|
|
187
|
+
path += '?ignoreErrors=true';
|
|
188
|
+
}
|
|
189
|
+
return this.client.http.requestApi(path, requestOptions);
|
|
182
190
|
}
|
|
183
191
|
createSubscription(topic, handler) {
|
|
184
192
|
const topics = utils_1.splitIntoIndividualTopics(topic);
|
|
@@ -234,7 +242,7 @@ class Notifications {
|
|
|
234
242
|
/* if we don't have bulk or individual subs, we don't need to resubscribe */
|
|
235
243
|
const noTopics = bulkSubs.length + this.getActiveIndividualTopics().length === 0;
|
|
236
244
|
if (noTopics) {
|
|
237
|
-
return Promise.resolve();
|
|
245
|
+
return Promise.resolve({});
|
|
238
246
|
}
|
|
239
247
|
/* only pass in bulk subs with the replace flag – bulkSubscribe() will handle merging our individual topics (see PCM-1846) */
|
|
240
248
|
return this.bulkSubscribe(bulkSubs, { replace: true });
|
|
@@ -289,7 +297,7 @@ class Notifications {
|
|
|
289
297
|
}
|
|
290
298
|
});
|
|
291
299
|
}
|
|
292
|
-
subscribe(topic, handler, immediate, priority) {
|
|
300
|
+
async subscribe(topic, handler, immediate, priority) {
|
|
293
301
|
if (priority) {
|
|
294
302
|
this.setTopicPriorities({ [topic]: priority });
|
|
295
303
|
}
|
|
@@ -307,7 +315,19 @@ class Notifications {
|
|
|
307
315
|
else {
|
|
308
316
|
this.bulkSubscriptions[topic] = true;
|
|
309
317
|
}
|
|
310
|
-
|
|
318
|
+
const result = await promise;
|
|
319
|
+
// Assume topic subscription succeeded if promise is resolved...
|
|
320
|
+
let topicResult = { topic, state: 'Permitted' };
|
|
321
|
+
// ... but if partial bulk resubscribe is enabled, use topic's individual result from the API response.
|
|
322
|
+
if (this.enablePartialBulkResubscribe && result && typeof result === 'object' && isTopicSubscribeResult(result[topic])) {
|
|
323
|
+
topicResult = result[topic];
|
|
324
|
+
}
|
|
325
|
+
// Topic result other than state=Permitted becomes a StreamingSubscriptionError promise rejection.
|
|
326
|
+
if (topicResult.state !== 'Permitted') {
|
|
327
|
+
const message = topicResult.rejectionReason || `Failed to subscribe topic ${topic}`;
|
|
328
|
+
throw new _1.StreamingSubscriptionError(message, topic, 'subscribe');
|
|
329
|
+
}
|
|
330
|
+
return topicResult;
|
|
311
331
|
}
|
|
312
332
|
unsubscribe(topic, handler, immediate) {
|
|
313
333
|
if (handler) {
|
|
@@ -335,13 +355,35 @@ class Notifications {
|
|
|
335
355
|
// if it's a forcible bulk subscribe, wipe out individual subscriptions
|
|
336
356
|
this.subscriptions = {};
|
|
337
357
|
}
|
|
338
|
-
await this.makeBulkSubscribeRequest(toSubscribe, options);
|
|
358
|
+
const response = await this.makeBulkSubscribeRequest(toSubscribe, options);
|
|
359
|
+
let topicResponseEntities = [];
|
|
360
|
+
if (response && response.data && 'entities' in response.data && Array.isArray(response.data.entities)) {
|
|
361
|
+
topicResponseEntities = response.data.entities;
|
|
362
|
+
}
|
|
363
|
+
const topicResponsesById = {};
|
|
364
|
+
for (const topicEntity of topicResponseEntities) {
|
|
365
|
+
topicResponsesById[topicEntity.id] = topicEntity;
|
|
366
|
+
}
|
|
367
|
+
const result = {};
|
|
339
368
|
if (options.replace) {
|
|
340
369
|
this.bulkSubscriptions = {};
|
|
341
370
|
}
|
|
342
371
|
topics.forEach(topic => {
|
|
343
372
|
this.bulkSubscriptions[topic] = true;
|
|
373
|
+
if (this.enablePartialBulkResubscribe) {
|
|
374
|
+
if (topic in topicResponsesById) {
|
|
375
|
+
const { state, rejectionReason } = topicResponsesById[topic];
|
|
376
|
+
result[topic] = { topic, state, rejectionReason };
|
|
377
|
+
}
|
|
378
|
+
else {
|
|
379
|
+
result[topic] = { topic, state: 'Unknown' };
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
else {
|
|
383
|
+
result[topic] = { topic, state: 'Permitted' };
|
|
384
|
+
}
|
|
344
385
|
});
|
|
386
|
+
return result;
|
|
345
387
|
}
|
|
346
388
|
get expose() {
|
|
347
389
|
return {
|
|
@@ -352,3 +394,12 @@ class Notifications {
|
|
|
352
394
|
}
|
|
353
395
|
}
|
|
354
396
|
exports.Notifications = Notifications;
|
|
397
|
+
function isTopicSubscribeResult(value) {
|
|
398
|
+
let hasTopic = false;
|
|
399
|
+
let hasValidState = false;
|
|
400
|
+
if (value && typeof value === 'object') {
|
|
401
|
+
hasTopic = 'topic' in value && typeof value.topic === 'string';
|
|
402
|
+
hasValidState = 'state' in value && ['Permitted', 'Rejected', 'Unknown'].includes(value.state);
|
|
403
|
+
}
|
|
404
|
+
return hasTopic && hasValidState;
|
|
405
|
+
}
|
|
@@ -295,7 +295,8 @@ class GenesysCloudMediaSession {
|
|
|
295
295
|
sdp: answer.sdp,
|
|
296
296
|
sessionId: this.id
|
|
297
297
|
};
|
|
298
|
-
|
|
298
|
+
// Do not log the SDP payload to avoid logging sensitive information.
|
|
299
|
+
this.logger.info('sending sdp answer', { sessionId: this.id, conversationId: this.conversationId });
|
|
299
300
|
return this.sendGenesysWebrtc({
|
|
300
301
|
jsonrpc: '2.0',
|
|
301
302
|
method: 'answer',
|
|
@@ -22,6 +22,8 @@ export interface IClientOptions {
|
|
|
22
22
|
appVersion?: string;
|
|
23
23
|
appId?: string;
|
|
24
24
|
customHeaders?: ICustomHeader;
|
|
25
|
+
/** Allow bulk topic resubscribe to succeed or fail per-topic rather than all or nothing */
|
|
26
|
+
enablePartialBulkResubscribe?: boolean;
|
|
25
27
|
}
|
|
26
28
|
export interface ICustomHeader {
|
|
27
29
|
[header: string]: string;
|
package/dist/npm/utils.d.ts
CHANGED
|
@@ -4,6 +4,12 @@ export declare class StreamingClientError extends Error {
|
|
|
4
4
|
details?: unknown;
|
|
5
5
|
constructor(type: StreamingClientErrorTypes | null, messageOrError: string | Error, details?: unknown);
|
|
6
6
|
}
|
|
7
|
+
export declare class StreamingSubscriptionError extends Error {
|
|
8
|
+
readonly topic?: string | undefined;
|
|
9
|
+
readonly operation?: "subscribe" | "unsubscribe" | undefined;
|
|
10
|
+
name: string;
|
|
11
|
+
constructor(message: string, topic?: string | undefined, operation?: "subscribe" | "unsubscribe" | undefined);
|
|
12
|
+
}
|
|
7
13
|
export declare function timeoutPromise(fn: Function, timeoutMs: number, msg: string, details?: any): Promise<any>;
|
|
8
14
|
export declare function delay(ms: number): Promise<void>;
|
|
9
15
|
export declare function splitIntoIndividualTopics(topicString: string): string[];
|
package/dist/npm/utils.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.iceIsDifferent = exports.getIcePwdFromSdp = exports.getUfragFromSdp = exports.calculatePayloadSize = exports.parseJwt = exports.retryPromise = exports.isVideoJid = exports.isSoftphoneJid = exports.isScreenRecordingJid = exports.isAcdJid = exports.splitIntoIndividualTopics = exports.delay = exports.timeoutPromise = exports.StreamingClientError = void 0;
|
|
3
|
+
exports.iceIsDifferent = exports.getIcePwdFromSdp = exports.getUfragFromSdp = exports.calculatePayloadSize = exports.parseJwt = exports.retryPromise = exports.isVideoJid = exports.isSoftphoneJid = exports.isScreenRecordingJid = exports.isAcdJid = exports.splitIntoIndividualTopics = exports.delay = exports.timeoutPromise = exports.StreamingSubscriptionError = exports.StreamingClientError = void 0;
|
|
4
4
|
const uuid_1 = require("uuid");
|
|
5
5
|
const timeout_error_1 = require("./types/timeout-error");
|
|
6
6
|
const interfaces_1 = require("./types/interfaces");
|
|
@@ -22,6 +22,15 @@ class StreamingClientError extends Error {
|
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
24
|
exports.StreamingClientError = StreamingClientError;
|
|
25
|
+
class StreamingSubscriptionError extends Error {
|
|
26
|
+
constructor(message, topic, operation) {
|
|
27
|
+
super(message);
|
|
28
|
+
this.topic = topic;
|
|
29
|
+
this.operation = operation;
|
|
30
|
+
this.name = 'StreamingSubscriptionError';
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
exports.StreamingSubscriptionError = StreamingSubscriptionError;
|
|
25
34
|
/* istanbul ignore next */
|
|
26
35
|
function timeoutPromise(fn, timeoutMs, msg, details) {
|
|
27
36
|
return new Promise(function (resolve, reject) {
|