@webex/calling 0.0.1-next.0

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.
Files changed (179) hide show
  1. package/README.md +67 -0
  2. package/dist/module/CallHistory/CallHistory.js +84 -0
  3. package/dist/module/CallHistory/callHistoryFixtures.js +307 -0
  4. package/dist/module/CallHistory/constants.js +9 -0
  5. package/dist/module/CallHistory/types.js +1 -0
  6. package/dist/module/CallSettings/CallSettings.js +65 -0
  7. package/dist/module/CallSettings/UcmBackendConnector.js +100 -0
  8. package/dist/module/CallSettings/WxCallBackendConnector.js +287 -0
  9. package/dist/module/CallSettings/constants.js +11 -0
  10. package/dist/module/CallSettings/testFixtures.js +62 -0
  11. package/dist/module/CallSettings/types.js +1 -0
  12. package/dist/module/CallingClient/CallingClient.js +268 -0
  13. package/dist/module/CallingClient/callRecordFixtures.js +93 -0
  14. package/dist/module/CallingClient/calling/CallerId/index.js +169 -0
  15. package/dist/module/CallingClient/calling/CallerId/types.js +1 -0
  16. package/dist/module/CallingClient/calling/call.js +1649 -0
  17. package/dist/module/CallingClient/calling/callManager.js +274 -0
  18. package/dist/module/CallingClient/calling/index.js +2 -0
  19. package/dist/module/CallingClient/calling/types.js +53 -0
  20. package/dist/module/CallingClient/callingClientFixtures.js +38 -0
  21. package/dist/module/CallingClient/constants.js +122 -0
  22. package/dist/module/CallingClient/line/index.js +110 -0
  23. package/dist/module/CallingClient/line/types.js +14 -0
  24. package/dist/module/CallingClient/registration/index.js +1 -0
  25. package/dist/module/CallingClient/registration/register.js +507 -0
  26. package/dist/module/CallingClient/registration/registerFixtures.js +28 -0
  27. package/dist/module/CallingClient/registration/types.js +1 -0
  28. package/dist/module/CallingClient/types.js +1 -0
  29. package/dist/module/Contacts/ContactsClient.js +487 -0
  30. package/dist/module/Contacts/constants.js +20 -0
  31. package/dist/module/Contacts/contactFixtures.js +284 -0
  32. package/dist/module/Contacts/types.js +10 -0
  33. package/dist/module/Errors/catalog/CallError.js +26 -0
  34. package/dist/module/Errors/catalog/CallingDeviceError.js +18 -0
  35. package/dist/module/Errors/catalog/ExtendedError.js +10 -0
  36. package/dist/module/Errors/catalog/LineError.js +24 -0
  37. package/dist/module/Errors/index.js +2 -0
  38. package/dist/module/Errors/types.js +48 -0
  39. package/dist/module/Events/impl/index.js +19 -0
  40. package/dist/module/Events/types.js +74 -0
  41. package/dist/module/Logger/index.js +114 -0
  42. package/dist/module/Logger/types.js +25 -0
  43. package/dist/module/Metrics/index.js +232 -0
  44. package/dist/module/Metrics/types.js +37 -0
  45. package/dist/module/SDKConnector/index.js +39 -0
  46. package/dist/module/SDKConnector/types.js +1 -0
  47. package/dist/module/SDKConnector/utils.js +12 -0
  48. package/dist/module/Voicemail/BroadworksBackendConnector.js +289 -0
  49. package/dist/module/Voicemail/UcmBackendConnector.js +275 -0
  50. package/dist/module/Voicemail/Voicemail.js +110 -0
  51. package/dist/module/Voicemail/WxCallBackendConnector.js +279 -0
  52. package/dist/module/Voicemail/constants.js +29 -0
  53. package/dist/module/Voicemail/types.js +1 -0
  54. package/dist/module/Voicemail/voicemailFixture.js +449 -0
  55. package/dist/module/common/Utils.js +802 -0
  56. package/dist/module/common/constants.js +40 -0
  57. package/dist/module/common/index.js +1 -0
  58. package/dist/module/common/testUtil.js +938 -0
  59. package/dist/module/common/types.js +57 -0
  60. package/dist/module/index.js +8 -0
  61. package/dist/types/CallHistory/CallHistory.d.ts +19 -0
  62. package/dist/types/CallHistory/CallHistory.d.ts.map +1 -0
  63. package/dist/types/CallHistory/callHistoryFixtures.d.ts +95 -0
  64. package/dist/types/CallHistory/callHistoryFixtures.d.ts.map +1 -0
  65. package/dist/types/CallHistory/constants.d.ts +10 -0
  66. package/dist/types/CallHistory/constants.d.ts.map +1 -0
  67. package/dist/types/CallHistory/types.d.ts +21 -0
  68. package/dist/types/CallHistory/types.d.ts.map +1 -0
  69. package/dist/types/CallSettings/CallSettings.d.ts +20 -0
  70. package/dist/types/CallSettings/CallSettings.d.ts.map +1 -0
  71. package/dist/types/CallSettings/UcmBackendConnector.d.ts +20 -0
  72. package/dist/types/CallSettings/UcmBackendConnector.d.ts.map +1 -0
  73. package/dist/types/CallSettings/WxCallBackendConnector.d.ts +22 -0
  74. package/dist/types/CallSettings/WxCallBackendConnector.d.ts.map +1 -0
  75. package/dist/types/CallSettings/constants.d.ts +12 -0
  76. package/dist/types/CallSettings/constants.d.ts.map +1 -0
  77. package/dist/types/CallSettings/testFixtures.d.ts +16 -0
  78. package/dist/types/CallSettings/testFixtures.d.ts.map +1 -0
  79. package/dist/types/CallSettings/types.d.ts +108 -0
  80. package/dist/types/CallSettings/types.d.ts.map +1 -0
  81. package/dist/types/CallingClient/CallingClient.d.ts +38 -0
  82. package/dist/types/CallingClient/CallingClient.d.ts.map +1 -0
  83. package/dist/types/CallingClient/callRecordFixtures.d.ts +4 -0
  84. package/dist/types/CallingClient/callRecordFixtures.d.ts.map +1 -0
  85. package/dist/types/CallingClient/calling/CallerId/index.d.ts +18 -0
  86. package/dist/types/CallingClient/calling/CallerId/index.d.ts.map +1 -0
  87. package/dist/types/CallingClient/calling/CallerId/types.d.ts +42 -0
  88. package/dist/types/CallingClient/calling/CallerId/types.d.ts.map +1 -0
  89. package/dist/types/CallingClient/calling/call.d.ts +95 -0
  90. package/dist/types/CallingClient/calling/call.d.ts.map +1 -0
  91. package/dist/types/CallingClient/calling/callManager.d.ts +22 -0
  92. package/dist/types/CallingClient/calling/callManager.d.ts.map +1 -0
  93. package/dist/types/CallingClient/calling/index.d.ts +3 -0
  94. package/dist/types/CallingClient/calling/index.d.ts.map +1 -0
  95. package/dist/types/CallingClient/calling/types.d.ts +204 -0
  96. package/dist/types/CallingClient/calling/types.d.ts.map +1 -0
  97. package/dist/types/CallingClient/callingClientFixtures.d.ts +19 -0
  98. package/dist/types/CallingClient/callingClientFixtures.d.ts.map +1 -0
  99. package/dist/types/CallingClient/constants.d.ts +123 -0
  100. package/dist/types/CallingClient/constants.d.ts.map +1 -0
  101. package/dist/types/CallingClient/line/index.d.ts +39 -0
  102. package/dist/types/CallingClient/line/index.d.ts.map +1 -0
  103. package/dist/types/CallingClient/line/types.d.ts +51 -0
  104. package/dist/types/CallingClient/line/types.d.ts.map +1 -0
  105. package/dist/types/CallingClient/registration/index.d.ts +2 -0
  106. package/dist/types/CallingClient/registration/index.d.ts.map +1 -0
  107. package/dist/types/CallingClient/registration/register.d.ts +65 -0
  108. package/dist/types/CallingClient/registration/register.d.ts.map +1 -0
  109. package/dist/types/CallingClient/registration/registerFixtures.d.ts +29 -0
  110. package/dist/types/CallingClient/registration/registerFixtures.d.ts.map +1 -0
  111. package/dist/types/CallingClient/registration/types.d.ts +21 -0
  112. package/dist/types/CallingClient/registration/types.d.ts.map +1 -0
  113. package/dist/types/CallingClient/types.d.ts +30 -0
  114. package/dist/types/CallingClient/types.d.ts.map +1 -0
  115. package/dist/types/Contacts/ContactsClient.d.ts +28 -0
  116. package/dist/types/Contacts/ContactsClient.d.ts.map +1 -0
  117. package/dist/types/Contacts/constants.d.ts +20 -0
  118. package/dist/types/Contacts/constants.d.ts.map +1 -0
  119. package/dist/types/Contacts/contactFixtures.d.ts +281 -0
  120. package/dist/types/Contacts/contactFixtures.d.ts.map +1 -0
  121. package/dist/types/Contacts/types.d.ts +75 -0
  122. package/dist/types/Contacts/types.d.ts.map +1 -0
  123. package/dist/types/Errors/catalog/CallError.d.ts +12 -0
  124. package/dist/types/Errors/catalog/CallError.d.ts.map +1 -0
  125. package/dist/types/Errors/catalog/CallingDeviceError.d.ts +11 -0
  126. package/dist/types/Errors/catalog/CallingDeviceError.d.ts.map +1 -0
  127. package/dist/types/Errors/catalog/ExtendedError.d.ts +7 -0
  128. package/dist/types/Errors/catalog/ExtendedError.d.ts.map +1 -0
  129. package/dist/types/Errors/catalog/LineError.d.ts +11 -0
  130. package/dist/types/Errors/catalog/LineError.d.ts.map +1 -0
  131. package/dist/types/Errors/index.d.ts +3 -0
  132. package/dist/types/Errors/index.d.ts.map +1 -0
  133. package/dist/types/Errors/types.d.ts +61 -0
  134. package/dist/types/Errors/types.d.ts.map +1 -0
  135. package/dist/types/Events/impl/index.d.ts +9 -0
  136. package/dist/types/Events/impl/index.d.ts.map +1 -0
  137. package/dist/types/Events/types.d.ts +284 -0
  138. package/dist/types/Events/types.d.ts.map +1 -0
  139. package/dist/types/Logger/index.d.ts +13 -0
  140. package/dist/types/Logger/index.d.ts.map +1 -0
  141. package/dist/types/Logger/types.d.ts +26 -0
  142. package/dist/types/Logger/types.d.ts.map +1 -0
  143. package/dist/types/Metrics/index.d.ts +6 -0
  144. package/dist/types/Metrics/index.d.ts.map +1 -0
  145. package/dist/types/Metrics/types.d.ts +43 -0
  146. package/dist/types/Metrics/types.d.ts.map +1 -0
  147. package/dist/types/SDKConnector/index.d.ts +13 -0
  148. package/dist/types/SDKConnector/index.d.ts.map +1 -0
  149. package/dist/types/SDKConnector/types.d.ts +129 -0
  150. package/dist/types/SDKConnector/types.d.ts.map +1 -0
  151. package/dist/types/SDKConnector/utils.d.ts +6 -0
  152. package/dist/types/SDKConnector/utils.d.ts.map +1 -0
  153. package/dist/types/Voicemail/BroadworksBackendConnector.d.ts +28 -0
  154. package/dist/types/Voicemail/BroadworksBackendConnector.d.ts.map +1 -0
  155. package/dist/types/Voicemail/UcmBackendConnector.d.ts +35 -0
  156. package/dist/types/Voicemail/UcmBackendConnector.d.ts.map +1 -0
  157. package/dist/types/Voicemail/Voicemail.d.ts +28 -0
  158. package/dist/types/Voicemail/Voicemail.d.ts.map +1 -0
  159. package/dist/types/Voicemail/WxCallBackendConnector.d.ts +24 -0
  160. package/dist/types/Voicemail/WxCallBackendConnector.d.ts.map +1 -0
  161. package/dist/types/Voicemail/constants.d.ts +30 -0
  162. package/dist/types/Voicemail/constants.d.ts.map +1 -0
  163. package/dist/types/Voicemail/types.d.ts +134 -0
  164. package/dist/types/Voicemail/types.d.ts.map +1 -0
  165. package/dist/types/Voicemail/voicemailFixture.d.ts +350 -0
  166. package/dist/types/Voicemail/voicemailFixture.d.ts.map +1 -0
  167. package/dist/types/common/Utils.d.ts +35 -0
  168. package/dist/types/common/Utils.d.ts.map +1 -0
  169. package/dist/types/common/constants.d.ts +41 -0
  170. package/dist/types/common/constants.d.ts.map +1 -0
  171. package/dist/types/common/index.d.ts +2 -0
  172. package/dist/types/common/index.d.ts.map +1 -0
  173. package/dist/types/common/testUtil.d.ts +3612 -0
  174. package/dist/types/common/testUtil.d.ts.map +1 -0
  175. package/dist/types/common/types.d.ts +192 -0
  176. package/dist/types/common/types.d.ts.map +1 -0
  177. package/dist/types/index.d.ts +9 -0
  178. package/dist/types/index.d.ts.map +1 -0
  179. package/package.json +161 -0
@@ -0,0 +1,507 @@
1
+ import { v4 as uuid } from 'uuid';
2
+ import { ERROR_CODE } from '../../Errors/types';
3
+ import { emitFinalFailure, handleRegistrationErrors } from '../../common';
4
+ import { METRIC_EVENT, METRIC_TYPE, REG_ACTION } from '../../Metrics/types';
5
+ import { getMetricManager } from '../../Metrics';
6
+ import { getCallManager } from '../calling';
7
+ import log from '../../Logger';
8
+ import SDKConnector from '../../SDKConnector';
9
+ import { ALLOWED_SERVICES, HTTP_METHODS, MobiusStatus, } from '../../common/types';
10
+ import { CALLING_USER_AGENT, CISCO_DEVICE_URL, DEVICES_ENDPOINT_RESOURCE, SPARK_USER_AGENT, WEBEX_WEB_CLIENT, BASE_REG_RETRY_TIMER_VAL_IN_SEC, BASE_REG_TIMER_MFACTOR, SEC_TO_MSEC_MFACTOR, REG_RANDOM_T_FACTOR_UPPER_LIMIT, REG_TRY_BACKUP_TIMER_VAL_IN_SEC, MINUTES_TO_SEC_MFACTOR, FAILBACK_429_RETRY_UTIL, REG_FAILBACK_429_MAX_RETRIES, FAILBACK_UTIL, REGISTRATION_FILE, DEFAULT_REHOMING_INTERVAL_MIN, DEFAULT_REHOMING_INTERVAL_MAX, DEFAULT_KEEPALIVE_INTERVAL, } from '../constants';
11
+ import { LINE_EVENTS } from '../line/types';
12
+ export class Registration {
13
+ sdkConnector;
14
+ webex;
15
+ userId = '';
16
+ serviceData;
17
+ failback429RetryAttempts;
18
+ registrationStatus;
19
+ failbackTimer;
20
+ activeMobiusUrl;
21
+ keepaliveTimer;
22
+ rehomingIntervalMin;
23
+ rehomingIntervalMax;
24
+ mutex;
25
+ metricManager;
26
+ lineEmitter;
27
+ callManager;
28
+ deviceInfo = {};
29
+ primaryMobiusUris;
30
+ backupMobiusUris;
31
+ registerRetry = false;
32
+ reconnectPending = false;
33
+ constructor(webex, serviceData, mutex, lineEmitter, logLevel) {
34
+ this.sdkConnector = SDKConnector;
35
+ this.serviceData = serviceData;
36
+ if (!this.sdkConnector.getWebex()) {
37
+ SDKConnector.setWebex(webex);
38
+ }
39
+ this.webex = this.sdkConnector.getWebex();
40
+ this.userId = this.webex.internal.device.userId;
41
+ this.registrationStatus = MobiusStatus.DEFAULT;
42
+ this.failback429RetryAttempts = 0;
43
+ log.setLogger(logLevel, REGISTRATION_FILE);
44
+ this.rehomingIntervalMin = DEFAULT_REHOMING_INTERVAL_MIN;
45
+ this.rehomingIntervalMax = DEFAULT_REHOMING_INTERVAL_MAX;
46
+ this.mutex = mutex;
47
+ this.callManager = getCallManager(this.webex, serviceData.indicator);
48
+ this.metricManager = getMetricManager(this.webex, serviceData.indicator);
49
+ this.lineEmitter = lineEmitter;
50
+ this.primaryMobiusUris = [];
51
+ this.backupMobiusUris = [];
52
+ }
53
+ getActiveMobiusUrl() {
54
+ return this.activeMobiusUrl;
55
+ }
56
+ setActiveMobiusUrl(url) {
57
+ log.info(`ActiveMobiusUrl: ${url}`, { method: 'setActiveMobiusUrl', file: REGISTRATION_FILE });
58
+ this.activeMobiusUrl = url;
59
+ this.callManager.updateActiveMobius(url);
60
+ }
61
+ setMobiusServers(primaryMobiusUris, backupMobiusUris) {
62
+ this.primaryMobiusUris = primaryMobiusUris;
63
+ this.backupMobiusUris = backupMobiusUris;
64
+ }
65
+ async postKeepAlive(url) {
66
+ return this.webex.request({
67
+ uri: `${url}/status`,
68
+ method: HTTP_METHODS.POST,
69
+ headers: {
70
+ [CISCO_DEVICE_URL]: this.webex.internal.device.url,
71
+ [SPARK_USER_AGENT]: CALLING_USER_AGENT,
72
+ },
73
+ service: ALLOWED_SERVICES.MOBIUS,
74
+ });
75
+ }
76
+ async deleteRegistration(url, deviceId, deviceUrl) {
77
+ const response = await fetch(`${url}${DEVICES_ENDPOINT_RESOURCE}/${deviceId}`, {
78
+ method: HTTP_METHODS.DELETE,
79
+ headers: {
80
+ [CISCO_DEVICE_URL]: deviceUrl,
81
+ Authorization: await this.webex.credentials.getUserToken(),
82
+ trackingId: `${WEBEX_WEB_CLIENT}_${uuid()}`,
83
+ [SPARK_USER_AGENT]: CALLING_USER_AGENT,
84
+ },
85
+ });
86
+ this.lineEmitter(LINE_EVENTS.UNREGISTERED);
87
+ return response.json();
88
+ }
89
+ async postRegistration(url) {
90
+ const deviceInfo = {
91
+ userId: this.userId,
92
+ clientDeviceUri: this.webex.internal.device.url,
93
+ serviceData: this.serviceData,
94
+ };
95
+ return this.webex.request({
96
+ uri: `${url}device`,
97
+ method: HTTP_METHODS.POST,
98
+ headers: {
99
+ [CISCO_DEVICE_URL]: deviceInfo.clientDeviceUri,
100
+ [SPARK_USER_AGENT]: CALLING_USER_AGENT,
101
+ },
102
+ body: deviceInfo,
103
+ service: ALLOWED_SERVICES.MOBIUS,
104
+ });
105
+ }
106
+ async restorePreviousRegistration(caller) {
107
+ let abort = false;
108
+ if (this.activeMobiusUrl) {
109
+ abort = await this.attemptRegistrationWithServers(caller, [this.activeMobiusUrl]);
110
+ }
111
+ return abort;
112
+ }
113
+ async scheduleFailback429Retry() {
114
+ if (this.failback429RetryAttempts >= REG_FAILBACK_429_MAX_RETRIES) {
115
+ return;
116
+ }
117
+ this.clearFailbackTimer();
118
+ this.failback429RetryAttempts += 1;
119
+ log.log(`Received 429 while rehoming, 429 retry count : ${this.failback429RetryAttempts}`, {
120
+ file: REGISTRATION_FILE,
121
+ method: FAILBACK_429_RETRY_UTIL,
122
+ });
123
+ const interval = this.getRegRetryInterval(this.failback429RetryAttempts);
124
+ this.startFailbackTimer(interval);
125
+ const abort = await this.restorePreviousRegistration(FAILBACK_429_RETRY_UTIL);
126
+ if (!abort && !this.isDeviceRegistered()) {
127
+ await this.restartRegistration(FAILBACK_429_RETRY_UTIL);
128
+ }
129
+ }
130
+ getRegRetryInterval(attempt = 1) {
131
+ return (BASE_REG_RETRY_TIMER_VAL_IN_SEC +
132
+ BASE_REG_TIMER_MFACTOR ** attempt +
133
+ Math.floor((Math.random() * (REG_RANDOM_T_FACTOR_UPPER_LIMIT - SEC_TO_MSEC_MFACTOR + 1) +
134
+ SEC_TO_MSEC_MFACTOR) /
135
+ SEC_TO_MSEC_MFACTOR));
136
+ }
137
+ async startFailoverTimer(attempt = 1, timeElapsed = 0) {
138
+ const loggerContext = {
139
+ file: REGISTRATION_FILE,
140
+ method: this.startFailoverTimer.name,
141
+ };
142
+ let interval = this.getRegRetryInterval(attempt);
143
+ if (timeElapsed + interval > REG_TRY_BACKUP_TIMER_VAL_IN_SEC) {
144
+ const excessVal = timeElapsed + interval - REG_TRY_BACKUP_TIMER_VAL_IN_SEC;
145
+ interval -= excessVal;
146
+ }
147
+ let abort;
148
+ if (interval > BASE_REG_RETRY_TIMER_VAL_IN_SEC) {
149
+ const scheduledTime = Math.floor(Date.now() / 1000);
150
+ setTimeout(async () => {
151
+ await this.mutex.runExclusive(async () => {
152
+ abort = await this.attemptRegistrationWithServers(this.startFailoverTimer.name);
153
+ const currentTime = Math.floor(Date.now() / 1000);
154
+ if (!abort && !this.isDeviceRegistered()) {
155
+ await this.startFailoverTimer(attempt + 1, timeElapsed + (currentTime - scheduledTime));
156
+ }
157
+ });
158
+ }, interval * SEC_TO_MSEC_MFACTOR);
159
+ log.log(`Scheduled retry with primary in ${interval} seconds, number of attempts : ${attempt}`, loggerContext);
160
+ }
161
+ else if (this.backupMobiusUris.length) {
162
+ log.log('Failing over to backup servers.', loggerContext);
163
+ abort = await this.attemptRegistrationWithServers(this.startFailoverTimer.name, this.backupMobiusUris);
164
+ if (!abort && !this.isDeviceRegistered()) {
165
+ interval = this.getRegRetryInterval();
166
+ setTimeout(async () => {
167
+ await this.mutex.runExclusive(async () => {
168
+ abort = await this.attemptRegistrationWithServers(this.startFailoverTimer.name, this.backupMobiusUris);
169
+ if (!abort && !this.isDeviceRegistered()) {
170
+ emitFinalFailure((clientError) => {
171
+ this.lineEmitter(LINE_EVENTS.ERROR, undefined, clientError);
172
+ }, loggerContext);
173
+ }
174
+ });
175
+ }, interval * SEC_TO_MSEC_MFACTOR);
176
+ log.log(`Scheduled retry with backup servers in ${interval} seconds.`, loggerContext);
177
+ }
178
+ }
179
+ else {
180
+ emitFinalFailure((clientError) => {
181
+ this.lineEmitter(LINE_EVENTS.ERROR, undefined, clientError);
182
+ }, loggerContext);
183
+ }
184
+ }
185
+ clearFailbackTimer() {
186
+ if (this.failbackTimer) {
187
+ clearTimeout(this.failbackTimer);
188
+ this.failbackTimer = undefined;
189
+ }
190
+ }
191
+ isFailbackRequired() {
192
+ return this.isDeviceRegistered() && this.primaryMobiusUris.indexOf(this.activeMobiusUrl) === -1;
193
+ }
194
+ getFailbackInterval() {
195
+ return Math.floor(Math.random() * (this.rehomingIntervalMax - this.rehomingIntervalMin + 1) +
196
+ this.rehomingIntervalMin);
197
+ }
198
+ initiateFailback() {
199
+ if (this.isFailbackRequired()) {
200
+ if (!this.failbackTimer) {
201
+ this.failback429RetryAttempts = 0;
202
+ const intervalInMinutes = this.getFailbackInterval();
203
+ this.startFailbackTimer(intervalInMinutes * MINUTES_TO_SEC_MFACTOR);
204
+ }
205
+ }
206
+ else {
207
+ this.failback429RetryAttempts = 0;
208
+ this.clearFailbackTimer();
209
+ }
210
+ }
211
+ startFailbackTimer(intervalInSeconds) {
212
+ this.failbackTimer = setTimeout(async () => this.executeFailback(), intervalInSeconds * SEC_TO_MSEC_MFACTOR);
213
+ log.log(`Failback scheduled after ${intervalInSeconds} seconds.`, {
214
+ file: REGISTRATION_FILE,
215
+ method: this.startFailbackTimer.name,
216
+ });
217
+ }
218
+ async executeFailback() {
219
+ await this.mutex.runExclusive(async () => {
220
+ if (this.isFailbackRequired()) {
221
+ if (Object.keys(this.callManager.getActiveCalls()).length === 0) {
222
+ log.info(`Attempting failback to primary.`, {
223
+ file: REGISTRATION_FILE,
224
+ method: this.executeFailback.name,
225
+ });
226
+ await this.deregister();
227
+ const abort = await this.attemptRegistrationWithServers(FAILBACK_UTIL);
228
+ if (!abort && !this.isDeviceRegistered()) {
229
+ const abortNew = await this.restorePreviousRegistration(FAILBACK_UTIL);
230
+ if (abortNew) {
231
+ this.clearFailbackTimer();
232
+ return;
233
+ }
234
+ if (!this.isDeviceRegistered()) {
235
+ await this.restartRegistration(this.executeFailback.name);
236
+ }
237
+ else {
238
+ this.failbackTimer = undefined;
239
+ this.initiateFailback();
240
+ }
241
+ }
242
+ }
243
+ else {
244
+ log.info('Active calls present, deferring failback to next cycle.', {
245
+ file: REGISTRATION_FILE,
246
+ method: this.executeFailback.name,
247
+ });
248
+ this.failbackTimer = undefined;
249
+ this.initiateFailback();
250
+ }
251
+ }
252
+ });
253
+ }
254
+ setIntervalValues(deviceInfo) {
255
+ if (this.primaryMobiusUris.indexOf(this.activeMobiusUrl) !== -1) {
256
+ this.rehomingIntervalMin = deviceInfo?.rehomingIntervalMin
257
+ ? deviceInfo.rehomingIntervalMin
258
+ : DEFAULT_REHOMING_INTERVAL_MIN;
259
+ this.rehomingIntervalMax = deviceInfo?.rehomingIntervalMax
260
+ ? deviceInfo.rehomingIntervalMax
261
+ : DEFAULT_REHOMING_INTERVAL_MAX;
262
+ }
263
+ }
264
+ getDeviceInfo() {
265
+ return this.deviceInfo;
266
+ }
267
+ isDeviceRegistered() {
268
+ return this.registrationStatus === MobiusStatus.ACTIVE;
269
+ }
270
+ getStatus() {
271
+ return this.registrationStatus;
272
+ }
273
+ setStatus(value) {
274
+ this.registrationStatus = value;
275
+ }
276
+ async restartRegistration(caller) {
277
+ this.clearFailbackTimer();
278
+ this.failback429RetryAttempts = 0;
279
+ const abort = await this.attemptRegistrationWithServers(caller, this.primaryMobiusUris);
280
+ if (!abort && !this.isDeviceRegistered()) {
281
+ await this.startFailoverTimer();
282
+ }
283
+ }
284
+ async handleConnectionRestoration(retry) {
285
+ await this.mutex.runExclusive(async () => {
286
+ if (retry) {
287
+ log.info('Mercury connection is up again, Re-registering with Mobius', {
288
+ file: REGISTRATION_FILE,
289
+ method: this.handleConnectionRestoration.name,
290
+ });
291
+ this.clearKeepaliveTimer();
292
+ if (this.isDeviceRegistered()) {
293
+ await this.deregister();
294
+ }
295
+ if (this.activeMobiusUrl) {
296
+ const abort = await this.restorePreviousRegistration(this.handleConnectionRestoration.name);
297
+ if (!abort && !this.isDeviceRegistered()) {
298
+ await this.restartRegistration(this.handleConnectionRestoration.name);
299
+ }
300
+ }
301
+ retry = false;
302
+ }
303
+ });
304
+ return retry;
305
+ }
306
+ restoreRegistrationCallBack() {
307
+ return async (restoreData, caller) => {
308
+ const logContext = { file: REGISTRATION_FILE, method: caller };
309
+ if (!this.isRegRetry()) {
310
+ log.info('Registration restoration in progress.', logContext);
311
+ const restore = this.getExistingDevice(restoreData);
312
+ if (restore) {
313
+ this.setRegRetry(true);
314
+ await this.deregister();
315
+ const finalError = await this.restorePreviousRegistration(caller);
316
+ this.setRegRetry(false);
317
+ if (this.isDeviceRegistered()) {
318
+ log.info('Registration restored successfully.', logContext);
319
+ }
320
+ return finalError;
321
+ }
322
+ this.lineEmitter(LINE_EVENTS.UNREGISTERED);
323
+ }
324
+ else {
325
+ this.lineEmitter(LINE_EVENTS.UNREGISTERED);
326
+ }
327
+ return false;
328
+ };
329
+ }
330
+ async triggerRegistration() {
331
+ if (this.primaryMobiusUris.length > 0) {
332
+ const abort = await this.attemptRegistrationWithServers(this.triggerRegistration.name, this.primaryMobiusUris);
333
+ if (!this.isDeviceRegistered() && !abort) {
334
+ await this.startFailoverTimer();
335
+ }
336
+ }
337
+ }
338
+ async attemptRegistrationWithServers(caller, servers = this.primaryMobiusUris) {
339
+ let abort = false;
340
+ if (this.isDeviceRegistered()) {
341
+ log.log(`[${caller}] : Device already registered with : ${this.activeMobiusUrl}`, {
342
+ file: REGISTRATION_FILE,
343
+ method: this.attemptRegistrationWithServers.name,
344
+ });
345
+ return abort;
346
+ }
347
+ for (const url of servers) {
348
+ try {
349
+ abort = false;
350
+ this.registrationStatus = MobiusStatus.DEFAULT;
351
+ this.lineEmitter(LINE_EVENTS.CONNECTING);
352
+ log.log(`[${caller}] : Mobius url to contact: ${url}`, {
353
+ file: REGISTRATION_FILE,
354
+ method: this.attemptRegistrationWithServers.name,
355
+ });
356
+ const resp = await this.postRegistration(url);
357
+ this.deviceInfo = resp.body;
358
+ this.lineEmitter(LINE_EVENTS.REGISTERED, resp.body);
359
+ this.registrationStatus = MobiusStatus.ACTIVE;
360
+ this.setActiveMobiusUrl(url);
361
+ this.setIntervalValues(this.deviceInfo);
362
+ this.metricManager.setDeviceInfo(this.deviceInfo);
363
+ this.metricManager.submitRegistrationMetric(METRIC_EVENT.REGISTRATION, REG_ACTION.REGISTER, METRIC_TYPE.BEHAVIORAL, undefined);
364
+ this.startKeepaliveTimer(this.deviceInfo.device?.uri, this.deviceInfo.keepaliveInterval);
365
+ this.initiateFailback();
366
+ break;
367
+ }
368
+ catch (err) {
369
+ const body = err;
370
+ abort = await handleRegistrationErrors(body, (clientError, finalError) => {
371
+ if (finalError) {
372
+ this.lineEmitter(LINE_EVENTS.ERROR, undefined, clientError);
373
+ }
374
+ else {
375
+ this.lineEmitter(LINE_EVENTS.UNREGISTERED);
376
+ }
377
+ this.metricManager.submitRegistrationMetric(METRIC_EVENT.REGISTRATION_ERROR, REG_ACTION.REGISTER, METRIC_TYPE.BEHAVIORAL, clientError);
378
+ }, { method: this.attemptRegistrationWithServers.name, file: REGISTRATION_FILE }, this.restoreRegistrationCallBack());
379
+ if (this.registrationStatus === MobiusStatus.ACTIVE) {
380
+ log.info(`[${caller}] : Device is already restored, active mobius url: ${this.activeMobiusUrl}`, {
381
+ file: REGISTRATION_FILE,
382
+ method: this.attemptRegistrationWithServers.name,
383
+ });
384
+ break;
385
+ }
386
+ if (abort) {
387
+ break;
388
+ }
389
+ else if (caller === this.executeFailback.name) {
390
+ const error = body.statusCode;
391
+ if (error === ERROR_CODE.TOO_MANY_REQUESTS) {
392
+ await this.scheduleFailback429Retry();
393
+ abort = true;
394
+ break;
395
+ }
396
+ }
397
+ }
398
+ }
399
+ return abort;
400
+ }
401
+ startKeepaliveTimer(url, interval) {
402
+ let keepAliveRetryCount = 0;
403
+ this.clearKeepaliveTimer();
404
+ this.keepaliveTimer = setInterval(async () => {
405
+ const logContext = {
406
+ file: REGISTRATION_FILE,
407
+ method: this.startKeepaliveTimer.name,
408
+ };
409
+ await this.mutex.runExclusive(async () => {
410
+ if (this.isDeviceRegistered() && keepAliveRetryCount < 5) {
411
+ try {
412
+ const res = await this.postKeepAlive(url);
413
+ log.info(`Sent Keepalive, status: ${res.statusCode}`, logContext);
414
+ if (keepAliveRetryCount > 0) {
415
+ this.lineEmitter(LINE_EVENTS.RECONNECTED);
416
+ }
417
+ keepAliveRetryCount = 0;
418
+ }
419
+ catch (err) {
420
+ keepAliveRetryCount += 1;
421
+ const error = err;
422
+ log.warn(`Keep-alive missed ${keepAliveRetryCount} times. Status -> ${error.statusCode} `, logContext);
423
+ const abort = await handleRegistrationErrors(error, (clientError, finalError) => {
424
+ if (finalError) {
425
+ this.lineEmitter(LINE_EVENTS.ERROR, undefined, clientError);
426
+ }
427
+ this.metricManager.submitRegistrationMetric(METRIC_EVENT.REGISTRATION, REG_ACTION.KEEPALIVE_FAILURE, METRIC_TYPE.BEHAVIORAL, clientError);
428
+ }, { method: this.startKeepaliveTimer.name, file: REGISTRATION_FILE });
429
+ if (abort || keepAliveRetryCount >= 5) {
430
+ this.setStatus(MobiusStatus.DEFAULT);
431
+ this.clearKeepaliveTimer();
432
+ this.clearFailbackTimer();
433
+ this.lineEmitter(LINE_EVENTS.UNREGISTERED);
434
+ if (!abort) {
435
+ await this.reconnectOnFailure(this.startKeepaliveTimer.name);
436
+ }
437
+ }
438
+ else {
439
+ this.lineEmitter(LINE_EVENTS.RECONNECTING);
440
+ }
441
+ }
442
+ }
443
+ });
444
+ }, interval * 1000);
445
+ }
446
+ clearKeepaliveTimer() {
447
+ if (this.keepaliveTimer) {
448
+ clearInterval(this.keepaliveTimer);
449
+ this.keepaliveTimer = undefined;
450
+ }
451
+ }
452
+ isReconnectPending() {
453
+ return this.reconnectPending;
454
+ }
455
+ async deregister() {
456
+ try {
457
+ await this.deleteRegistration(this.activeMobiusUrl, this.deviceInfo.device?.deviceId, this.deviceInfo.device?.clientDeviceUri);
458
+ }
459
+ catch (err) {
460
+ log.warn(`Delete failed with Mobius`, {});
461
+ }
462
+ this.clearKeepaliveTimer();
463
+ this.setStatus(MobiusStatus.DEFAULT);
464
+ }
465
+ isRegRetry() {
466
+ return this.registerRetry;
467
+ }
468
+ setRegRetry(value) {
469
+ this.registerRetry = value;
470
+ }
471
+ getExistingDevice(restoreData) {
472
+ if (restoreData.devices && restoreData.devices.length > 0) {
473
+ this.deviceInfo = {
474
+ userId: restoreData.userId,
475
+ device: restoreData.devices[0],
476
+ keepaliveInterval: DEFAULT_KEEPALIVE_INTERVAL,
477
+ rehomingIntervalMax: DEFAULT_REHOMING_INTERVAL_MAX,
478
+ rehomingIntervalMin: DEFAULT_REHOMING_INTERVAL_MIN,
479
+ };
480
+ const stringToReplace = `${DEVICES_ENDPOINT_RESOURCE}/${restoreData.devices[0].deviceId}`;
481
+ const uri = restoreData.devices[0].uri.replace(stringToReplace, '');
482
+ this.setActiveMobiusUrl(uri);
483
+ this.registrationStatus = MobiusStatus.ACTIVE;
484
+ return true;
485
+ }
486
+ return false;
487
+ }
488
+ async reconnectOnFailure(caller) {
489
+ this.reconnectPending = false;
490
+ if (!this.isDeviceRegistered()) {
491
+ if (Object.keys(this.callManager.getActiveCalls()).length === 0) {
492
+ const abort = await this.restorePreviousRegistration(caller);
493
+ if (!abort && !this.isDeviceRegistered()) {
494
+ await this.restartRegistration(caller);
495
+ }
496
+ }
497
+ else {
498
+ this.reconnectPending = true;
499
+ log.info('Active call(s) present, deferred reconnect till call cleanup.', {
500
+ file: REGISTRATION_FILE,
501
+ method: this.reconnectOnFailure.name,
502
+ });
503
+ }
504
+ }
505
+ }
506
+ }
507
+ export const createRegistration = (webex, serviceData, mutex, lineEmitter, logLevel) => new Registration(webex, serviceData, mutex, lineEmitter, logLevel);
@@ -0,0 +1,28 @@
1
+ export const DEVICE_ID = '9381a370-b26b-3c5b-8901-4e8dab405dcc';
2
+ export const CLIENT_DEVICE_URL = 'https://clientDeviceUrl';
3
+ export const URL = 'https://wdm-intb.ciscospark.com/wdm/api/v1/';
4
+ export const mockPostResponse = {
5
+ userId: '8a67806f-fc4d-446b-a131-31e71ea5b0e9',
6
+ device: {
7
+ deviceId: 'beb3c025-8c6a-3c44-8f3d-9b7d65363ac1',
8
+ uri: 'https://wdm-intb.ciscospark.com/wdm/api/v1/calling/web/devices/beb3c025-8c6a-3c44-8f3d-9b7d65363ac1',
9
+ status: 'active',
10
+ lastSeen: '2022-04-05T05:08:46Z',
11
+ addresses: ['sip:pbs9p4cbr9_G6JJNI5DD5NP@64941297.int10.bcld.webex.com'],
12
+ clientDeviceUri: CLIENT_DEVICE_URL,
13
+ },
14
+ keepaliveInterval: 30,
15
+ rehomingIntervalMin: 90,
16
+ rehomingIntervalMax: 180,
17
+ };
18
+ export const mockDeleteResponse = {
19
+ userId: '8a67806f-fc4d-446b-a131-31e71ea5b0e9',
20
+ device: {
21
+ deviceId: DEVICE_ID,
22
+ uri: 'https://wdm-intb.ciscospark.com/wdm/api/v1/calling/web/devices/9381a370-b26b-3c5b-8901-4e8dab405dcc',
23
+ status: 'active',
24
+ lastSeen: '2022-04-05T05:16:37Z',
25
+ addresses: ['sip:pbs9p4cbr9_G6JJNI5DD5NP@64941297.int10.bcld.webex.com'],
26
+ clientDeviceUri: CLIENT_DEVICE_URL,
27
+ },
28
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};