-
Notifications
You must be signed in to change notification settings - Fork 7
Expand file tree
/
Copy pathindex.ts
More file actions
105 lines (90 loc) · 4 KB
/
Copy pathindex.ts
File metadata and controls
105 lines (90 loc) · 4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
import { errorParser, messageParser } from './NotificationParser';
import { notificationKeeperFactory } from './NotificationKeeper';
import { PUSH_RETRYABLE_ERROR, PUSH_NONRETRYABLE_ERROR, OCCUPANCY, CONTROL, SEGMENT_UPDATE, SPLIT_KILL, SPLIT_UPDATE, MEMBERSHIPS_MS_UPDATE, MEMBERSHIPS_LS_UPDATE, RB_SEGMENT_UPDATE } from '../constants';
import { IPushEventEmitter } from '../types';
import { ISseEventHandler } from '../SSEClient/types';
import { INotificationError, INotificationMessage } from './types';
import { ILogger } from '../../../logger/types';
import { STREAMING_PARSING_ERROR_FAILS, ERROR_STREAMING_SSE, STREAMING_PARSING_MESSAGE_FAILS, STREAMING_NEW_MESSAGE } from '../../../logger/constants';
import { ABLY_ERROR, NON_REQUESTED, SSE_CONNECTION_ERROR } from '../../../utils/constants';
import { ITelemetryTracker } from '../../../trackers/types';
/**
* Factory for SSEHandler, which processes SSEClient messages and emits the corresponding push events.
*
* @param log - factory logger
* @param pushEmitter - emitter for events related to streaming support
*/
export function SSEHandlerFactory(log: ILogger, pushEmitter: IPushEventEmitter, telemetryTracker: ITelemetryTracker): ISseEventHandler {
const notificationKeeper = notificationKeeperFactory(pushEmitter, telemetryTracker);
function isRetryableError(error: INotificationError): boolean {
if (error.parsedData && error.parsedData.code) {
// Ably error
const code = error.parsedData.code;
telemetryTracker.streamingEvent(ABLY_ERROR, code);
// 401 errors due to invalid or expired token (e.g., if refresh token couldn't be executed)
if (40140 <= code && code <= 40149) return true;
// Others 4XX errors (e.g., bad request from the SDK)
if (40000 <= code && code <= 49999) return false;
} else {
// network errors or 5XX HTTP errors
telemetryTracker.streamingEvent(SSE_CONNECTION_ERROR, NON_REQUESTED);
}
return true;
}
return {
handleOpen() {
notificationKeeper.handleOpen();
},
/* HTTP & Network errors */
handleError(error) {
let errorWithParsedData: INotificationError = error;
try {
errorWithParsedData = errorParser(error);
} catch (err) {
log.warn(STREAMING_PARSING_ERROR_FAILS, [err]);
}
let errorMessage = (errorWithParsedData.parsedData && errorWithParsedData.parsedData.message) || errorWithParsedData.message;
log.error(ERROR_STREAMING_SSE, [errorMessage]);
if (isRetryableError(errorWithParsedData)) {
pushEmitter.emit(PUSH_RETRYABLE_ERROR);
} else {
pushEmitter.emit(PUSH_NONRETRYABLE_ERROR);
}
},
/* NotificationProcessor */
handleMessage(message) {
let messageWithParsedData: INotificationMessage | undefined;
try {
messageWithParsedData = messageParser(message);
if (!messageWithParsedData) return; // Messages with empty data are ignored
} catch (err) {
log.warn(STREAMING_PARSING_MESSAGE_FAILS, [err]);
return;
}
const { parsedData, data, channel, timestamp } = messageWithParsedData;
log.debug(STREAMING_NEW_MESSAGE, [data]);
// we only handle update events if streaming is up
if (!notificationKeeper.isStreamingUp() && [OCCUPANCY, CONTROL].indexOf(parsedData.type) === -1) return;
switch (parsedData.type) {
/* update events */
case SPLIT_UPDATE:
case SEGMENT_UPDATE:
case MEMBERSHIPS_MS_UPDATE:
case MEMBERSHIPS_LS_UPDATE:
case SPLIT_KILL:
case RB_SEGMENT_UPDATE:
pushEmitter.emit(parsedData.type, parsedData);
break;
/* occupancy & control events, handled by NotificationManagerKeeper */
case OCCUPANCY:
notificationKeeper.handleOccupancyEvent(parsedData.metrics.publishers, channel, timestamp);
break;
case CONTROL:
notificationKeeper.handleControlEvent(parsedData.controlType, channel, timestamp);
break;
default:
break;
}
},
};
}