noibu-react-native 0.2.2 → 0.2.4
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 +1 -1
- package/dist/api/clientConfig.js +225 -217
- package/dist/api/helpCode.js +61 -87
- package/dist/api/metroplexSocket.js +460 -463
- package/dist/api/storedPageVisit.js +150 -208
- package/dist/constants.js +10 -2
- package/dist/entry/init.js +65 -63
- package/dist/monitors/{appNavigationMonitor.js → AppNavigationMonitor.js} +12 -22
- package/dist/monitors/ClickMonitor.js +198 -0
- package/dist/monitors/ErrorMonitor.js +206 -0
- package/dist/monitors/KeyboardInputMonitor.js +60 -0
- package/dist/monitors/PageMonitor.js +98 -0
- package/dist/monitors/RequestMonitor.js +390 -0
- package/dist/monitors/http-tools/GqlErrorValidator.js +259 -0
- package/dist/monitors/http-tools/HTTPDataBundler.js +458 -0
- package/dist/monitors/integrations/react-native-navigation-integration.js +4 -2
- package/dist/pageVisit/EventDebouncer.js +99 -0
- package/dist/pageVisit/pageVisitEventError.js +2 -2
- package/dist/pageVisit/pageVisitEventHTTP.js +79 -93
- package/dist/react/ErrorBoundary.js +18 -15
- package/dist/sessionRecorder/nativeSessionRecorderSubscription.js +3 -2
- package/dist/sessionRecorder/sessionRecorder.js +152 -151
- package/dist/{api → src/api}/clientConfig.d.ts +2 -2
- package/dist/{api → src/api}/helpCode.d.ts +10 -16
- package/dist/{api → src/api}/metroplexSocket.d.ts +48 -67
- package/dist/{api → src/api}/storedPageVisit.d.ts +12 -21
- package/dist/{constants.d.ts → src/constants.d.ts} +45 -0
- package/dist/{entry → src/entry}/init.d.ts +1 -1
- package/dist/src/monitors/AppNavigationMonitor.d.ts +18 -0
- package/dist/src/monitors/ClickMonitor.d.ts +31 -0
- package/dist/src/monitors/ErrorMonitor.d.ts +63 -0
- package/dist/{monitors/keyboardInputMonitor.d.ts → src/monitors/KeyboardInputMonitor.d.ts} +7 -4
- package/dist/{monitors/pageMonitor.d.ts → src/monitors/PageMonitor.d.ts} +6 -8
- package/dist/src/monitors/RequestMonitor.d.ts +94 -0
- package/dist/src/monitors/http-tools/GqlErrorValidator.d.ts +59 -0
- package/dist/src/monitors/http-tools/HTTPDataBundler.d.ts +112 -0
- package/dist/{monitors → src/monitors}/integrations/react-native-navigation-integration.d.ts +3 -2
- package/dist/src/pageVisit/EventDebouncer.d.ts +24 -0
- package/dist/{pageVisit → src/pageVisit}/pageVisit.d.ts +1 -1
- package/dist/src/pageVisit/pageVisitEventHTTP.d.ts +25 -0
- package/dist/{sessionRecorder → src/sessionRecorder}/types.d.ts +1 -1
- package/dist/{storage → src/storage}/rnStorageProvider.d.ts +1 -1
- package/dist/{storage → src/storage}/storage.d.ts +2 -2
- package/dist/{storage → src/storage}/storageProvider.d.ts +3 -3
- package/dist/{utils → src/utils}/function.d.ts +27 -7
- package/dist/{utils → src/utils}/object.d.ts +11 -8
- package/dist/src/utils/piiRedactor.d.ts +11 -0
- package/dist/src/utils/polyfills.d.ts +4 -0
- package/dist/storage/rnStorageProvider.js +7 -4
- package/dist/storage/storage.js +43 -35
- package/dist/storage/storageProvider.js +23 -19
- package/dist/types/Config.d.ts +24 -20
- package/dist/types/Metroplex.types.d.ts +73 -0
- package/dist/types/Monitor.d.ts +11 -0
- package/dist/types/Monitor.js +19 -0
- package/dist/types/PageVisit.types.d.ts +8 -0
- package/dist/types/PageVisitErrors.types.d.ts +114 -0
- package/dist/types/PageVisitEvents.types.d.ts +91 -0
- package/dist/types/PageVisitMetrics.types.d.ts +27 -0
- package/dist/types/Storage.d.ts +1 -1
- package/dist/types/StoredPageVisit.types.d.ts +4 -47
- package/dist/types/WrappedObjects.d.ts +6 -0
- package/dist/utils/function.js +110 -77
- package/dist/utils/object.js +59 -6
- package/dist/utils/piiRedactor.js +98 -0
- package/dist/utils/polyfills.js +24 -0
- package/package.json +8 -8
- package/dist/monitors/appNavigationMonitor.d.ts +0 -22
- package/dist/monitors/clickMonitor.d.ts +0 -44
- package/dist/monitors/clickMonitor.js +0 -251
- package/dist/monitors/errorMonitor.d.ts +0 -28
- package/dist/monitors/errorMonitor.js +0 -180
- package/dist/monitors/gqlErrorValidator.d.ts +0 -82
- package/dist/monitors/gqlErrorValidator.js +0 -306
- package/dist/monitors/httpDataBundler.d.ts +0 -161
- package/dist/monitors/httpDataBundler.js +0 -725
- package/dist/monitors/inputMonitor.d.ts +0 -34
- package/dist/monitors/inputMonitor.js +0 -138
- package/dist/monitors/keyboardInputMonitor.js +0 -66
- package/dist/monitors/pageMonitor.js +0 -122
- package/dist/monitors/requestMonitor.d.ts +0 -10
- package/dist/monitors/requestMonitor.js +0 -401
- package/dist/pageVisit/pageVisitEventHTTP.d.ts +0 -18
- package/dist/types/PageVisit.d.ts +0 -22
- package/dist/types/ReactNative.d.ts +0 -4
- package/dist/types/globals.d.ts +0 -45
- /package/dist/{api → src/api}/inputManager.d.ts +0 -0
- /package/dist/{api → src/api}/storedMetrics.d.ts +0 -0
- /package/dist/{const_matchers.d.ts → src/const_matchers.d.ts} +0 -0
- /package/dist/{entry → src/entry}/index.d.ts +0 -0
- /package/dist/{pageVisit → src/pageVisit}/pageVisitEventError.d.ts +0 -0
- /package/dist/{pageVisit → src/pageVisit}/userStep.d.ts +0 -0
- /package/dist/{react → src/react}/ErrorBoundary.d.ts +0 -0
- /package/dist/{sessionRecorder → src/sessionRecorder}/nativeSessionRecorderSubscription.d.ts +0 -0
- /package/dist/{sessionRecorder → src/sessionRecorder}/sessionRecorder.d.ts +0 -0
- /package/dist/{utils → src/utils}/date.d.ts +0 -0
- /package/dist/{utils → src/utils}/eventlistener.d.ts +0 -0
- /package/dist/{utils → src/utils}/log.d.ts +0 -0
- /package/dist/{utils → src/utils}/performance.d.ts +0 -0
- /package/dist/{utils → src/utils}/stacktrace-parser.d.ts +0 -0
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import { __awaiter } from 'tslib';
|
|
1
2
|
import uuid from 'react-native-uuid';
|
|
2
3
|
import { getUserAgent, stringifyJSON, getVideoRecorderType } from '../utils/function.js';
|
|
3
4
|
import { addSafeEventListener } from '../utils/eventlistener.js';
|
|
4
|
-
import { GET_METROPLEX_BASE_SOCKET_URL, METROPLEX_FRAG_ROUTE, GET_METROPLEX_POST_URL, METROPLEX_RETRY_FREQUENCY,
|
|
5
|
+
import { GET_METROPLEX_BASE_SOCKET_URL, METROPLEX_FRAG_ROUTE, GET_METROPLEX_POST_URL, METROPLEX_RETRY_FREQUENCY, SEQ_NUM_ATT_NAME, WORK_REQUEST_ATT_NAME, HELP_CODE_ATT_NAME, PV_METROPLEX_TYPE, PAGE_VISIT_PART_ATT_NAME, PV_EVENTS_ATT_NAME, TYPE_ATT_NAME, USERSTEP_EVENT_TYPE, GET_MAX_METROPLEX_RECONNECTION_NUMBER, MAX_METROPLEX_CONNECTION_COUNT, GET_METROPLEX_CONSECUTIVE_CONNECTION_DELAY, END_AT_ATT_NAME, SEVERITY, OK_SOCKET_MESSAGE, CLOSE_CONNECTION_FORCEFULLY, BLOCK_SOCKET_MESSAGE, STOP_STORING_PV_SOCKET_MESSAGE, STOP_STORING_VID_SOCKET_MESSAGE, VIDEO_METROPLEX_TYPE, PAGE_VISIT_VID_FRAG_ATT_NAME, MAX_BEACON_PAYLOAD_SIZE, PAGE_VISIT_INFORMATION_ATT_NAME, PAGE_VISIT_HTTP_DATA_ATT_NAME, VIDEO_PART_COUNT_ATT_NAME, IS_LAST_ATT_NAME, HTTP_DATA_METROPLEX_TYPE, BROWSER_ID_ATT_NAME, PV_ID_ATT_NAME, VER_ATT_NAME, CURRENT_PV_VERSION, PV_SEQ_ATT_NAME, ON_URL_ATT_NAME, REF_URL_ATT_NAME, STARTED_AT_ATT_NAME, CONN_COUNT_ATT_NAME, COLLECT_VER_ATT_NAME, CURRENT_NOIBUJS_VERSION, SCRIPT_ID_ATT_NAME, GET_SCRIPT_ID, SCRIPT_INSTANCE_ID_ATT_NAME, METROPLEX_SOCKET_INSTANCE_ID_ATT_NAME, SOCKET_INSTANCE_ID_ATT_NAME, VIDEO_RECORDER_ATT_NAME, META_DATA_METROPLEX_TYPE, PAGE_VISIT_META_DATA_ATT_NAME, MAX_RETRY_MSG_Q_SIZE } from '../constants.js';
|
|
5
6
|
import ClientConfig from './clientConfig.js';
|
|
6
7
|
import StoredMetrics from './storedMetrics.js';
|
|
7
8
|
import StoredPageVisit from './storedPageVisit.js';
|
|
@@ -9,47 +10,39 @@ import { safePerformanceNow } from '../utils/performance.js';
|
|
|
9
10
|
import { isDateOverwritten } from '../utils/date.js';
|
|
10
11
|
import { unwrapNoibuWrapped } from '../utils/object.js';
|
|
11
12
|
import { noibuLog } from '../utils/log.js';
|
|
13
|
+
import { Singleton } from '../types/Monitor.js';
|
|
12
14
|
|
|
13
|
-
/**
|
|
15
|
+
/**
|
|
16
|
+
* Implements rolling window of specified size,
|
|
17
|
+
* but only makes a cut once array length exceeds 150%.
|
|
18
|
+
* During downsize deletes oldest (lowest indexes) elements.
|
|
19
|
+
*/
|
|
20
|
+
function createSlidingArrayOfSize(size, arraySource = [], downsizeThreshold = 1.5, downsizeFactor = 2) {
|
|
21
|
+
return new Proxy(arraySource, {
|
|
22
|
+
/**
|
|
23
|
+
* override push to implement sliding window
|
|
24
|
+
*/
|
|
25
|
+
get(target, p) {
|
|
26
|
+
if (p === 'push') {
|
|
27
|
+
return (...args) => {
|
|
28
|
+
target.push(...args);
|
|
29
|
+
if (target.length >= downsizeThreshold * size) {
|
|
30
|
+
target.splice(0, size / downsizeFactor);
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
return target[p];
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
}
|
|
14
38
|
/** Manages the socket to Metroplex */
|
|
15
|
-
class MetroplexSocket {
|
|
16
|
-
_initialURL;
|
|
17
|
-
ackedOnce;
|
|
18
|
-
connectionCount;
|
|
19
|
-
// promise that will resolve once the socket is connected and metroplex has acknowledged
|
|
20
|
-
connectionPromise;
|
|
21
|
-
connectionURL;
|
|
22
|
-
currentConnectionAttempts;
|
|
23
|
-
forceClosed;
|
|
24
|
-
helpCodeCb;
|
|
25
|
-
initialReferingURL;
|
|
26
|
-
static instance;
|
|
27
|
-
instanceId;
|
|
28
|
-
isRetryLoopDisabled;
|
|
29
|
-
latestReceivedSeqNumStoredTime;
|
|
30
|
-
latestReceivedSeqNumber;
|
|
31
|
-
messageSequenceNum;
|
|
32
|
-
metroRetryFrequencyMS;
|
|
33
|
-
metroplexTypeLock;
|
|
34
|
-
pageVisitInfoSent;
|
|
35
|
-
postURL;
|
|
36
|
-
previousMessageType;
|
|
37
|
-
retryMessageQueue;
|
|
38
|
-
retryMetroplexInterval;
|
|
39
|
-
scriptInstanceId;
|
|
40
|
-
sessionLength;
|
|
41
|
-
sessionStartTime;
|
|
42
|
-
sessionTimestamp;
|
|
43
|
-
socket;
|
|
44
|
-
socketInstanceId;
|
|
45
|
-
socketCloseCodes;
|
|
46
|
-
socketOpens;
|
|
39
|
+
class MetroplexSocket extends Singleton {
|
|
47
40
|
/**
|
|
48
41
|
* Creates an instance of metroplex
|
|
49
42
|
* id of script, to make sure only a single socket is open
|
|
50
43
|
*/
|
|
51
44
|
constructor(scriptInstanceId) {
|
|
52
|
-
|
|
45
|
+
super();
|
|
53
46
|
// this flag is set to true when we manually need to close the
|
|
54
47
|
// socket. It happens currently during the pagehide event and
|
|
55
48
|
// if we havent sent a message to our backend in some time.
|
|
@@ -64,12 +57,13 @@ class MetroplexSocket {
|
|
|
64
57
|
this.currentConnectionAttempts = 0;
|
|
65
58
|
// how many successful connections to metroplex
|
|
66
59
|
this.connectionCount = 0;
|
|
67
|
-
//
|
|
60
|
+
// session start time, used to calculate accurate end time
|
|
68
61
|
this.sessionStartTime = safePerformanceNow();
|
|
62
|
+
this.connectionPromise = null;
|
|
69
63
|
// Whether or not we have sent the page visit information after connecting the socket
|
|
70
64
|
this.pageVisitInfoSent = false;
|
|
71
65
|
// socket connection url
|
|
72
|
-
this.connectionURL = `${
|
|
66
|
+
this.connectionURL = `${GET_METROPLEX_BASE_SOCKET_URL()}/${METROPLEX_FRAG_ROUTE}`;
|
|
73
67
|
// post endpoint for the same events we would send to the socket
|
|
74
68
|
this.postURL = GET_METROPLEX_POST_URL();
|
|
75
69
|
// sequence number of the message sent to metroplex
|
|
@@ -78,25 +72,20 @@ class MetroplexSocket {
|
|
|
78
72
|
this.latestReceivedSeqNumber = -1;
|
|
79
73
|
// set to true only when we start running behind on messages in metroplex
|
|
80
74
|
this.isRetryLoopDisabled = false;
|
|
81
|
-
|
|
82
|
-
this.retryMessageQueue =
|
|
75
|
+
/** messages that need to be resent to metroplex since they are lacking confirmation */
|
|
76
|
+
this.retryMessageQueue = createSlidingArrayOfSize(MAX_RETRY_MSG_Q_SIZE);
|
|
83
77
|
// this map will hold all types that noibujs should not send to metroplex
|
|
84
78
|
this.metroplexTypeLock = {};
|
|
85
79
|
// setting initial URL at the start in order to guarentee that the
|
|
86
80
|
// current page visit has the real initial onURL. Fragments and SPA's
|
|
87
81
|
// can change the URL without reloading the page.
|
|
88
|
-
this.
|
|
89
|
-
|
|
90
|
-
initialURL: this._initialURL,
|
|
91
|
-
});
|
|
92
|
-
this.initialReferingURL = '';
|
|
82
|
+
this.initialURL = ClientConfig.getInstance().globalUrl;
|
|
83
|
+
this.initialReferringURL = '';
|
|
93
84
|
this.sessionTimestamp = new Date();
|
|
94
85
|
// latest time that we received a confirmation message from metroplex
|
|
95
86
|
this.latestReceivedSeqNumStoredTime = new Date();
|
|
96
87
|
// unique instance id of Metroplex Socket
|
|
97
88
|
this.instanceId = uuid.v4();
|
|
98
|
-
// unique script instance id
|
|
99
|
-
this.scriptInstanceId = scriptInstanceId;
|
|
100
89
|
// length of the session based on page visit events
|
|
101
90
|
this.sessionLength = 0;
|
|
102
91
|
// track socket closure codes for debug
|
|
@@ -107,51 +96,30 @@ class MetroplexSocket {
|
|
|
107
96
|
this.ackedOnce = false;
|
|
108
97
|
// retry frequency in ms
|
|
109
98
|
this.metroRetryFrequencyMS = METROPLEX_RETRY_FREQUENCY;
|
|
110
|
-
this.helpCodeCb = null;
|
|
111
99
|
this.retryMetroplexInterval = null;
|
|
100
|
+
this.helpCodeCb = null;
|
|
101
|
+
noibuLog('Metroplex constructor', {
|
|
102
|
+
initialURL: this.initialURL,
|
|
103
|
+
scriptInstanceId,
|
|
104
|
+
});
|
|
105
|
+
this.scriptInstanceId = scriptInstanceId;
|
|
112
106
|
// Connect the WS
|
|
113
107
|
this.connectionPromise = this.connectSocket();
|
|
114
108
|
// Set up the offload events immediately
|
|
115
109
|
this._setupOffloadEvents();
|
|
116
110
|
}
|
|
117
|
-
/**
|
|
118
|
-
* gets the singleton instance
|
|
119
|
-
* @returns {MetroplexSocket}
|
|
120
|
-
*/
|
|
121
|
-
static getInstance(scriptInstanceId) {
|
|
122
|
-
if (!this.instance) {
|
|
123
|
-
this.instance = new MetroplexSocket(scriptInstanceId);
|
|
124
|
-
}
|
|
125
|
-
return this.instance;
|
|
126
|
-
}
|
|
127
111
|
/**
|
|
128
112
|
* Adds the seq num field to the given payload depending on whether its
|
|
129
113
|
* a page visit part or video frag
|
|
130
114
|
*/
|
|
131
115
|
_addSeqNumToPayload(type, payload) {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
this._setSeqNumInPayloadAndIncrementSeqNum(PAGE_VISIT_VID_FRAG_ATT_NAME, payload);
|
|
138
|
-
break;
|
|
139
|
-
case HTTP_DATA_METROPLEX_TYPE:
|
|
140
|
-
this._setSeqNumInPayloadAndIncrementSeqNum(PAGE_VISIT_HTTP_DATA_ATT_NAME, payload);
|
|
141
|
-
break;
|
|
142
|
-
case META_DATA_METROPLEX_TYPE:
|
|
143
|
-
this._setSeqNumInPayloadAndIncrementSeqNum(PAGE_VISIT_META_DATA_ATT_NAME, payload);
|
|
144
|
-
break;
|
|
116
|
+
const newPayload = Object.assign({}, payload);
|
|
117
|
+
const key = MetroplexSocket.typeToPayloadPropMap[type];
|
|
118
|
+
if (key) {
|
|
119
|
+
newPayload[key][SEQ_NUM_ATT_NAME] = this.messageSequenceNum;
|
|
120
|
+
this.messageSequenceNum += 1;
|
|
145
121
|
}
|
|
146
|
-
|
|
147
|
-
/**
|
|
148
|
-
* sets the seq num in the payload for the given key and increments the
|
|
149
|
-
* global seq number
|
|
150
|
-
*/
|
|
151
|
-
_setSeqNumInPayloadAndIncrementSeqNum(payloadKey, payload) {
|
|
152
|
-
// eslint-disable-next-line no-param-reassign
|
|
153
|
-
payload[payloadKey][SEQ_NUM_ATT_NAME] = this.messageSequenceNum;
|
|
154
|
-
this.messageSequenceNum += 1;
|
|
122
|
+
return newPayload;
|
|
155
123
|
}
|
|
156
124
|
/** requests help code and saves a callback to be called on response */
|
|
157
125
|
requestHelpCode(cb) {
|
|
@@ -165,50 +133,53 @@ class MetroplexSocket {
|
|
|
165
133
|
* Queues the message if the connection isn't open yet.
|
|
166
134
|
* returns true if message was sent succefully, false otherwise
|
|
167
135
|
*/
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
136
|
+
sendMessage(type, payload) {
|
|
137
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
138
|
+
noibuLog('sendMessage start', { type });
|
|
139
|
+
// if we have a lock on this specific type, we dont send it
|
|
140
|
+
if (type in this.metroplexTypeLock ||
|
|
141
|
+
ClientConfig.getInstance().isClientDisabled) {
|
|
142
|
+
noibuLog('sendMessage quitting due to', {
|
|
143
|
+
typeInLock: type in this.metroplexTypeLock,
|
|
144
|
+
isClientDisabled: ClientConfig.getInstance().isClientDisabled,
|
|
145
|
+
});
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
if (type !== WORK_REQUEST_ATT_NAME) {
|
|
149
|
+
// Increasing the message sequence number for every message we send to metroplex
|
|
150
|
+
// and move the data to the retry queue.
|
|
151
|
+
this._addSeqNumToPayload(type, payload);
|
|
152
|
+
// push the message to the retry queue immediately in case socket isnt connected
|
|
153
|
+
this.retryMessageQueue.push({ payload, type });
|
|
154
|
+
StoredPageVisit.getInstance().checkAndStoreRetryQueue(this.retryMessageQueue, yield this.getPageInformation());
|
|
155
|
+
}
|
|
156
|
+
// send the socket message if we are connected and have sent page visit info
|
|
157
|
+
if (this.isConnected() && this.pageVisitInfoSent) {
|
|
158
|
+
// sending the data to metroplex
|
|
159
|
+
yield this._sendSocketMessage(payload);
|
|
160
|
+
}
|
|
161
|
+
this.previousMessageType = type;
|
|
162
|
+
// Only update the last message send if its a page visit with user action
|
|
163
|
+
// ensure this is done regardless of whether socket is connected or not,
|
|
164
|
+
// so that pagehide will post PV if user is active but socket is not
|
|
165
|
+
if (type === PV_METROPLEX_TYPE && payload[PAGE_VISIT_PART_ATT_NAME]) {
|
|
166
|
+
const events = payload[PAGE_VISIT_PART_ATT_NAME][PV_EVENTS_ATT_NAME]
|
|
167
|
+
? payload[PAGE_VISIT_PART_ATT_NAME][PV_EVENTS_ATT_NAME]
|
|
168
|
+
: [];
|
|
169
|
+
yield this._updateLatestPvTimestamp(events);
|
|
170
|
+
}
|
|
171
|
+
return true;
|
|
172
|
+
});
|
|
204
173
|
}
|
|
205
174
|
/** Updates the latest pv message sent timestamp if events contain any user steps
|
|
206
175
|
*/
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
176
|
+
_updateLatestPvTimestamp(events) {
|
|
177
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
178
|
+
const userstepsEvents = events.filter(ev => ev[TYPE_ATT_NAME] === USERSTEP_EVENT_TYPE);
|
|
179
|
+
if (userstepsEvents.length > 0) {
|
|
180
|
+
yield ClientConfig.getInstance().updateLastActiveTime(new Date());
|
|
181
|
+
}
|
|
182
|
+
});
|
|
212
183
|
}
|
|
213
184
|
/** returns true if the socket is either connecting or connected to metroplex */
|
|
214
185
|
isConnected() {
|
|
@@ -231,77 +202,79 @@ class MetroplexSocket {
|
|
|
231
202
|
}
|
|
232
203
|
/** Connects the web socket to metroplex and calls callback upon successfully connecting
|
|
233
204
|
*/
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
}
|
|
241
|
-
this.currentConnectionAttempts += 1;
|
|
242
|
-
this.socket = new WebSocket(this.connectionURL, undefined, {
|
|
243
|
-
headers: {
|
|
244
|
-
'User-Agent': await getUserAgent(), // must pass useragent explicitly
|
|
245
|
-
},
|
|
246
|
-
});
|
|
247
|
-
this.socketInstanceId = uuid.v4();
|
|
248
|
-
this.socket.onerror = error => {
|
|
249
|
-
noibuLog('metroplex handleConnect, onerror', { error });
|
|
250
|
-
// use for metrics
|
|
251
|
-
// there is no useful error message/description from this event
|
|
252
|
-
};
|
|
253
|
-
// once the socket closes
|
|
254
|
-
this.socket.onclose = async (event) => {
|
|
255
|
-
noibuLog('metroplex handleConnect, onclose', { event });
|
|
256
|
-
// Reset this to prevent sending messages before the PVI is sent on reconnect
|
|
257
|
-
this.pageVisitInfoSent = false;
|
|
258
|
-
// we do not reconnect if the page is unloading
|
|
259
|
-
if (this.forceClosed) {
|
|
205
|
+
handleConnect(forceOpen) {
|
|
206
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
207
|
+
noibuLog('metroplex handleConnect');
|
|
208
|
+
// if we are connected or about to connect, we do not reset
|
|
209
|
+
// the socket information
|
|
210
|
+
if (!forceOpen && (this.isConnected() || this.isConnecting())) {
|
|
260
211
|
return;
|
|
261
212
|
}
|
|
262
|
-
|
|
263
|
-
this.
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
}
|
|
274
|
-
//
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
//
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
//
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
213
|
+
this.currentConnectionAttempts += 1;
|
|
214
|
+
this.socket = new WebSocket(this.connectionURL, undefined, {
|
|
215
|
+
headers: {
|
|
216
|
+
'User-Agent': yield getUserAgent(), // must pass useragent explicitly
|
|
217
|
+
},
|
|
218
|
+
});
|
|
219
|
+
this.socketInstanceId = uuid.v4();
|
|
220
|
+
this.socket.onerror = error => {
|
|
221
|
+
noibuLog('metroplex handleConnect, onerror', { error });
|
|
222
|
+
// use for metrics
|
|
223
|
+
// there is no useful error message/description from this event
|
|
224
|
+
};
|
|
225
|
+
// once the socket closes
|
|
226
|
+
this.socket.onclose = (event) => __awaiter(this, void 0, void 0, function* () {
|
|
227
|
+
noibuLog('metroplex handleConnect, onclose', { event });
|
|
228
|
+
// Reset this to prevent sending messages before the PVI is sent on reconnect
|
|
229
|
+
this.pageVisitInfoSent = false;
|
|
230
|
+
// we do not reconnect if the page is unloading
|
|
231
|
+
if (this.forceClosed) {
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
// track socket closure codes and times
|
|
235
|
+
this.socketCloseCodes.push(`${!isDateOverwritten() ? new Date().toISOString() : ''}:${event.code}`);
|
|
236
|
+
// we are already in the process of reconnecting
|
|
237
|
+
// we do not attempt to reconnect yet
|
|
238
|
+
if (this.isConnecting()) {
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
// Clear the interval that resends unconfirmed msgs so we don't try send while the socket
|
|
242
|
+
// is and starting back up, after which it will be restarted
|
|
243
|
+
if (this.retryMetroplexInterval) {
|
|
244
|
+
clearInterval(this.retryMetroplexInterval);
|
|
245
|
+
}
|
|
246
|
+
// if we tried reconnecting too many times we abandon
|
|
247
|
+
if (this.currentConnectionAttempts >=
|
|
248
|
+
GET_MAX_METROPLEX_RECONNECTION_NUMBER()) {
|
|
249
|
+
// if we tried beyond the threshold, we block ourselves a short
|
|
250
|
+
// while fix the issue
|
|
251
|
+
yield ClientConfig.getInstance().lockClientUntilNextPage('Too many reconnection attempts, locking until next page');
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
// if we tried reconnecting too many times we abandon
|
|
255
|
+
if (this.connectionCount >= MAX_METROPLEX_CONNECTION_COUNT) {
|
|
256
|
+
// if we tried beyond the threshold, we block ourselves a short
|
|
257
|
+
// while fix the issue
|
|
258
|
+
yield ClientConfig.getInstance().lockClientUntilNextPage('Too many connections, locking until next page');
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
// try to reconnect on close but after a certain delay based off unsuccessful connections
|
|
262
|
+
setTimeout(() => {
|
|
263
|
+
this.handleConnect(false);
|
|
264
|
+
}, Math.pow(this.currentConnectionAttempts, 2) * GET_METROPLEX_CONSECUTIVE_CONNECTION_DELAY());
|
|
265
|
+
});
|
|
266
|
+
// everytime we get a message from the socket
|
|
267
|
+
this.socket.onmessage = event => {
|
|
268
|
+
noibuLog('metroplex handleConnect, onmessage', { event });
|
|
269
|
+
this._onSocketMessage(event);
|
|
270
|
+
};
|
|
271
|
+
// the moment we open the socket
|
|
272
|
+
this.socket.onopen = () => {
|
|
273
|
+
// track socket open codes and times
|
|
274
|
+
this.socketOpens.push(`${!isDateOverwritten() ? new Date().toISOString() : ''}`);
|
|
275
|
+
this._onSocketOpen();
|
|
276
|
+
};
|
|
277
|
+
});
|
|
305
278
|
}
|
|
306
279
|
/**
|
|
307
280
|
* connectSocket will establish a websocket connection to the metroplex
|
|
@@ -328,95 +301,99 @@ class MetroplexSocket {
|
|
|
328
301
|
if (isPageVisit) {
|
|
329
302
|
this.sessionLength = delta;
|
|
330
303
|
}
|
|
331
|
-
return {
|
|
332
|
-
...payload,
|
|
333
|
-
[END_AT_ATT_NAME]: new Date(this.sessionTimestamp.getTime() + delta).toISOString(),
|
|
334
|
-
};
|
|
304
|
+
return Object.assign(Object.assign({}, payload), { [END_AT_ATT_NAME]: new Date(this.sessionTimestamp.getTime() + delta).toISOString() });
|
|
335
305
|
}
|
|
336
306
|
/** open handler for socket */
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
307
|
+
_onSocketOpen() {
|
|
308
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
309
|
+
// if we are not connected, we do not send any messages
|
|
310
|
+
if (!this.isConnected() || ClientConfig.getInstance().isClientDisabled) {
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
yield this._sendSocketMessage(yield this.getPageInformation());
|
|
314
|
+
// Set this to allow normal page visit and video frag messages to be sent
|
|
315
|
+
this.pageVisitInfoSent = true;
|
|
316
|
+
this.currentConnectionAttempts = 0;
|
|
317
|
+
// Resend the pagevisit or video message type before sending data so reset the prev type
|
|
318
|
+
this.previousMessageType = '';
|
|
319
|
+
// attempt to send the unconfirmed messages again
|
|
320
|
+
yield this._sendUnconfirmedMessages(false);
|
|
321
|
+
// setting up the socket retry mechanism
|
|
322
|
+
this.setupRetryMechanism();
|
|
323
|
+
// increasing the connection count everytime we connect
|
|
324
|
+
this.connectionCount += 1;
|
|
325
|
+
});
|
|
354
326
|
}
|
|
355
327
|
/** message handler for socket
|
|
356
328
|
* @param {} event
|
|
357
329
|
*/
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
// and we need to clear out the retry queue.
|
|
386
|
-
if (event.data.includes(SEQ_NUM_ATT_NAME)) {
|
|
387
|
-
const seqNumSplit = event.data.split(`${SEQ_NUM_ATT_NAME}:`);
|
|
388
|
-
if (seqNumSplit.length < 2) {
|
|
389
|
-
ClientConfig.getInstance().postNoibuErrorAndOptionallyDisableClient(`Invalid message received from metroplex while clearing retry queue ${event.data}`, false, SEVERITY.error);
|
|
390
|
-
break;
|
|
391
|
-
}
|
|
392
|
-
// the second element in the string split is the sequence number
|
|
393
|
-
const seqNum = parseInt(seqNumSplit[1], 10);
|
|
394
|
-
// ignore sequence num if this is just page info, it will always be -1 and we don't want
|
|
395
|
-
// to disable the retry loop on this
|
|
396
|
-
if (seqNum === -1) {
|
|
330
|
+
_onSocketMessage(event) {
|
|
331
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
332
|
+
switch (event.data) {
|
|
333
|
+
case STOP_STORING_VID_SOCKET_MESSAGE:
|
|
334
|
+
// stopping vids from being sent
|
|
335
|
+
this.metroplexTypeLock[VIDEO_METROPLEX_TYPE] = true;
|
|
336
|
+
StoredMetrics.getInstance().setDidCutVideo();
|
|
337
|
+
break;
|
|
338
|
+
case STOP_STORING_PV_SOCKET_MESSAGE:
|
|
339
|
+
// stopping pv from being sent
|
|
340
|
+
this.metroplexTypeLock[PV_METROPLEX_TYPE] = true;
|
|
341
|
+
StoredMetrics.getInstance().setDidCutPv();
|
|
342
|
+
break;
|
|
343
|
+
case BLOCK_SOCKET_MESSAGE:
|
|
344
|
+
// we are disabling collect for 1 day if we get the
|
|
345
|
+
// block message from metroplex. We are probably on it
|
|
346
|
+
yield ClientConfig.getInstance().lockClient(1440, 'Metroplex blocked script');
|
|
347
|
+
this.close();
|
|
348
|
+
break;
|
|
349
|
+
case CLOSE_CONNECTION_FORCEFULLY:
|
|
350
|
+
this.close();
|
|
351
|
+
break;
|
|
352
|
+
case OK_SOCKET_MESSAGE:
|
|
353
|
+
// we do nothing on an OK
|
|
354
|
+
break;
|
|
355
|
+
default:
|
|
356
|
+
if (!event.data) {
|
|
397
357
|
break;
|
|
398
358
|
}
|
|
399
|
-
// we
|
|
400
|
-
|
|
401
|
-
|
|
359
|
+
// we now need to check if we receive text that contains
|
|
360
|
+
// the text SEQ_NUM. if that is true, then its a seq num
|
|
361
|
+
// and we need to clear out the retry queue.
|
|
362
|
+
if (event.data.includes(SEQ_NUM_ATT_NAME)) {
|
|
363
|
+
const seqNumSplit = event.data.split(`${SEQ_NUM_ATT_NAME}:`);
|
|
364
|
+
if (seqNumSplit.length < 2) {
|
|
365
|
+
ClientConfig.getInstance().postNoibuErrorAndOptionallyDisableClient(`Invalid message received from metroplex while clearing retry queue ${event.data}`, false, SEVERITY.error);
|
|
366
|
+
break;
|
|
367
|
+
}
|
|
368
|
+
// the second element in the string split is the sequence number
|
|
369
|
+
const seqNum = parseInt(seqNumSplit[1], 10);
|
|
370
|
+
// ignore sequence num if this is just page info, it will always be -1 and we don't want
|
|
371
|
+
// to disable the retry loop on this
|
|
372
|
+
if (seqNum === -1) {
|
|
373
|
+
break;
|
|
374
|
+
}
|
|
375
|
+
// we disable the retry loop if we start running behind
|
|
376
|
+
if (seqNum <= this.latestReceivedSeqNumber) {
|
|
377
|
+
this.isRetryLoopDisabled = true;
|
|
378
|
+
}
|
|
379
|
+
else {
|
|
380
|
+
this.isRetryLoopDisabled = false;
|
|
381
|
+
// setting the latest received seqNum
|
|
382
|
+
this.latestReceivedSeqNumber = seqNum;
|
|
383
|
+
// clear the retry queue when for this seqNum and below
|
|
384
|
+
this._clearRetryQueue(seqNum);
|
|
385
|
+
}
|
|
386
|
+
if (!this.ackedOnce) {
|
|
387
|
+
this.ackedOnce = true;
|
|
388
|
+
}
|
|
402
389
|
}
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
// setting the latest received seqNum
|
|
406
|
-
this.latestReceivedSeqNumber = seqNum;
|
|
407
|
-
// clear the retry queue when for this seqNum and below
|
|
408
|
-
this._clearRetryQueue(seqNum);
|
|
409
|
-
}
|
|
410
|
-
if (!this.ackedOnce) {
|
|
411
|
-
this.ackedOnce = true;
|
|
390
|
+
if (this._tryProcessHelpCodeResponse(event.data)) {
|
|
391
|
+
break;
|
|
412
392
|
}
|
|
413
|
-
}
|
|
414
|
-
if (this._tryProcessHelpCodeResponse(event.data)) {
|
|
415
393
|
break;
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
}
|
|
394
|
+
// still need to collect the id sent back from metroplex
|
|
395
|
+
}
|
|
396
|
+
});
|
|
420
397
|
}
|
|
421
398
|
/**
|
|
422
399
|
* Returns true if the message's payload has the payload type given and has a sequence
|
|
@@ -433,41 +410,44 @@ class MetroplexSocket {
|
|
|
433
410
|
*/
|
|
434
411
|
_clearRetryQueue(seqNum) {
|
|
435
412
|
this.latestReceivedSeqNumStoredTime = new Date();
|
|
436
|
-
|
|
413
|
+
const newQueue = this.retryMessageQueue.filter(message => this._messagePayloadHasLargerSeqNum(message, PAGE_VISIT_PART_ATT_NAME, seqNum) ||
|
|
437
414
|
this._messagePayloadHasLargerSeqNum(message, PAGE_VISIT_VID_FRAG_ATT_NAME, seqNum));
|
|
415
|
+
this.retryMessageQueue = createSlidingArrayOfSize(MAX_RETRY_MSG_Q_SIZE, newQueue);
|
|
438
416
|
}
|
|
439
417
|
/** will resend everything that is in the retry queue */
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
}
|
|
447
|
-
// We always send if this is on socket open
|
|
448
|
-
if (socketWasAlreadyOpen) {
|
|
449
|
-
// we do not send the messages to metroplex if we are falling behind on received
|
|
450
|
-
// sequence numbers
|
|
451
|
-
const someTimeAgo = new Date();
|
|
452
|
-
someTimeAgo.setMilliseconds(someTimeAgo.getMilliseconds() - this.metroRetryFrequencyMS);
|
|
453
|
-
if (someTimeAgo < this.latestReceivedSeqNumStoredTime) {
|
|
418
|
+
_sendUnconfirmedMessages(socketWasAlreadyOpen) {
|
|
419
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
420
|
+
noibuLog('_sendUnconfirmedMessages');
|
|
421
|
+
// we only check once. If the socket is disconnected midway, the events would
|
|
422
|
+
// still be in the retry queue.
|
|
423
|
+
if (!this.isConnected() || ClientConfig.getInstance().isClientDisabled) {
|
|
454
424
|
return;
|
|
455
425
|
}
|
|
456
|
-
//
|
|
457
|
-
if (
|
|
458
|
-
|
|
426
|
+
// We always send if this is on socket open
|
|
427
|
+
if (socketWasAlreadyOpen) {
|
|
428
|
+
// we do not send the messages to metroplex if we are falling behind on received
|
|
429
|
+
// sequence numbers
|
|
430
|
+
const someTimeAgo = new Date();
|
|
431
|
+
someTimeAgo.setMilliseconds(someTimeAgo.getMilliseconds() - this.metroRetryFrequencyMS);
|
|
432
|
+
if (someTimeAgo < this.latestReceivedSeqNumStoredTime) {
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
// we do not resend messages to metroplex if we are running behind
|
|
436
|
+
if (this.isRetryLoopDisabled) {
|
|
437
|
+
return;
|
|
438
|
+
}
|
|
459
439
|
}
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
}
|
|
440
|
+
// removing all the messages with a blocked type from the retry queue
|
|
441
|
+
this.retryMessageQueue = this.retryMessageQueue.filter(message => !(message.type in this.metroplexTypeLock));
|
|
442
|
+
// we dont remove any items from this queue in this function
|
|
443
|
+
for (let i = 0; i < this.retryMessageQueue.length; i += 1) {
|
|
444
|
+
const { type, payload } = this.retryMessageQueue[i];
|
|
445
|
+
// sending the data to metroplex
|
|
446
|
+
// eslint-disable-next-line no-await-in-loop
|
|
447
|
+
yield this._sendSocketMessage(payload);
|
|
448
|
+
this.previousMessageType = type;
|
|
449
|
+
}
|
|
450
|
+
});
|
|
471
451
|
}
|
|
472
452
|
/** sets up the interval to empty the queue as we receive confirmation messages from metroplex */
|
|
473
453
|
setupRetryMechanism() {
|
|
@@ -485,143 +465,149 @@ class MetroplexSocket {
|
|
|
485
465
|
* will handle the final moments of a page being active. It
|
|
486
466
|
* will try to empty both the queues with beacons.
|
|
487
467
|
*/
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
468
|
+
_handleUnload() {
|
|
469
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
470
|
+
// closing the socket since the page is unloading
|
|
471
|
+
this.close();
|
|
472
|
+
// if we disable the client within the pagevisit, we do not
|
|
473
|
+
// post anything during the unloading.
|
|
474
|
+
if (ClientConfig.getInstance().isClientDisabled) {
|
|
475
|
+
return;
|
|
476
|
+
}
|
|
477
|
+
// Don't send the final complete PV if the session has become inactive
|
|
478
|
+
// we already sent the PV when went inactive
|
|
479
|
+
if (ClientConfig.getInstance().isInactive()) {
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
yield this.postFullPageVisit(MAX_BEACON_PAYLOAD_SIZE);
|
|
483
|
+
});
|
|
502
484
|
}
|
|
503
485
|
/**
|
|
504
486
|
* will post full page visit to metroplex. It
|
|
505
487
|
* will try to empty both the queues with beacons.
|
|
506
488
|
*/
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
}
|
|
511
|
-
// debug counters
|
|
512
|
-
const postInfo = [];
|
|
513
|
-
const numDropped = {
|
|
514
|
-
[VIDEO_METROPLEX_TYPE]: 0,
|
|
515
|
-
[PV_METROPLEX_TYPE]: 0,
|
|
516
|
-
};
|
|
517
|
-
let currentMsgPayloadSize = 0;
|
|
518
|
-
let currentCompletePv = {
|
|
519
|
-
[PAGE_VISIT_INFORMATION_ATT_NAME]: await this.getPageInformation(),
|
|
520
|
-
[PAGE_VISIT_PART_ATT_NAME]: [],
|
|
521
|
-
[PAGE_VISIT_VID_FRAG_ATT_NAME]: [],
|
|
522
|
-
[PAGE_VISIT_HTTP_DATA_ATT_NAME]: [],
|
|
523
|
-
[VIDEO_PART_COUNT_ATT_NAME]: this.connectionCount,
|
|
524
|
-
};
|
|
525
|
-
currentCompletePv[PAGE_VISIT_INFORMATION_ATT_NAME][IS_LAST_ATT_NAME] = true;
|
|
526
|
-
const pageInfo = await this.getPageInformation();
|
|
527
|
-
this.retryMessageQueue.forEach(msg => {
|
|
528
|
-
// can't use two variables types in obj deconstruction
|
|
529
|
-
// eslint-disable-next-line prefer-const
|
|
530
|
-
let { type, payload } = msg;
|
|
531
|
-
// If the message is larger than the beacon limit then skip to the next message
|
|
532
|
-
const currentPayloadSize = new Blob([stringifyJSON(payload)]).size;
|
|
533
|
-
if (currentPayloadSize > maxMessageSize) {
|
|
534
|
-
// increment dropped count for this type
|
|
535
|
-
numDropped[type] += 1;
|
|
489
|
+
postFullPageVisit(maxMessageSize) {
|
|
490
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
491
|
+
if (this.retryMessageQueue.length === 0) {
|
|
536
492
|
return;
|
|
537
493
|
}
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
494
|
+
// debug counters
|
|
495
|
+
const postInfo = [];
|
|
496
|
+
const numDropped = {
|
|
497
|
+
[VIDEO_METROPLEX_TYPE]: 0,
|
|
498
|
+
[PV_METROPLEX_TYPE]: 0,
|
|
499
|
+
};
|
|
500
|
+
let currentMsgPayloadSize = 0;
|
|
501
|
+
let currentCompletePv = {
|
|
502
|
+
[PAGE_VISIT_INFORMATION_ATT_NAME]: yield this.getPageInformation(),
|
|
503
|
+
[PAGE_VISIT_PART_ATT_NAME]: [],
|
|
504
|
+
[PAGE_VISIT_VID_FRAG_ATT_NAME]: [],
|
|
505
|
+
[PAGE_VISIT_HTTP_DATA_ATT_NAME]: [],
|
|
506
|
+
[VIDEO_PART_COUNT_ATT_NAME]: this.connectionCount,
|
|
507
|
+
};
|
|
508
|
+
currentCompletePv[PAGE_VISIT_INFORMATION_ATT_NAME][IS_LAST_ATT_NAME] = true;
|
|
509
|
+
const pageInfo = yield this.getPageInformation();
|
|
510
|
+
this.retryMessageQueue.forEach(msg => {
|
|
511
|
+
// can't use two variables types in obj deconstruction
|
|
512
|
+
// eslint-disable-next-line prefer-const
|
|
513
|
+
let { type, payload } = msg;
|
|
514
|
+
// If the message is larger than the beacon limit then skip to the next message
|
|
515
|
+
const currentPayloadSize = new Blob([stringifyJSON(payload)]).size;
|
|
516
|
+
if (currentPayloadSize > maxMessageSize) {
|
|
517
|
+
// increment dropped count for this type
|
|
518
|
+
numDropped[type] += 1;
|
|
519
|
+
return;
|
|
520
|
+
}
|
|
521
|
+
currentMsgPayloadSize += currentPayloadSize;
|
|
522
|
+
if (currentMsgPayloadSize >= maxMessageSize) {
|
|
523
|
+
this.postMessage(currentCompletePv);
|
|
524
|
+
// add to post info
|
|
525
|
+
let postInfoMessage = `Vid: ${currentCompletePv[PAGE_VISIT_VID_FRAG_ATT_NAME].length}`;
|
|
526
|
+
postInfoMessage += ` PV: ${currentCompletePv[PAGE_VISIT_PART_ATT_NAME].length}`;
|
|
527
|
+
postInfoMessage += ` HTTP: ${currentCompletePv[PAGE_VISIT_HTTP_DATA_ATT_NAME].length},`;
|
|
528
|
+
postInfo.push(postInfoMessage);
|
|
529
|
+
// resetting currentCompletePv, since we need to keep adding
|
|
530
|
+
// events to a blank object to send to metroplex.
|
|
531
|
+
// retain the video part count, so we don't overwrite anything
|
|
532
|
+
currentCompletePv = {
|
|
533
|
+
[PAGE_VISIT_INFORMATION_ATT_NAME]: pageInfo,
|
|
534
|
+
[PAGE_VISIT_PART_ATT_NAME]: [],
|
|
535
|
+
[PAGE_VISIT_VID_FRAG_ATT_NAME]: [],
|
|
536
|
+
[PAGE_VISIT_HTTP_DATA_ATT_NAME]: [],
|
|
537
|
+
[VIDEO_PART_COUNT_ATT_NAME]: currentCompletePv[VIDEO_PART_COUNT_ATT_NAME],
|
|
538
|
+
};
|
|
539
|
+
currentCompletePv[PAGE_VISIT_INFORMATION_ATT_NAME][IS_LAST_ATT_NAME] =
|
|
540
|
+
true;
|
|
541
|
+
// resetting the message size based on what is being added
|
|
542
|
+
currentMsgPayloadSize = currentPayloadSize;
|
|
543
|
+
}
|
|
544
|
+
switch (type) {
|
|
545
|
+
case VIDEO_METROPLEX_TYPE:
|
|
546
|
+
currentCompletePv[PAGE_VISIT_VID_FRAG_ATT_NAME].push(payload[PAGE_VISIT_VID_FRAG_ATT_NAME]);
|
|
547
|
+
break;
|
|
548
|
+
case PV_METROPLEX_TYPE:
|
|
549
|
+
currentCompletePv[PAGE_VISIT_PART_ATT_NAME].push(payload[PAGE_VISIT_PART_ATT_NAME]);
|
|
550
|
+
break;
|
|
551
|
+
case HTTP_DATA_METROPLEX_TYPE:
|
|
552
|
+
currentCompletePv[PAGE_VISIT_HTTP_DATA_ATT_NAME].push(payload[PAGE_VISIT_HTTP_DATA_ATT_NAME]);
|
|
553
|
+
break;
|
|
554
|
+
}
|
|
555
|
+
});
|
|
556
|
+
this.postMessage(currentCompletePv);
|
|
557
|
+
// debug log if large retry message queue
|
|
558
|
+
if (this.retryMessageQueue.length > 100) {
|
|
542
559
|
let postInfoMessage = `Vid: ${currentCompletePv[PAGE_VISIT_VID_FRAG_ATT_NAME].length}`;
|
|
543
560
|
postInfoMessage += ` PV: ${currentCompletePv[PAGE_VISIT_PART_ATT_NAME].length}`;
|
|
544
561
|
postInfoMessage += ` HTTP: ${currentCompletePv[PAGE_VISIT_HTTP_DATA_ATT_NAME].length},`;
|
|
545
562
|
postInfo.push(postInfoMessage);
|
|
546
|
-
//
|
|
547
|
-
//
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
563
|
+
// we completed posted the full pv, send the confirmation debug logs
|
|
564
|
+
// build the debug message
|
|
565
|
+
let message = 'POST Full PV complete';
|
|
566
|
+
// POST counts
|
|
567
|
+
message += `, POSTs count: ${postInfo.length}`;
|
|
568
|
+
message += `, POSTs info: ${stringifyJSON(postInfo)}`;
|
|
569
|
+
// Initial retry message queue size
|
|
570
|
+
message += `, Retry message queue size: ${this.retryMessageQueue.length}`;
|
|
571
|
+
// Num drops
|
|
572
|
+
if (numDropped[VIDEO_METROPLEX_TYPE] > 0) {
|
|
573
|
+
message += `, Video parts dropped: ${numDropped[VIDEO_METROPLEX_TYPE]}`;
|
|
574
|
+
}
|
|
575
|
+
if (numDropped[PV_METROPLEX_TYPE] > 0) {
|
|
576
|
+
message += `, Page visit parts dropped: ${numDropped[PV_METROPLEX_TYPE]}`;
|
|
577
|
+
}
|
|
578
|
+
if (numDropped[HTTP_DATA_METROPLEX_TYPE] > 0) {
|
|
579
|
+
message += `, HTTP data parts dropped: ${numDropped[HTTP_DATA_METROPLEX_TYPE]}`;
|
|
580
|
+
}
|
|
581
|
+
// Sequence info
|
|
582
|
+
message += `, Sequence Info: Latest ${this.messageSequenceNum}`;
|
|
583
|
+
message += ` Ack'd ${this.latestReceivedSeqNumStoredTime} ${this.latestReceivedSeqNumber}`;
|
|
584
|
+
// if client was disabled (due to inactive or otherwise) enable briefly so the
|
|
585
|
+
// debug message gets through
|
|
586
|
+
const clientDisabled = ClientConfig.getInstance().isClientDisabled;
|
|
587
|
+
ClientConfig.getInstance().isClientDisabled = false;
|
|
588
|
+
ClientConfig.getInstance().postNoibuErrorAndOptionallyDisableClient(message, clientDisabled, SEVERITY.warn);
|
|
571
589
|
}
|
|
572
590
|
});
|
|
573
|
-
this.postMessage(currentCompletePv);
|
|
574
|
-
// debug log if large retry message queue
|
|
575
|
-
if (this.retryMessageQueue.length > 100) {
|
|
576
|
-
let postInfoMessage = `Vid: ${currentCompletePv[PAGE_VISIT_VID_FRAG_ATT_NAME].length}`;
|
|
577
|
-
postInfoMessage += ` PV: ${currentCompletePv[PAGE_VISIT_PART_ATT_NAME].length}`;
|
|
578
|
-
postInfoMessage += ` HTTP: ${currentCompletePv[PAGE_VISIT_HTTP_DATA_ATT_NAME].length},`;
|
|
579
|
-
postInfo.push(postInfoMessage);
|
|
580
|
-
// we completed posted the full pv, send the confirmation debug logs
|
|
581
|
-
// build the debug message
|
|
582
|
-
let message = 'POST Full PV complete';
|
|
583
|
-
// POST counts
|
|
584
|
-
message += `, POSTs count: ${postInfo.length}`;
|
|
585
|
-
message += `, POSTs info: ${stringifyJSON(postInfo)}`;
|
|
586
|
-
// Initial retry message queue size
|
|
587
|
-
message += `, Retry message queue size: ${this.retryMessageQueue.length}`;
|
|
588
|
-
// Num drops
|
|
589
|
-
if (numDropped[VIDEO_METROPLEX_TYPE] > 0) {
|
|
590
|
-
message += `, Video parts dropped: ${numDropped[VIDEO_METROPLEX_TYPE]}`;
|
|
591
|
-
}
|
|
592
|
-
if (numDropped[PV_METROPLEX_TYPE] > 0) {
|
|
593
|
-
message += `, Page visit parts dropped: ${numDropped[PV_METROPLEX_TYPE]}`;
|
|
594
|
-
}
|
|
595
|
-
if (numDropped[HTTP_DATA_METROPLEX_TYPE] > 0) {
|
|
596
|
-
message += `, HTTP data parts dropped: ${numDropped[HTTP_DATA_METROPLEX_TYPE]}`;
|
|
597
|
-
}
|
|
598
|
-
// Sequence info
|
|
599
|
-
message += `, Sequence Info: Latest ${this.messageSequenceNum}`;
|
|
600
|
-
message += ` Ack'd ${this.latestReceivedSeqNumStoredTime} ${this.latestReceivedSeqNumber}`;
|
|
601
|
-
// if client was disabled (due to inactive or otherwise) enable briefly so the
|
|
602
|
-
// debug message gets through
|
|
603
|
-
const clientDisabled = ClientConfig.getInstance().isClientDisabled;
|
|
604
|
-
ClientConfig.getInstance().isClientDisabled = false;
|
|
605
|
-
ClientConfig.getInstance().postNoibuErrorAndOptionallyDisableClient(message, clientDisabled, SEVERITY.warn);
|
|
606
|
-
}
|
|
607
591
|
}
|
|
608
592
|
/**
|
|
609
593
|
* will send a message to metroplex via a post request that will outlive the current page
|
|
610
594
|
*/
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
595
|
+
postMessage(msg) {
|
|
596
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
597
|
+
const updatedMsg = msg;
|
|
598
|
+
// ensure a unique video part number each call
|
|
599
|
+
updatedMsg[VIDEO_PART_COUNT_ATT_NAME] += 1;
|
|
600
|
+
// we send the remainder elements
|
|
601
|
+
unwrapNoibuWrapped(fetch)(this.postURL, {
|
|
602
|
+
method: 'POST',
|
|
603
|
+
headers: {
|
|
604
|
+
'content-type': 'application/json',
|
|
605
|
+
'User-Agent': yield getUserAgent(),
|
|
606
|
+
},
|
|
607
|
+
body: stringifyJSON(updatedMsg),
|
|
608
|
+
// keep alive outlives the current page, its the same as beacon
|
|
609
|
+
keepalive: true,
|
|
610
|
+
});
|
|
625
611
|
});
|
|
626
612
|
}
|
|
627
613
|
/**
|
|
@@ -629,64 +615,69 @@ class MetroplexSocket {
|
|
|
629
615
|
* is active and returns true. If inactive the session and socket are closed
|
|
630
616
|
* and this method returns false.
|
|
631
617
|
*/
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
618
|
+
_sendSocketMessage(payload) {
|
|
619
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
620
|
+
noibuLog('_sendSocketMessage');
|
|
621
|
+
const closeIfInactive = yield this.closeIfInactive();
|
|
622
|
+
if (closeIfInactive) {
|
|
623
|
+
noibuLog('_sendSocketMessage dropped due to', { closeIfInactive });
|
|
624
|
+
return;
|
|
625
|
+
}
|
|
626
|
+
const payloadJson = stringifyJSON(payload);
|
|
627
|
+
noibuLog(`_sendSocketMessage sending: ${PAGE_VISIT_VID_FRAG_ATT_NAME in payload
|
|
628
|
+
? PAGE_VISIT_VID_FRAG_ATT_NAME
|
|
629
|
+
: payloadJson}`);
|
|
630
|
+
if (this.socket) {
|
|
631
|
+
this.socket.send(payloadJson);
|
|
632
|
+
}
|
|
633
|
+
});
|
|
644
634
|
}
|
|
645
635
|
/**
|
|
646
636
|
* Closes the socket connection if the session is inactive. Returns true if the
|
|
647
637
|
* session is inactive
|
|
648
638
|
*/
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
639
|
+
closeIfInactive() {
|
|
640
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
641
|
+
const inactive = ClientConfig.getInstance().isInactive();
|
|
642
|
+
// if we weren't already inactive, go to inactive state
|
|
643
|
+
if (inactive && !ClientConfig.getInstance().isClientDisabled) {
|
|
644
|
+
// lock the client for 1 minute, since the lock expires
|
|
645
|
+
// only on new page loads this will lock the client until
|
|
646
|
+
// the next page is loaded.
|
|
647
|
+
yield ClientConfig.getInstance().lockClientUntilNextPage('Session is inactive, locking until next page');
|
|
648
|
+
this.close();
|
|
659
649
|
// post metrics now so we don't include events after going inactive
|
|
660
|
-
StoredMetrics.getInstance().postMetrics()
|
|
650
|
+
StoredMetrics.getInstance().postMetrics();
|
|
661
651
|
// post retry queue now so we don't include event after going inactive
|
|
662
|
-
this.postFullPageVisit(MAX_BEACON_PAYLOAD_SIZE)
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
652
|
+
this.postFullPageVisit(MAX_BEACON_PAYLOAD_SIZE);
|
|
653
|
+
}
|
|
654
|
+
return inactive;
|
|
655
|
+
});
|
|
666
656
|
}
|
|
667
657
|
/** will get page information, calling this will increase the connection count */
|
|
668
|
-
|
|
669
|
-
return {
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
658
|
+
getPageInformation() {
|
|
659
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
660
|
+
return {
|
|
661
|
+
[BROWSER_ID_ATT_NAME]: ClientConfig.getInstance().browserId,
|
|
662
|
+
[PV_ID_ATT_NAME]: ClientConfig.getInstance().pageVisitId,
|
|
663
|
+
[VER_ATT_NAME]: CURRENT_PV_VERSION,
|
|
664
|
+
[PV_SEQ_ATT_NAME]: yield ClientConfig.getInstance().getPageVisitSeq(),
|
|
665
|
+
[ON_URL_ATT_NAME]: this.initialURL,
|
|
666
|
+
[REF_URL_ATT_NAME]: this.initialReferringURL,
|
|
667
|
+
[STARTED_AT_ATT_NAME]: this.sessionTimestamp.toISOString(),
|
|
668
|
+
[CONN_COUNT_ATT_NAME]: this.connectionCount,
|
|
669
|
+
[COLLECT_VER_ATT_NAME]: CURRENT_NOIBUJS_VERSION,
|
|
670
|
+
[IS_LAST_ATT_NAME]: false,
|
|
671
|
+
[SCRIPT_ID_ATT_NAME]: GET_SCRIPT_ID(),
|
|
672
|
+
[SCRIPT_INSTANCE_ID_ATT_NAME]: this.scriptInstanceId,
|
|
673
|
+
[METROPLEX_SOCKET_INSTANCE_ID_ATT_NAME]: this.instanceId,
|
|
674
|
+
[SOCKET_INSTANCE_ID_ATT_NAME]: this.socketInstanceId,
|
|
675
|
+
[VIDEO_RECORDER_ATT_NAME]: yield getVideoRecorderType(),
|
|
676
|
+
};
|
|
677
|
+
});
|
|
686
678
|
}
|
|
687
679
|
/**
|
|
688
680
|
* Try to parse help code response and fire custom event
|
|
689
|
-
* @param {String} response
|
|
690
681
|
*/
|
|
691
682
|
_tryProcessHelpCodeResponse(response) {
|
|
692
683
|
const prefix = `${HELP_CODE_ATT_NAME}:`;
|
|
@@ -701,5 +692,11 @@ class MetroplexSocket {
|
|
|
701
692
|
return true;
|
|
702
693
|
}
|
|
703
694
|
}
|
|
695
|
+
MetroplexSocket.typeToPayloadPropMap = {
|
|
696
|
+
[HTTP_DATA_METROPLEX_TYPE]: PAGE_VISIT_HTTP_DATA_ATT_NAME,
|
|
697
|
+
[META_DATA_METROPLEX_TYPE]: PAGE_VISIT_META_DATA_ATT_NAME,
|
|
698
|
+
[PV_METROPLEX_TYPE]: PAGE_VISIT_PART_ATT_NAME,
|
|
699
|
+
[VIDEO_METROPLEX_TYPE]: PAGE_VISIT_VID_FRAG_ATT_NAME,
|
|
700
|
+
};
|
|
704
701
|
|
|
705
|
-
export { MetroplexSocket as default };
|
|
702
|
+
export { createSlidingArrayOfSize, MetroplexSocket as default };
|