@wazo/sdk 0.37.0-rc.5 → 0.37.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.
Files changed (149) hide show
  1. package/dist/wazo-sdk.js +5 -5
  2. package/esm/__tests__/api-client.test.js +93 -16
  3. package/esm/__tests__/web-rtc-client.test.js +153 -54
  4. package/esm/api/agentd.js +24 -3
  5. package/esm/api/amid.js +7 -1
  6. package/esm/api/application.js +24 -3
  7. package/esm/api/auth.js +24 -3
  8. package/esm/api/call-logd.js +78 -20
  9. package/esm/api/calld.js +24 -20
  10. package/esm/api/chatd.js +24 -3
  11. package/esm/api/confd.js +24 -3
  12. package/esm/api/ctid-ng.js +24 -3
  13. package/esm/api/dird.js +27 -6
  14. package/esm/api/webhookd.js +7 -1
  15. package/esm/api-client.js +93 -16
  16. package/esm/checker/Checker.js +156 -54
  17. package/esm/checker/checks/aor.js +95 -16
  18. package/esm/checker/checks/api.js +94 -16
  19. package/esm/checker/checks/index.js +156 -54
  20. package/esm/checker/checks/wazo-websocket.js +7 -1
  21. package/esm/checker/checks/webrtc-transport.js +153 -54
  22. package/esm/checker/checks/webrtc.js +153 -54
  23. package/esm/domain/AdHocAPIConference.js +1717 -1640
  24. package/esm/domain/CallLog.js +71 -19
  25. package/esm/domain/CallSession.js +1 -18
  26. package/esm/domain/Contact.js +3 -3
  27. package/esm/domain/Features.js +93 -16
  28. package/esm/domain/IndirectTransfer.js +0 -17
  29. package/esm/domain/Phone/CTIPhone.js +100 -19
  30. package/esm/domain/Phone/Phone.js +0 -17
  31. package/esm/domain/Phone/WebRTCPhone.js +51 -36
  32. package/esm/domain/Recording.js +51 -0
  33. package/esm/domain/Room.js +0 -17
  34. package/esm/domain/SwitchboardCall.js +0 -17
  35. package/esm/domain/__tests__/CallLog.test.js +89 -19
  36. package/esm/domain/__tests__/CallSession.test.js +1 -18
  37. package/esm/domain/__tests__/Contact.test.js +3 -3
  38. package/esm/domain/__tests__/IndirectTransfer.test.js +1 -18
  39. package/esm/domain/__tests__/Room.test.js +1 -18
  40. package/esm/domain/__tests__/Session.test.js +7 -0
  41. package/esm/index.js +3436 -2911
  42. package/esm/lib/WazoSessionDescriptionHandler.js +28 -10
  43. package/esm/service/CallApi.js +93 -16
  44. package/esm/service/IssueReporter.js +7 -1
  45. package/esm/service/__tests__/IssueReporter.test.js +26 -2
  46. package/esm/service/getApiClient.js +93 -16
  47. package/esm/simple/Auth.js +619 -97
  48. package/esm/simple/Configuration.js +619 -97
  49. package/esm/simple/Directory.js +93 -16
  50. package/esm/simple/Phone.js +3261 -2739
  51. package/esm/simple/Websocket.js +7 -1
  52. package/esm/simple/index.js +619 -97
  53. package/esm/simple/room/LocalParticipant.js +619 -97
  54. package/esm/simple/room/Participant.js +619 -97
  55. package/esm/simple/room/RemoteParticipant.js +619 -97
  56. package/esm/simple/room/Room.js +619 -97
  57. package/esm/simple/room/Stream.js +619 -97
  58. package/esm/simple/room/__tests__/RemoteParticipant.test.js +619 -97
  59. package/esm/simple/utils.js +619 -97
  60. package/esm/utils/__tests__/api-requester.test.js +32 -6
  61. package/esm/utils/api-requester.js +24 -3
  62. package/esm/web-rtc-client.js +153 -54
  63. package/esm/websocket-client.js +7 -1
  64. package/lib/__tests__/api-client.test.js +93 -16
  65. package/lib/__tests__/web-rtc-client.test.js +154 -55
  66. package/lib/api/agentd.js +24 -3
  67. package/lib/api/amid.js +7 -1
  68. package/lib/api/application.js +24 -3
  69. package/lib/api/auth.js +24 -3
  70. package/lib/api/call-logd.js +78 -20
  71. package/lib/api/calld.js +24 -20
  72. package/lib/api/chatd.js +24 -3
  73. package/lib/api/confd.js +24 -3
  74. package/lib/api/ctid-ng.js +24 -3
  75. package/lib/api/dird.js +27 -6
  76. package/lib/api/webhookd.js +7 -1
  77. package/lib/api-client.js +93 -16
  78. package/lib/checker/Checker.js +157 -55
  79. package/lib/checker/checks/aor.js +95 -16
  80. package/lib/checker/checks/api.js +94 -16
  81. package/lib/checker/checks/index.js +157 -55
  82. package/lib/checker/checks/wazo-websocket.js +7 -1
  83. package/lib/checker/checks/webrtc-transport.js +154 -55
  84. package/lib/checker/checks/webrtc.js +154 -55
  85. package/lib/domain/AdHocAPIConference.js +1717 -1640
  86. package/lib/domain/CallLog.js +71 -19
  87. package/lib/domain/CallSession.js +1 -18
  88. package/lib/domain/Contact.js +3 -3
  89. package/lib/domain/Features.js +93 -16
  90. package/lib/domain/IndirectTransfer.js +0 -17
  91. package/lib/domain/Phone/CTIPhone.js +100 -19
  92. package/lib/domain/Phone/Phone.js +0 -16
  93. package/lib/domain/Phone/WebRTCPhone.js +51 -36
  94. package/lib/domain/Recording.js +57 -0
  95. package/lib/domain/Room.js +0 -17
  96. package/lib/domain/SwitchboardCall.js +0 -17
  97. package/lib/domain/__tests__/CallLog.test.js +89 -19
  98. package/lib/domain/__tests__/CallSession.test.js +1 -18
  99. package/lib/domain/__tests__/Contact.test.js +3 -3
  100. package/lib/domain/__tests__/IndirectTransfer.test.js +1 -18
  101. package/lib/domain/__tests__/Room.test.js +1 -18
  102. package/lib/domain/__tests__/Session.test.js +7 -0
  103. package/lib/index.js +3437 -2912
  104. package/lib/lib/WazoSessionDescriptionHandler.js +28 -10
  105. package/lib/service/CallApi.js +93 -16
  106. package/lib/service/IssueReporter.js +7 -1
  107. package/lib/service/__tests__/IssueReporter.test.js +26 -2
  108. package/lib/service/getApiClient.js +93 -16
  109. package/lib/simple/Auth.js +620 -98
  110. package/lib/simple/Configuration.js +620 -98
  111. package/lib/simple/Directory.js +93 -16
  112. package/lib/simple/Phone.js +3262 -2740
  113. package/lib/simple/Websocket.js +7 -1
  114. package/lib/simple/index.js +620 -98
  115. package/lib/simple/room/LocalParticipant.js +620 -98
  116. package/lib/simple/room/Participant.js +620 -98
  117. package/lib/simple/room/RemoteParticipant.js +620 -98
  118. package/lib/simple/room/Room.js +620 -98
  119. package/lib/simple/room/Stream.js +620 -98
  120. package/lib/simple/room/__tests__/RemoteParticipant.test.js +620 -98
  121. package/lib/simple/utils.js +620 -98
  122. package/lib/utils/__tests__/api-requester.test.js +32 -6
  123. package/lib/utils/api-requester.js +24 -3
  124. package/lib/web-rtc-client.js +154 -55
  125. package/lib/websocket-client.js +7 -1
  126. package/package.json +1 -1
  127. package/src/api-client.js +4 -0
  128. package/src/checker/checks/aor.js +2 -0
  129. package/src/checker/checks/api.js +1 -0
  130. package/src/domain/CallLog.js +18 -11
  131. package/src/domain/CallSession.js +1 -2
  132. package/src/domain/Contact.js +3 -3
  133. package/src/domain/Phone/CTIPhone.js +6 -2
  134. package/src/domain/Phone/Phone.js +2 -0
  135. package/src/domain/Phone/WebRTCPhone.js +26 -16
  136. package/src/domain/Recording.js +48 -0
  137. package/src/domain/__tests__/CallLog.test.js +19 -0
  138. package/src/domain/__tests__/Session.test.js +7 -0
  139. package/src/index.js +6 -0
  140. package/src/lib/WazoSessionDescriptionHandler.js +21 -9
  141. package/src/service/IssueReporter.js +7 -1
  142. package/src/service/__tests__/IssueReporter.test.js +20 -1
  143. package/src/simple/Phone.js +5 -1
  144. package/src/simple/index.js +6 -0
  145. package/src/simple/room/Room.js +18 -15
  146. package/src/simple/room/Stream.js +1 -4
  147. package/src/utils/__tests__/api-requester.test.js +9 -4
  148. package/src/utils/api-requester.js +17 -2
  149. package/src/web-rtc-client.js +40 -13
@@ -2,8 +2,8 @@
2
2
 
3
3
  require('sip.js/lib/api/session-state');
4
4
  var moment = require('moment');
5
- var jsrsasign = require('jsrsasign');
6
5
  var jsBase64 = require('js-base64');
6
+ var jsrsasign = require('jsrsasign');
7
7
  require('events');
8
8
  var jsonToGraphQLQuery = require('json-to-graphql-query/lib/jsonToGraphQLQuery');
9
9
 
@@ -213,2052 +213,2073 @@ class Call {
213
213
  }
214
214
 
215
215
  //
216
+ /* eslint-disable no-param-reassign */
216
217
 
217
- const swarmPublicKey = `-----BEGIN PUBLIC KEY-----
218
- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmkXOuNfY8u5xoTiIhkb8
219
- djnbIwG/Wrz3vpo8BZir8L5e1a1nSy740qBjP7ZINBQoALDhFfmdOfJnCyEGiHuz
220
- ZW6jbG6C3PryE3Bu6GKwqSmD6k3q4Zk27fpwYAnNl+rWhYYM563rJZBda/INyHNN
221
- pK7M1mixWi7gNdjjXwoEXSSBx+VpYMkY6LiAB2mvHXTY9M1qI14dvgGoQISZQoKi
222
- NMTRCg5UP2ic0Dd9nSz/XpcOxGfa+0fwIl1F7RC1tJXOqvkGGPTOV4LLfg/Yta3h
223
- nUPX9EZZDIX6vO/0IBV1LzjSl2A1bYFYAjJfowv3i1CpvONBOClHjSY5t9Y8MH6p
224
- BwIDAQAB
225
- -----END PUBLIC KEY-----`;
218
+ var updateFrom = (instance , from ) => {
219
+ Object.keys(from).forEach(key => {
220
+ // $FlowFixMe
221
+ if (
222
+ (!instance[key] && from[key])
223
+ || (typeof instance[key] !== 'undefined' && typeof from[key] !== 'undefined' && instance[key] !== from[key])
224
+ ) {
225
+ // $FlowFixMe
226
+ instance[key] = from[key];
227
+ }
228
+ });
229
+ };
226
230
 
227
- //
231
+ /* */
228
232
 
229
-
230
-
231
-
232
-
233
-
234
-
233
+ class BadResponse extends Error {
234
+ static fromResponse(error , status ) {
235
+ return new BadResponse(
236
+ error.message || JSON.stringify(error),
237
+ status,
238
+ error.timestamp,
239
+ error.error_id,
240
+ error.details,
241
+ error,
242
+ );
243
+ }
235
244
 
236
-
237
-
238
-
239
-
240
-
245
+ static fromText(response , status ) {
246
+ return new BadResponse(response, status);
247
+ }
241
248
 
249
+
250
+
242
251
 
243
-
244
-
245
-
246
-
247
-
248
-
252
+
253
+
254
+
249
255
 
250
-
251
-
252
-
253
-
254
-
255
-
256
-
256
+ constructor(
257
+ message ,
258
+ status ,
259
+ timestamp = null,
260
+ errorId = null,
261
+ details = null,
262
+ error = null,
263
+ ) {
264
+ super(message);
257
265
 
258
- class Line {
259
-
266
+ this.timestamp = timestamp;
267
+ this.status = status;
268
+ this.errorId = errorId;
269
+ this.details = details;
270
+ this.error = error;
271
+ }
272
+ }
260
273
 
261
-
262
-
263
-
264
-
265
-
274
+ /* */
266
275
 
267
- static parse(plain ) {
268
- return new Line({
269
- id: plain.id,
270
- extensions: plain.extensions,
271
- endpointCustom: plain.endpoint_custom || null,
272
- endpointSccp: plain.endpoint_sccp || null,
273
- endpointSip: plain.endpoint_sip || null,
274
- });
276
+ class ServerError extends BadResponse {
277
+ static fromResponse(error , status ) {
278
+ return new ServerError(
279
+ error.message || JSON.stringify(error),
280
+ status,
281
+ error.timestamp,
282
+ error.error_id,
283
+ error.details,
284
+ );
275
285
  }
276
286
 
277
- static newFrom(profile ) {
278
- return newFrom(profile, Line);
287
+ static fromText(response , status ) {
288
+ return new ServerError(response, status);
279
289
  }
290
+ }
280
291
 
281
- is(line ) {
282
- return this.id === line.id;
283
- }
292
+ /* */
293
+ /* global navigator */
294
+ const isMobile = () => typeof navigator !== 'undefined' && navigator.product === 'ReactNative';
284
295
 
285
- hasExtension(extension ) {
286
- if (!this.extensions) {
287
- return false;
288
- }
296
+ /* global window */
289
297
 
290
- return this.extensions.some(ext => ext.exten === extension);
291
- }
298
+ const TRACE = 'trace';
299
+ const DEBUG = 'debug';
300
+ const INFO = 'info';
301
+ const LOG = 'log';
302
+ const WARN = 'warn';
303
+ const ERROR = 'error';
304
+ const CONSOLE_METHODS = [INFO, LOG, WARN, ERROR];
305
+ const LOG_LEVELS = [TRACE, DEBUG, INFO, LOG, WARN, ERROR];
306
+ const CATEGORY_PREFIX = 'logger-category=';
292
307
 
293
- constructor({ id, extensions, endpointCustom, endpointSccp, endpointSip } = {}) {
294
- this.id = id;
295
- this.extensions = extensions;
296
- this.endpointCustom = endpointCustom || null;
297
- this.endpointSccp = endpointSccp || null;
298
- this.endpointSip = endpointSip || null;
308
+ const MAX_REMOTE_RETRY = 100;
299
309
 
300
- // Useful to compare instead of instanceof with minified code
301
- this.type = 'Line';
310
+ const addLevelsTo = (instance , withMethods = false) => {
311
+ instance.TRACE = TRACE;
312
+ instance.DEBUG = DEBUG;
313
+ instance.INFO = INFO;
314
+ instance.LOG = LOG;
315
+ instance.WARN = WARN;
316
+ instance.ERROR = ERROR;
317
+
318
+ if (withMethods) {
319
+ instance.trace = (...args) => instance.apply(null, [TRACE, ...args]);
320
+ instance.debug = (...args) => instance.apply(null, [DEBUG, ...args]);
321
+ instance.info = (...args) => instance.apply(null, [INFO, ...args]);
322
+ instance.log = (...args) => instance.apply(null, [LOG, ...args]);
323
+ instance.warn = (...args) => instance.apply(null, [WARN, ...args]);
324
+ instance.error = (...args) => instance.apply(null, [ERROR, ...args]);
302
325
  }
303
- }
304
326
 
305
- //
327
+ return instance;
328
+ };
306
329
 
307
-
308
-
309
-
310
-
330
+ const safeStringify = (object ) => {
331
+ const result = '{"message": "Not parsable JSON"}';
332
+ try {
333
+ return JSON.stringify(object);
334
+ } catch (e) {
335
+ // Nothing to do
336
+ }
311
337
 
312
- const FORWARD_KEYS = {
313
- BUSY: 'busy',
314
- NO_ANSWER: 'noanswer',
315
- UNCONDITIONAL: 'unconditional',
338
+ return result;
316
339
  };
317
340
 
318
-
319
-
320
-
341
+ class IssueReporter {
342
+
343
+
321
344
 
322
-
345
+
346
+
323
347
 
324
- class ForwardOption {
325
-
348
+
326
349
 
327
-
350
+
328
351
 
329
- static parse(plain , key ) {
330
- return new ForwardOption({
331
- destination: plain.destination || '',
332
- enabled: plain.enabled,
333
- key,
334
- });
352
+ constructor() {
353
+ addLevelsTo(this);
354
+
355
+ this.oldConsoleMethods = null;
356
+ this.enabled = false;
357
+ this.remoteClientConfiguration = null;
335
358
  }
336
359
 
337
- static newFrom(profile ) {
338
- return newFrom(profile, ForwardOption);
360
+ init() {
361
+ this._catchConsole();
339
362
  }
340
363
 
341
- constructor({ destination, enabled, key } = {}) {
342
- this.destination = destination;
343
- this.enabled = enabled;
344
- this.key = key;
364
+ configureRemoteClient(configuration = { tag: 'wazo-sdk', host: null, port: null, level: null, extra: {} }) {
365
+ this.remoteClientConfiguration = configuration;
345
366
  }
346
367
 
347
- setDestination(number ) {
348
- this.destination = number;
368
+ enable() {
369
+ if (!this.oldConsoleMethods) {
370
+ this.init();
371
+ }
349
372
 
350
- return this;
373
+ this.enabled = true;
351
374
  }
352
375
 
353
- setEnabled(enabled ) {
354
- this.enabled = enabled;
355
-
356
- return this;
376
+ disable() {
377
+ this.enabled = false;
357
378
  }
358
379
 
359
- is(other ) {
360
- return this.key === other.key;
380
+ loggerFor(category ) {
381
+ const logger = (level , ...args ) => {
382
+ this.log.apply(this, [level, this._makeCategory(category), ...args]);
383
+ };
384
+
385
+ return addLevelsTo(logger, true);
361
386
  }
362
- }
363
387
 
364
- //
365
-
388
+ removeSlashes(str ) {
389
+ return str.replace(/"/g, "'").replace(/\\/g, '');
390
+ }
366
391
 
367
- const availableCodecs = ['vp8', 'vp9', 'h264'];
392
+ log(level , ...args ) {
393
+ if (!this.enabled) {
394
+ return;
395
+ }
368
396
 
369
-
370
-
371
-
372
-
373
-
374
-
375
-
376
-
377
-
378
-
379
-
380
-
381
-
382
-
383
-
397
+ // Handle category label
398
+ let category = null;
399
+ let extra = {};
400
+ if (args[0].indexOf(CATEGORY_PREFIX) === 0) {
401
+ category = args[0].split('=')[1];
402
+ // eslint-disable-next-line no-param-reassign
403
+ args.splice(0, 1);
404
+ }
384
405
 
385
-
386
-
387
-
388
-
389
-
390
-
391
-
392
-
393
-
394
-
395
-
396
-
397
-
398
-
406
+ // Handle extra data as object for the last argument
407
+ const lastArg = args[args.length - 1];
408
+ if (lastArg && ((typeof lastArg === 'object' && Object.keys(lastArg).length) || lastArg instanceof Error)) {
409
+ if (lastArg instanceof Error) {
410
+ extra = {
411
+ errorMessage: lastArg.message,
412
+ errorStack: lastArg.stack,
413
+ errorType: lastArg.constructor.name,
414
+ };
415
+ } else {
416
+ extra = lastArg;
417
+ }
418
+ // eslint-disable-next-line no-param-reassign
419
+ args.splice(1, 1);
420
+ }
399
421
 
400
- class SipLine {
401
-
402
-
403
-
404
-
405
-
406
-
407
-
408
-
409
-
410
-
411
-
412
-
422
+ const date = moment__default['default']().format('YYYY-MM-DD HH:mm:ss.SSS');
423
+ const message = args.join(', ');
424
+ let consoleMessage = message;
413
425
 
414
- static parse(plain ) {
415
- let { username, secret, host } = plain;
426
+ if (Object.keys(extra).length) {
427
+ const parsedExtra = safeStringify(extra);
428
+ consoleMessage = `${consoleMessage} (${parsedExtra})`;
429
+ }
416
430
 
417
- // Since 20.13 engine so options are now in section
418
- if (plain.auth_section_options) {
419
- // $FlowFixMe
420
- const findOption = (options , name ) => options.find(option => option[0] === name);
431
+ if (category) {
432
+ consoleMessage = `[${category}] ${consoleMessage}`;
433
+ }
421
434
 
422
- const usernameOption = findOption(plain.auth_section_options, 'username');
423
- const secretOption = findOption(plain.auth_section_options, 'password');
424
- const hostOption = findOption(plain.endpoint_section_options, 'media_address');
435
+ // Log the message in the console anyway (but don't console.error on mobile)
436
+ const consoleLevel = isMobile() && level === 'error' ? WARN : level;
425
437
 
426
- username = usernameOption ? usernameOption[1] : '';
427
- secret = secretOption ? secretOption[1] : '';
428
- host = hostOption ? hostOption[1] : '';
438
+ // eslint-disable-next-line
439
+ const oldMethod = this.oldConsoleMethods[consoleLevel] || this.oldConsoleMethods.log;
440
+ oldMethod.apply(oldMethod, [date, consoleMessage]);
441
+
442
+ this._sendToRemoteLogger(level, { date, message, category, ...extra });
443
+ }
444
+
445
+ logRequest(url , options , response , start ) {
446
+ if (!this.enabled) {
447
+ return;
429
448
  }
449
+ const { status } = response;
450
+ const duration = new Date() - start;
430
451
 
431
- return new SipLine({
432
- id: plain.id,
433
- uuid: plain.uuid,
434
- tenantUuid: plain.tenant_uuid,
435
- username,
436
- secret,
437
- type: plain.type,
438
- host,
439
- options: plain.options,
440
- endpointSectionOptions: plain.endpoint_section_options,
441
- links: plain.links,
442
- trunk: plain.trunk,
443
- line: plain.line,
452
+ let level = TRACE;
453
+ if (status >= 400 && status < 500) {
454
+ level = WARN;
455
+ } else if (status >= 500) {
456
+ level = ERROR;
457
+ }
458
+
459
+ this.log(level, this._makeCategory('http'), url, {
460
+ status,
461
+ body: this.removeSlashes(JSON.stringify(options.body)),
462
+ method: options.method,
463
+ headers: options.headers,
464
+ duration,
444
465
  });
445
466
  }
446
467
 
447
- static newFrom(sipLine ) {
448
- return newFrom(sipLine, SipLine);
468
+ // Logs aren't stored anymore
469
+ getLogs() {
470
+ console.warn('IssueReporter\'s logs aren\'t stored anymore. Please use fluentd to store them');
471
+ return [];
449
472
  }
450
473
 
451
- is(line ) {
452
- if (line.uuid) {
453
- return this.uuid === line.uuid;
454
- }
455
- return this.id === line.id;
474
+ getParsedLogs() {
475
+ console.warn('IssueReporter\'s logs aren\'t stored anymore. Please use fluentd to store them');
476
+ return [];
456
477
  }
457
478
 
458
- getOptions() {
459
- // `options` params have beend remove since 20.13... we should now use endpointSectionOptions
460
- return this.endpointSectionOptions || this.options || [];
479
+ getReport() {
480
+ return this.getParsedLogs().join('\r\n');
461
481
  }
462
482
 
463
- isWebRtc() {
464
- return this.getOptions().some(option => option[0] === 'webrtc' && option[1] === 'yes');
483
+ _catchConsole() {
484
+ this.oldConsoleMethods = {};
485
+ CONSOLE_METHODS.forEach((methodName ) => {
486
+ // eslint-disable-next-line
487
+ this.oldConsoleMethods[methodName] = console[methodName];
488
+ window.console[methodName] = (...args) => {
489
+ // Store message
490
+ try {
491
+ this.log(methodName, args.join(' '));
492
+ // Use old console method to log it normally
493
+ this.oldConsoleMethods[methodName].apply(null, args);
494
+ } catch (e) {
495
+ // Avoid circular structure issues
496
+ }
497
+ };
498
+ });
465
499
  }
466
500
 
467
- hasVideo() {
468
- const allow = this.getOptions().find(option => option[0] === 'allow');
469
- return Array.isArray(allow) && allow[1].split(',').some(codec => availableCodecs.some(c => c === codec));
470
- }
501
+ _sendToRemoteLogger(level , payload , retry = 0) {
502
+ if (!this.remoteClientConfiguration || retry >= MAX_REMOTE_RETRY) {
503
+ return;
504
+ }
471
505
 
472
- hasVideoConference() {
473
- return (
474
- this.getOptions().some(option => option[0] === 'max_audio_streams' && parseInt(option[1], 10) > 0)
475
- && this.getOptions().some(option => option[0] === 'max_video_streams' && parseInt(option[1], 10) > 1)
476
- );
506
+ const { tag, host, port, extra, level: minLevel } = this.remoteClientConfiguration;
507
+ if (!minLevel || this._isLevelAbove(minLevel, level)) {
508
+ return;
509
+ }
510
+
511
+ const isSecure = +port === 443;
512
+ const url = `http${isSecure ? 's' : ''}://${host}${isSecure ? '' : `:${port}`}/${tag}`;
513
+
514
+ realFetch()(url, {
515
+ method: 'POST',
516
+ headers: {
517
+ Accept: 'application/json',
518
+ 'Content-Type': 'application/json',
519
+ },
520
+ body: safeStringify({
521
+ level,
522
+ ...payload,
523
+ ...extra,
524
+ }),
525
+ }).catch(() => {
526
+ setTimeout(() => {
527
+ // eslint-disable-next-line no-underscore-dangle
528
+ payload._retry = retry + 1;
529
+ this._sendToRemoteLogger(level, payload, retry + 1);
530
+ }, 5000 + retry * 1000);
531
+ });
477
532
  }
478
533
 
479
- constructor({
480
- id,
481
- uuid,
482
- tenantUuid,
483
- username,
484
- secret,
485
- type,
486
- host,
487
- options,
488
- endpointSectionOptions,
489
- links,
490
- trunk,
491
- line,
492
- } = {}) {
493
- this.id = id;
494
- this.uuid = uuid;
495
- this.tenantUuid = tenantUuid;
496
- this.username = username;
497
- this.secret = secret;
498
- this.type = type;
499
- this.host = host;
500
- this.options = options;
501
- this.endpointSectionOptions = endpointSectionOptions;
502
- this.links = links;
503
- this.trunk = trunk;
504
- this.line = line;
534
+ _isLevelAbove(level1 , level2 ) {
535
+ const index1 = LOG_LEVELS.indexOf(level1);
536
+ const index2 = LOG_LEVELS.indexOf(level2);
537
+ if (index1 === -1 || index2 === -1) {
538
+ return false;
539
+ }
505
540
 
506
- // Useful to compare instead of instanceof with minified code
507
- this.type = 'SipLine';
541
+ return index1 > index2;
508
542
  }
509
- }
510
543
 
511
- //
544
+ _makeCategory(category ) {
545
+ return `${CATEGORY_PREFIX}${category}`;
546
+ }
547
+ }
512
548
 
513
- const STATE = {
514
- AVAILABLE: 'available',
515
- UNAVAILABLE: 'unavailable',
516
- INVISIBLE: 'invisible',
517
- DISCONNECTED: 'disconnected',
518
- AWAY: 'away',
519
- };
549
+ var IssueReporter$1 = new IssueReporter();
520
550
 
521
- const LINE_STATE = {
522
- AVAILABLE: 'available',
523
- HOLDING: 'holding',
524
- RINGING: 'ringing',
525
- TALKING: 'talking',
526
- UNAVAILABLE: 'unavailable',
527
- PROGRESSING: 'progressing',
528
- };
551
+ /* */
529
552
 
530
-
531
-
553
+
554
+
555
+
532
556
 
533
-
534
-
535
-
536
-
537
-
538
-
539
-
540
-
541
-
542
-
543
-
544
-
545
-
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
-
571
-
572
-
573
-
574
-
575
-
576
-
577
-
578
-
579
-
580
-
581
-
582
-
583
-
557
+
584
558
 
585
-
586
-
559
+
587
560
 
588
561
 
589
-
590
-
591
-
592
-
593
-
594
-
595
-
596
-
597
-
598
-
599
-
600
-
562
+ const methods = ['head', 'get', 'post', 'put', 'delete'];
563
+
564
+ const logger$1 = IssueReporter$1 ? IssueReporter$1.loggerFor('api') : console;
565
+
566
+ // Use a function here to be able to mock it in tests
567
+ const realFetch = () => {
568
+ if (typeof document !== 'undefined') {
569
+ // Browser
570
+ return window.fetch;
571
+ }
572
+
573
+ if (isMobile()) {
574
+ // React native
575
+ return fetch;
576
+ }
577
+
578
+ // nodejs
579
+ // this package is disable for react-native in package.json because it requires nodejs modules
580
+ // Used to trick the optimizer to avoid requiring `node-fetch/lib/index` directly
581
+ // It causes to require it on browsers when delivered by a nodejs engine (cf: vitejs).
582
+ try {
583
+ // $FlowFixMe
584
+ return require(Math.random() >= 0 ? 'node-fetch/lib/index' : '');
585
+ } catch (e) {
586
+ return fetch;
587
+ }
588
+ };
589
+
590
+ class ApiRequester {
601
591
 
602
-
603
-
604
-
605
592
 
606
-
607
-
608
-
609
-
610
-
611
-
612
-
613
-
614
-
615
-
616
-
617
-
618
- class Profile {
619
-
620
593
 
621
-
622
594
 
623
-
624
-
625
-
595
+
626
596
 
627
597
 
628
-
629
-
598
+
599
+
600
+
630
601
 
631
-
602
+
632
603
 
633
-
634
-
635
-
636
-
604
+
605
+
637
606
 
638
- static parse(plain ) {
639
- return new Profile({
640
- id: plain.uuid,
641
- firstName: plain.firstName || plain.firstname || '',
642
- lastName: plain.lastName || plain.lastname || '',
643
- email: plain.email,
644
- lines: plain.lines.map(line => Line.parse(line)),
645
- username: plain.username,
646
- mobileNumber: plain.mobile_phone_number || '',
647
- forwards: [
648
- ForwardOption.parse(plain.forwards.unconditional, FORWARD_KEYS.UNCONDITIONAL),
649
- ForwardOption.parse(plain.forwards.noanswer, FORWARD_KEYS.NO_ANSWER),
650
- ForwardOption.parse(plain.forwards.busy, FORWARD_KEYS.BUSY),
651
- ],
652
- doNotDisturb: plain.services.dnd.enabled,
653
- subscriptionType: plain.subscription_type,
654
- voicemail: plain.voicemail,
655
- switchboards: plain.switchboards || [],
656
- agent: plain.agent,
657
- status: '',
658
- callPickupTargetUsers: plain.call_pickup_target_users || [],
659
- onlineCallRecordEnabled: plain.online_call_record_enabled,
660
- });
607
+ // eslint-disable-next-line
608
+ static successResponseParser(response , isJson ) {
609
+ return response.status === 204 || response.status === 201 || response.status === 200;
661
610
  }
662
611
 
663
- static newFrom(profile ) {
664
- return newFrom(profile, Profile);
612
+ static defaultParser(response ) {
613
+ return response.json().then((data ) => data);
665
614
  }
666
615
 
667
- constructor({
668
- id,
669
- firstName,
670
- lastName,
671
- email,
672
- lines,
673
- username,
674
- mobileNumber,
675
- forwards,
676
- doNotDisturb,
677
- state,
678
- subscriptionType,
679
- voicemail,
680
- switchboards,
681
- agent,
682
- status,
683
- sipLines,
684
- callPickupTargetUsers,
685
- onlineCallRecordEnabled,
686
- } = {}) {
687
- this.id = id;
688
- this.firstName = firstName;
689
- this.lastName = lastName;
690
- this.email = email;
691
- this.lines = lines;
692
- this.username = username;
693
- this.mobileNumber = mobileNumber;
694
- this.forwards = forwards;
695
- this.doNotDisturb = doNotDisturb;
696
- this.state = state;
697
- this.voicemail = voicemail;
698
- this.subscriptionType = subscriptionType;
699
- this.switchboards = switchboards;
700
- this.agent = agent;
701
- this.status = status;
702
- this.callPickupTargetUsers = callPickupTargetUsers;
703
- this.onlineCallRecordEnabled = onlineCallRecordEnabled;
616
+ static getQueryString(obj ) {
617
+ return Object.keys(obj)
618
+ .filter(key => obj[key])
619
+ .map(key => `${key}=${encodeURIComponent(obj[key])}`)
620
+ .join('&');
621
+ }
704
622
 
705
- this.sipLines = sipLines || [];
623
+ static base64Encode(str ) {
624
+ return typeof btoa !== 'undefined' ? btoa(str) : jsBase64.Base64.encode(str);
706
625
  }
707
626
 
708
- static getLinesState(lines ) {
709
- let result = LINE_STATE.UNAVAILABLE;
627
+ // @see https://github.com/facebook/flow/issues/183#issuecomment-358607052
628
+ constructor({ server, refreshTokenCallback, clientId, agent = null, token = null, fetchOptions } ) {
629
+ this.server = server;
630
+ this.agent = agent;
631
+ this.clientId = clientId;
632
+ this.refreshTokenCallback = refreshTokenCallback;
633
+ this.refreshTokenPromise = null;
634
+ this.fetchOptions = fetchOptions || {};
635
+ if (token) {
636
+ this.token = token;
637
+ }
710
638
 
711
- // eslint-disable-next-line
712
- for (const line of lines) {
713
- if (line.state === LINE_STATE.RINGING) {
714
- result = LINE_STATE.RINGING;
715
- break;
716
- }
639
+ this.shouldLogErrors = true;
717
640
 
718
- if (line.state === LINE_STATE.TALKING) {
719
- result = LINE_STATE.TALKING;
720
- break;
721
- }
641
+ methods.forEach(method => {
642
+ // $FlowFixMe
643
+ ApiRequester.prototype[method] = function sugar(...args) {
644
+ // Add method in arguments passed to `call`
645
+ args.splice(1, 0, method);
722
646
 
723
- if (line.state === LINE_STATE.AVAILABLE) {
724
- result = LINE_STATE.AVAILABLE;
725
- }
726
- }
647
+ return this.call.call(this, ...args);
648
+ };
649
+ });
650
+ }
727
651
 
728
- return result;
652
+ setTenant(tenant ) {
653
+ this.tenant = tenant;
729
654
  }
730
655
 
731
- hasId(id ) {
732
- return id === this.id;
656
+ setToken(token ) {
657
+ this.token = token;
733
658
  }
734
659
 
735
- setMobileNumber(number ) {
736
- this.mobileNumber = number;
660
+ setFetchOptions(options ) {
661
+ this.fetchOptions = options;
662
+ }
737
663
 
738
- return this;
664
+ disableErrorLogging() {
665
+ this.shouldLogErrors = false;
739
666
  }
740
667
 
741
- setForwardOption(forwardOption ) {
742
- const updatedForwardOptions = this.forwards.slice();
743
- const index = updatedForwardOptions.findIndex(forward => forward.is(forwardOption));
744
- updatedForwardOptions.splice(index, 1, forwardOption);
668
+ enableErrorLogging() {
669
+ this.shouldLogErrors = true;
670
+ }
745
671
 
746
- this.forwards = updatedForwardOptions;
672
+ async call(
673
+ path ,
674
+ method = 'get',
675
+ body = null,
676
+ headers = null,
677
+ parse = ApiRequester.defaultParser,
678
+ firstCall = true,
679
+ ) {
680
+ const url = this.computeUrl(method, path, body);
681
+ const newHeaders = this.getHeaders(headers);
682
+ let newBody = method === 'get' ? null : body;
683
+ if (newBody && newHeaders['Content-Type'] === 'application/json') {
684
+ newBody = JSON.stringify(newBody);
685
+ }
686
+ const isHead = method === 'head';
687
+ const hasEmptyResponse = method === 'delete' || isHead;
688
+ const newParse = hasEmptyResponse ? ApiRequester.successResponseParser : parse;
747
689
 
748
- return this;
690
+ const fetchOptions = { ...this.fetchOptions || {} };
691
+ const extraHeaders = fetchOptions.headers || {};
692
+ delete fetchOptions.headers;
693
+
694
+ const options = {
695
+ method,
696
+ body: newBody,
697
+ headers: { ...this.getHeaders(headers), ...extraHeaders },
698
+ agent: this.agent,
699
+ ...fetchOptions,
700
+ };
701
+
702
+ if (this.refreshTokenPromise) {
703
+ logger$1.info('A token is already refreshing, waiting ...', { url });
704
+ await this.refreshTokenPromise;
705
+ }
706
+
707
+ const start = new Date();
708
+
709
+ return realFetch()(url, options).then(response => {
710
+ const contentType = response.headers.get('content-type') || '';
711
+ const isJson = contentType.indexOf('application/json') !== -1;
712
+
713
+ IssueReporter$1.logRequest(url, options, response, start);
714
+
715
+ // Throw an error only if status >= 400
716
+ if ((isHead && response.status >= 500) || (!isHead && response.status >= 400)) {
717
+ const promise = isJson ? response.json() : response.text();
718
+ const exceptionClass = response.status >= 500 ? ServerError : BadResponse;
719
+
720
+ return promise.then(async (err ) => {
721
+ // Check if the token is still valid
722
+ if (firstCall && this._checkTokenExpired(response, err)) {
723
+ logger$1.warn('token expired', { error: err.reason });
724
+ // Replay the call after refreshing the token
725
+ return this._replayWithNewToken(err, path, method, body, headers, parse);
726
+ }
727
+
728
+ const error = typeof err === 'string'
729
+ ? exceptionClass.fromText(err, response.status)
730
+ : exceptionClass.fromResponse(err, response.status);
731
+
732
+ if (this.shouldLogErrors) {
733
+ logger$1.error('API error', error);
734
+ }
735
+
736
+ throw error;
737
+ });
738
+ }
739
+
740
+ return newParse(response, isJson);
741
+ }).catch(error => {
742
+ if (this.shouldLogErrors) {
743
+ logger$1.error('Fetch failed', { url, options, message: error.message, stack: error.stack });
744
+ }
745
+
746
+ throw error;
747
+ });
749
748
  }
750
749
 
751
- setDoNotDisturb(enabled ) {
752
- this.doNotDisturb = enabled;
750
+ _checkTokenExpired(response , err ) {
751
+ // Special case when authenticating form a token: we got a 404
752
+ const isTokenNotFound = response.status === 404 && this._isTokenNotFound(err);
753
753
 
754
- return this;
754
+ return response.status === 401 || isTokenNotFound;
755
755
  }
756
756
 
757
- setState(state ) {
758
- this.state = state;
757
+ _isTokenNotFound(err ) {
758
+ return err && err.reason && err.reason[0] === 'No such token';
759
+ }
759
760
 
760
- return this;
761
+ _replayWithNewToken(
762
+ err ,
763
+ path ,
764
+ method ,
765
+ body = null,
766
+ headers = null,
767
+ parse ,
768
+ ) {
769
+ const isTokenNotFound = this._isTokenNotFound(err);
770
+ let newPath = path;
771
+ logger$1.info('refreshing token', { inProgress: !!this.refreshTokenPromise });
772
+
773
+ this.refreshTokenPromise = this.refreshTokenPromise || this.refreshTokenCallback();
774
+
775
+ return this.refreshTokenPromise.then(() => {
776
+ this.refreshTokenPromise = null;
777
+ logger$1.info('token refreshed', { isTokenNotFound });
778
+ if (isTokenNotFound) {
779
+ const pathParts = path.split('/');
780
+ pathParts.pop();
781
+ pathParts.push(this.token);
782
+ newPath = pathParts.join('/');
783
+ }
784
+
785
+ return this.call(newPath, method, body, headers, parse, false);
786
+ }).catch((e) => {
787
+ this.refreshTokenPromise = null;
788
+ throw e;
789
+ });
790
+ }
791
+
792
+ getHeaders(header ) {
793
+ if (header instanceof Object) {
794
+ return header;
795
+ }
796
+
797
+ return {
798
+ 'X-Auth-Token': this.token,
799
+ ...(this.tenant ? { 'Wazo-Tenant': this.tenant } : null),
800
+ Accept: 'application/json',
801
+ 'Content-Type': 'application/json',
802
+ };
803
+ }
804
+
805
+ computeUrl(method , path , body ) {
806
+ const url = `${this.baseUrl}/${path}`;
807
+
808
+ return method === 'get' && body && Object.keys(body).length ? `${url}?${ApiRequester.getQueryString(body)}` : url;
809
+ }
810
+
811
+ get baseUrl() {
812
+ return `https://${this.server}/api`;
761
813
  }
762
814
  }
763
815
 
764
816
  //
765
-
766
817
 
767
- const BACKEND = {
768
- OFFICE365: 'office365',
769
- PERSONAL: 'personal',
770
- GOOGLE: 'google',
771
- WAZO: 'wazo',
772
- };
818
+ const swarmPublicKey = `-----BEGIN PUBLIC KEY-----
819
+ MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmkXOuNfY8u5xoTiIhkb8
820
+ djnbIwG/Wrz3vpo8BZir8L5e1a1nSy740qBjP7ZINBQoALDhFfmdOfJnCyEGiHuz
821
+ ZW6jbG6C3PryE3Bu6GKwqSmD6k3q4Zk27fpwYAnNl+rWhYYM563rJZBda/INyHNN
822
+ pK7M1mixWi7gNdjjXwoEXSSBx+VpYMkY6LiAB2mvHXTY9M1qI14dvgGoQISZQoKi
823
+ NMTRCg5UP2ic0Dd9nSz/XpcOxGfa+0fwIl1F7RC1tJXOqvkGGPTOV4LLfg/Yta3h
824
+ nUPX9EZZDIX6vO/0IBV1LzjSl2A1bYFYAjJfowv3i1CpvONBOClHjSY5t9Y8MH6p
825
+ BwIDAQAB
826
+ -----END PUBLIC KEY-----`;
773
827
 
774
-
775
-
776
-
777
-
778
-
779
-
780
-
781
-
828
+ //
829
+
830
+
831
+
782
832
 
783
-
784
-
833
+
834
+
785
835
 
786
836
 
787
-
788
-
789
-
790
-
791
-
792
-
793
-
794
-
795
837
 
796
-
797
-
798
-
838
+
839
+
840
+
799
841
 
800
842
 
801
-
843
+
844
+
845
+
846
+
802
847
 
803
-
804
-
805
-
806
-
807
-
808
-
809
848
 
810
-
811
-
812
-
813
-
814
-
815
-
816
-
817
-
818
-
819
-
820
-
821
-
822
-
823
-
824
849
 
825
850
 
826
-
827
-
828
-
829
-
830
851
 
831
-
852
+
832
853
 
833
-
834
-
835
-
854
+
836
855
 
837
-
838
-
839
-
840
-
841
-
842
-
843
-
844
-
856
+
845
857
 
846
858
 
847
- // @see: https://github.com/rt2zz/react-native-contacts#example-contact-record
848
-
849
-
850
-
851
-
852
-
853
-
854
-
855
-
856
-
857
-
858
-
859
+ class Line {
859
860
 
860
-
861
-
862
-
863
-
864
-
865
-
866
-
867
-
868
-
869
-
870
-
871
-
872
-
873
-
874
-
875
-
876
-
877
-
878
-
861
+
879
862
 
880
-
881
-
882
-
883
-
884
-
863
+
864
+
865
+
866
+
867
+
868
+ static parse(plain ) {
869
+ return new Line({
870
+ id: plain.id,
871
+ extensions: plain.extensions,
872
+ endpointCustom: plain.endpoint_custom || null,
873
+ endpointSccp: plain.endpoint_sccp || null,
874
+ endpointSip: plain.endpoint_sip || null,
875
+ });
876
+ }
877
+
878
+ static newFrom(profile ) {
879
+ return newFrom(profile, Line);
880
+ }
881
+
882
+ is(line ) {
883
+ return this.id === line.id;
884
+ }
885
+
886
+ hasExtension(extension ) {
887
+ if (!this.extensions) {
888
+ return false;
889
+ }
890
+
891
+ return this.extensions.some(ext => ext.exten === extension);
892
+ }
893
+
894
+ constructor({ id, extensions, endpointCustom, endpointSccp, endpointSip } = {}) {
895
+ this.id = id;
896
+ this.extensions = extensions;
897
+ this.endpointCustom = endpointCustom || null;
898
+ this.endpointSccp = endpointSccp || null;
899
+ this.endpointSip = endpointSip || null;
900
+
901
+ // Useful to compare instead of instanceof with minified code
902
+ this.type = 'Line';
903
+ }
904
+ }
905
+
906
+ //
885
907
 
886
-
887
-
888
-
889
-
890
-
891
-
892
-
893
908
 
894
-
895
-
896
-
897
-
898
-
899
909
 
900
-
901
-
902
-
903
-
904
-
905
-
906
-
907
-
908
-
909
910
 
910
-
911
-
911
+
912
+
913
+ const FORWARD_KEYS = {
914
+ BUSY: 'busy',
915
+ NO_ANSWER: 'noanswer',
916
+ UNCONDITIONAL: 'unconditional',
917
+ };
918
+
919
+
912
920
 
913
-
914
-
915
921
 
922
+
916
923
 
917
924
 
918
-
919
-
920
-
921
-
922
-
923
-
924
-
925
-
926
-
927
-
928
-
929
-
925
+ class ForwardOption {
930
926
 
931
-
932
-
933
-
934
-
935
927
 
936
-
928
+
929
+
930
+ static parse(plain , key ) {
931
+ return new ForwardOption({
932
+ destination: plain.destination || '',
933
+ enabled: plain.enabled,
934
+ key,
935
+ });
936
+ }
937
+
938
+ static newFrom(profile ) {
939
+ return newFrom(profile, ForwardOption);
940
+ }
941
+
942
+ constructor({ destination, enabled, key } = {}) {
943
+ this.destination = destination;
944
+ this.enabled = enabled;
945
+ this.key = key;
946
+ }
947
+
948
+ setDestination(number ) {
949
+ this.destination = number;
950
+
951
+ return this;
952
+ }
953
+
954
+ setEnabled(enabled ) {
955
+ this.enabled = enabled;
956
+
957
+ return this;
958
+ }
959
+
960
+ is(other ) {
961
+ return this.key === other.key;
962
+ }
963
+ }
964
+
965
+ //
966
+
967
+
968
+ const availableCodecs = ['vp8', 'vp9', 'h264'];
969
+
970
+
937
971
 
938
-
939
-
940
-
941
-
942
972
 
943
-
944
973
 
945
-
946
-
947
-
948
-
949
-
950
-
951
-
952
-
953
-
954
-
955
-
956
974
 
975
+
976
+
977
+
978
+
979
+
980
+
981
+
982
+
983
+
957
984
 
958
985
 
986
+
987
+
988
+
959
989
 
960
-
961
-
962
-
963
990
 
964
-
991
+
965
992
 
966
-
967
-
968
-
969
-
970
-
971
993
 
972
-
973
-
974
-
994
+
995
+
996
+
997
+
998
+
975
999
 
976
1000
 
977
-
1001
+ class SipLine {
1002
+
1003
+
1004
+
978
1005
 
979
-
1006
+
980
1007
 
981
-
982
-
983
-
984
-
985
- const SOURCE_MOBILE = 'mobile';
986
-
987
- class Contact {
988
1008
 
989
-
990
-
991
-
992
-
993
-
994
-
995
-
1009
+
1010
+
1011
+
996
1012
 
997
-
998
-
999
-
1000
-
1001
-
1002
-
1003
-
1004
1013
 
1005
-
1006
-
1007
-
1008
-
1009
-
1010
-
1011
-
1012
-
1013
-
1014
-
1015
-
1016
-
1017
-
1018
-
1019
-
1020
- static merge(oldContacts , newContacts ) {
1021
- return newContacts.map(current => {
1022
- const old = oldContacts.find(contact => contact && contact.is(current));
1023
1014
 
1024
- return old ? current.merge(old) : current;
1025
- });
1026
- }
1015
+ static parse(plain ) {
1016
+ let { username, secret, host } = plain;
1027
1017
 
1028
- static sortContacts(a , b ) {
1029
- const aNames = a.separateName();
1030
- const bNames = b.separateName();
1031
- const aLastName = aNames.lastName;
1032
- const bLastName = bNames.lastName;
1033
-
1034
- // last Name can be empty
1035
- if (aLastName === bLastName) {
1036
- return aNames.firstName.localeCompare(bNames.firstName);
1037
- }
1038
-
1039
- return aLastName.localeCompare(bLastName);
1040
- }
1041
-
1042
- static parseMany(response ) {
1043
- return response.results.map(r => Contact.parse(r, response.column_types));
1044
- }
1045
-
1046
- static manyGraphQlWithNumbersParser(numbers ) {
1047
- return (response ) => {
1048
- if (!response.data || !response.data.me || !response.data.me.contacts) {
1049
- return [];
1050
- }
1051
-
1052
- return response.data.me.contacts.edges.map((edge, i) => {
1053
- if (!edge.node) {
1054
- return null;
1055
- }
1056
-
1057
- const { email } = edge.node;
1058
- const name = edge.node.firstname && edge.node.lastname
1059
- ? `${edge.node.firstname || ''} ${edge.node.lastname || ''}` : edge.node.wazoReverse;
1060
-
1061
- return new Contact({
1062
- name,
1063
- number: numbers[i],
1064
- numbers: [{ label: 'primary', number: numbers[i] }],
1065
- backend: edge.node.wazoBackend,
1066
- source: edge.node.wazoSourceName,
1067
- sourceId: edge.node.wazoSourceEntryId || '',
1068
- email: email || '',
1069
- emails: email ? [{ label: 'primary', email }] : [],
1070
- uuid: edge.node.userUuid,
1071
- });
1072
- }).filter(contact => !!contact);
1073
- };
1074
- }
1075
-
1076
- static fetchNumbers(plain , columns ) {
1077
- const numberColumns = columns
1078
- .map((e, index) => ({ index, columnName: e }))
1079
- .filter(e => e.columnName === 'number' || e.columnName === 'callable')
1080
- .map(e => e.index);
1081
-
1082
- return plain.column_values.filter((e, index) => numberColumns.some(i => i === index) && e !== null);
1083
- }
1084
-
1085
- static parse(plain , columns ) {
1086
- const numbers = Contact.fetchNumbers(plain, columns);
1087
- const email = plain.column_values[columns.indexOf('email')];
1088
-
1089
- return new Contact({
1090
- name: plain.column_values[columns.indexOf('name')],
1091
- number: numbers.length ? numbers[0] : '',
1092
- numbers: numbers.map((number, i) => ({ label: i === 0 ? 'primary' : 'secondary', number })),
1093
- favorited: plain.column_values[columns.indexOf('favorite')],
1094
- email: email || '',
1095
- emails: email ? [{ label: 'primary', email }] : [],
1096
- entreprise: plain.column_values[columns.indexOf('entreprise')] || '',
1097
- birthday: plain.column_values[columns.indexOf('birthday')] || '',
1098
- address: plain.column_values[columns.indexOf('address')] || '',
1099
- note: plain.column_values[columns.indexOf('note')] || '',
1100
- endpointId: plain.relations.endpoint_id,
1101
- personal: plain.column_values[columns.indexOf('personal')],
1102
- source: plain.source,
1103
- sourceId: plain.relations.source_entry_id || '',
1104
- uuid: plain.relations.user_uuid,
1105
- backend: plain.backend || '',
1106
- });
1107
- }
1108
-
1109
- static parseManyPersonal(results ) {
1110
- return results.map(r => Contact.parsePersonal(r));
1111
- }
1112
-
1113
- static parsePersonal(plain ) {
1114
- return new Contact({
1115
- name: `${plain.firstName || plain.firstname || ''} ${plain.lastName || plain.lastname || ''}`,
1116
- number: plain.number || '',
1117
- numbers: plain.number ? [{ label: 'primary', number: plain.number }] : [],
1118
- email: plain.email || '',
1119
- emails: plain.email ? [{ label: 'primary', email: plain.email }] : [],
1120
- source: 'personal',
1121
- sourceId: plain.id || '',
1122
- entreprise: plain.entreprise || '',
1123
- birthday: plain.birthday || '',
1124
- address: plain.address || '',
1125
- note: plain.note || '',
1126
- favorited: plain.favorited,
1127
- personal: true,
1128
- backend: plain.backend || BACKEND.PERSONAL,
1129
- });
1130
- }
1131
-
1132
- static parseMobile(plain ) {
1133
- let address = '';
1134
- if (plain.postalAddresses.length) {
1135
- const postalAddress = plain.postalAddresses[0];
1136
-
1137
- address = `${postalAddress.street} ${postalAddress.city} ${postalAddress.postCode} ${postalAddress.country}`;
1138
- }
1139
-
1140
- return new Contact({
1141
- name: `${plain.givenName || ''} ${plain.familyName || ''}`,
1142
- number: plain.phoneNumbers.length ? plain.phoneNumbers[0].number : '',
1143
- numbers: plain.phoneNumbers.length ? [{ label: 'primary', number: plain.phoneNumbers[0].number }] : [],
1144
- email: plain.emailAddresses.length ? plain.emailAddresses[0].email : '',
1145
- emails: plain.emailAddresses.length ? [{ label: 'primary', email: plain.emailAddresses[0].email }] : [],
1146
- source: SOURCE_MOBILE,
1147
- sourceId: plain.recordID || '',
1148
- birthday: plain.birthday ? `${plain.birthday.year}-${plain.birthday.month}-${plain.birthday.day}` : '',
1149
- address,
1150
- note: plain.note || '',
1151
- favorited: false,
1152
- personal: true,
1153
- });
1154
- }
1155
-
1156
- static parseManyOffice365(response , source ) {
1157
- return response.map(r => Contact.parseOffice365(r, source));
1158
- }
1159
-
1160
- static parseOffice365(single , source ) {
1161
- const emails = [];
1162
- const numbers = [];
1163
-
1164
- if (single.emailAddresses) {
1165
- single.emailAddresses.map(email => emails.push({ email: email.address }));
1166
- }
1167
-
1168
- if (single.businessPhones) {
1169
- single.businessPhones.map(phone => numbers.push({ label: 'business', number: phone }));
1170
- }
1018
+ // Since 20.13 engine so options are now in section
1019
+ if (plain.auth_section_options) {
1020
+ // $FlowFixMe
1021
+ const findOption = (options , name ) => options.find(option => option[0] === name);
1171
1022
 
1172
- if (single.mobilePhone) {
1173
- numbers.push({ label: 'mobile', number: single.mobilePhone });
1174
- }
1023
+ const usernameOption = findOption(plain.auth_section_options, 'username');
1024
+ const secretOption = findOption(plain.auth_section_options, 'password');
1025
+ const hostOption = findOption(plain.endpoint_section_options, 'media_address');
1175
1026
 
1176
- if (single.homePhones) {
1177
- single.homePhones.map(phone => numbers.push({ label: 'home', number: phone }));
1027
+ username = usernameOption ? usernameOption[1] : '';
1028
+ secret = secretOption ? secretOption[1] : '';
1029
+ host = hostOption ? hostOption[1] : '';
1178
1030
  }
1179
1031
 
1180
- return new Contact({
1181
- sourceId: single.id || '',
1182
- name: single.displayName,
1183
- number: numbers.length ? numbers[0].number : '',
1184
- numbers,
1185
- emails,
1186
- source: source.name,
1187
- backend: BACKEND.OFFICE365,
1032
+ return new SipLine({
1033
+ id: plain.id,
1034
+ uuid: plain.uuid,
1035
+ tenantUuid: plain.tenant_uuid,
1036
+ username,
1037
+ secret,
1038
+ type: plain.type,
1039
+ host,
1040
+ options: plain.options,
1041
+ endpointSectionOptions: plain.endpoint_section_options,
1042
+ links: plain.links,
1043
+ trunk: plain.trunk,
1044
+ line: plain.line,
1188
1045
  });
1189
1046
  }
1190
1047
 
1191
- static parseManyGoogle(response , source ) {
1192
- return response.map(r => Contact.parseGoogle(r, source));
1048
+ static newFrom(sipLine ) {
1049
+ return newFrom(sipLine, SipLine);
1193
1050
  }
1194
1051
 
1195
- static parseGoogle(single , source ) {
1196
- const emails = [];
1197
- const numbers = [];
1198
-
1199
- if (single.emails) {
1200
- single.emails.forEach(email =>
1201
- (typeof email === 'object' ? { email: email.address, label: email.label } : { email }));
1202
- }
1203
-
1204
- if (single.numbers_by_label) {
1205
- Object.keys(single.numbers_by_label).forEach(label =>
1206
- numbers.push({ label, number: single.numbers_by_label[label] }));
1207
- } else if (single.numbers) {
1208
- single.numbers.forEach(phone => numbers.push({ number: phone }));
1052
+ is(line ) {
1053
+ if (line.uuid) {
1054
+ return this.uuid === line.uuid;
1209
1055
  }
1210
-
1211
- return new Contact({
1212
- sourceId: single.id || '',
1213
- name: single.name,
1214
- number: numbers.length ? numbers[0].number : '',
1215
- numbers,
1216
- emails,
1217
- source: source.name,
1218
- backend: BACKEND.GOOGLE,
1219
- });
1220
- }
1221
-
1222
- static parseManyWazo(response , source ) {
1223
- return response.map(r => Contact.parseWazo(r, source));
1056
+ return this.id === line.id;
1224
1057
  }
1225
1058
 
1226
- static parseWazo(single , source ) {
1227
- const emails = [];
1228
- const numbers = [];
1229
-
1230
- if (single.email) {
1231
- emails.push({ label: 'email', email: single.email });
1232
- }
1233
-
1234
- if (single.exten) {
1235
- numbers.push({ label: 'exten', number: single.exten });
1236
- }
1237
-
1238
- if (single.mobile_phone_number) {
1239
- numbers.push({ label: 'mobile', number: single.mobile_phone_number });
1240
- }
1241
-
1242
- return new Contact({
1243
- uuid: single.uuid,
1244
- sourceId: String(single.id) || '',
1245
- name: `${single.firstname}${single.lastname ? ` ${single.lastname}` : ''}`,
1246
- number: numbers.length ? numbers[0].number : '',
1247
- numbers,
1248
- emails,
1249
- source: source.name,
1250
- backend: BACKEND.WAZO,
1251
- });
1059
+ getOptions() {
1060
+ // `options` params have beend remove since 20.13... we should now use endpointSectionOptions
1061
+ return this.endpointSectionOptions || this.options || [];
1252
1062
  }
1253
1063
 
1254
- static parseManyConference(response , source ) {
1255
- return response.map(r => Contact.parseConference(r, source));
1064
+ isWebRtc() {
1065
+ return this.getOptions().some(option => option[0] === 'webrtc' && option[1] === 'yes');
1256
1066
  }
1257
1067
 
1258
- static parseConference(single , source ) {
1259
- const numbers = [];
1260
- let firstNumber = '';
1261
-
1262
- if (single && single.extensions && single.extensions.length > 0 && single.extensions[0].exten) {
1263
- firstNumber = single.extensions[0].exten;
1264
- numbers.push({ label: 'exten', number: firstNumber });
1265
- }
1266
-
1267
- return new Contact({
1268
- sourceId: String(single.id),
1269
- name: single.name,
1270
- number: firstNumber,
1271
- numbers,
1272
- source: source.name,
1273
- backend: 'conference',
1274
- });
1068
+ hasVideo() {
1069
+ const allow = this.getOptions().find(option => option[0] === 'allow');
1070
+ return Array.isArray(allow) && allow[1].split(',').some(codec => availableCodecs.some(c => c === codec));
1275
1071
  }
1276
1072
 
1277
- static newFrom(contact ) {
1278
- return newFrom(contact, Contact);
1073
+ hasVideoConference() {
1074
+ return (
1075
+ this.getOptions().some(option => option[0] === 'max_audio_streams' && parseInt(option[1], 10) > 0)
1076
+ && this.getOptions().some(option => option[0] === 'max_video_streams' && parseInt(option[1], 10) > 1)
1077
+ );
1279
1078
  }
1280
1079
 
1281
1080
  constructor({
1282
1081
  id,
1283
1082
  uuid,
1284
- name,
1285
- number,
1286
- numbers,
1287
- email,
1288
- emails,
1289
- source,
1290
- sourceId,
1291
- entreprise,
1292
- birthday,
1293
- address,
1294
- note,
1295
- state,
1296
- lineState,
1297
- lastActivity,
1298
- mobile,
1299
- status,
1300
- endpointId,
1301
- personal,
1302
- favorited,
1303
- backend,
1304
- personalStatus,
1305
- sessions,
1306
- connected,
1307
- doNotDisturb,
1308
- ringing,
1309
- previousPresential,
1310
- lines,
1083
+ tenantUuid,
1084
+ username,
1085
+ secret,
1086
+ type,
1087
+ host,
1088
+ options,
1089
+ endpointSectionOptions,
1090
+ links,
1091
+ trunk,
1092
+ line,
1311
1093
  } = {}) {
1312
1094
  this.id = id;
1313
1095
  this.uuid = uuid;
1314
- this.name = name;
1315
- this.number = number;
1316
- this.numbers = numbers;
1317
- this.email = email;
1318
- this.emails = emails;
1319
- this.source = source;
1320
- this.sourceId = sourceId;
1321
- this.entreprise = entreprise;
1322
- this.birthday = birthday;
1323
- this.address = address;
1324
- this.note = note;
1325
- this.state = state;
1326
- this.lineState = lineState;
1327
- this.lastActivity = lastActivity;
1328
- this.mobile = mobile;
1329
- this.status = status;
1330
- this.endpointId = endpointId;
1331
- this.personal = personal;
1332
- this.favorited = favorited;
1333
- this.backend = backend;
1334
- this.personalStatus = personalStatus || '';
1335
- this.sessions = sessions || [];
1336
- this.connected = connected;
1337
- this.doNotDisturb = doNotDisturb;
1338
- this.ringing = ringing;
1339
- this.previousPresential = previousPresential;
1340
- this.lines = lines || [];
1096
+ this.tenantUuid = tenantUuid;
1097
+ this.username = username;
1098
+ this.secret = secret;
1099
+ this.type = type;
1100
+ this.host = host;
1101
+ this.options = options;
1102
+ this.endpointSectionOptions = endpointSectionOptions;
1103
+ this.links = links;
1104
+ this.trunk = trunk;
1105
+ this.line = line;
1341
1106
 
1342
1107
  // Useful to compare instead of instanceof with minified code
1343
- this.type = 'Contact';
1108
+ this.type = 'SipLine';
1344
1109
  }
1110
+ }
1345
1111
 
1346
- setFavorite(value ) {
1347
- this.favorited = value;
1112
+ //
1348
1113
 
1349
- return this;
1350
- }
1114
+ const STATE = {
1115
+ AVAILABLE: 'available',
1116
+ UNAVAILABLE: 'unavailable',
1117
+ INVISIBLE: 'invisible',
1118
+ DISCONNECTED: 'disconnected',
1119
+ AWAY: 'away',
1120
+ };
1351
1121
 
1352
- is(other ) {
1353
- const sameSourceId = this.sourceId !== null && other.sourceId !== null && this.sourceId === other.sourceId;
1354
- const sameUuid = !!this.uuid && !!other.uuid && this.uuid === other.uuid;
1122
+ const LINE_STATE = {
1123
+ AVAILABLE: 'available',
1124
+ HOLDING: 'holding',
1125
+ RINGING: 'ringing',
1126
+ TALKING: 'talking',
1127
+ UNAVAILABLE: 'unavailable',
1128
+ PROGRESSING: 'progressing',
1129
+ };
1355
1130
 
1356
- const hasSameId = sameSourceId || sameUuid;
1357
- const hasSameBackend = !!this.backend && !!other.backend && this.backend === other.backend;
1358
- const hasSameSource = !!this.source && !!other.source && this.source === other.source;
1131
+
1132
+
1133
+
1134
+
1135
+
1136
+
1137
+
1138
+
1139
+
1140
+
1141
+
1142
+
1143
+
1144
+
1145
+
1146
+
1147
+
1148
+
1149
+
1150
+
1151
+
1152
+
1153
+
1154
+
1155
+
1156
+
1157
+
1158
+
1159
+
1160
+
1161
+
1162
+
1163
+
1164
+
1165
+
1166
+
1167
+
1168
+
1169
+
1170
+
1171
+
1172
+
1173
+
1174
+
1175
+
1176
+
1177
+
1178
+
1179
+
1180
+
1181
+
1182
+
1183
+
1184
+
1185
+
1186
+
1187
+
1188
+
1359
1189
 
1360
- return !!other && hasSameId && hasSameBackend && hasSameSource;
1190
+
1191
+
1192
+
1193
+
1194
+
1195
+
1196
+
1197
+
1198
+
1199
+
1200
+
1201
+
1202
+
1203
+
1204
+
1205
+
1206
+
1207
+
1208
+
1209
+
1210
+
1211
+
1212
+
1213
+
1214
+
1215
+
1216
+
1217
+
1218
+
1219
+ class Profile {
1220
+
1221
+
1222
+
1223
+
1224
+
1225
+
1226
+
1227
+
1228
+
1229
+
1230
+
1231
+
1232
+
1233
+
1234
+
1235
+
1236
+
1237
+
1238
+
1239
+ static parse(plain ) {
1240
+ return new Profile({
1241
+ id: plain.uuid,
1242
+ firstName: plain.firstName || plain.firstname || '',
1243
+ lastName: plain.lastName || plain.lastname || '',
1244
+ email: plain.email,
1245
+ lines: plain.lines.map(line => Line.parse(line)),
1246
+ username: plain.username,
1247
+ mobileNumber: plain.mobile_phone_number || '',
1248
+ forwards: [
1249
+ ForwardOption.parse(plain.forwards.unconditional, FORWARD_KEYS.UNCONDITIONAL),
1250
+ ForwardOption.parse(plain.forwards.noanswer, FORWARD_KEYS.NO_ANSWER),
1251
+ ForwardOption.parse(plain.forwards.busy, FORWARD_KEYS.BUSY),
1252
+ ],
1253
+ doNotDisturb: plain.services.dnd.enabled,
1254
+ subscriptionType: plain.subscription_type,
1255
+ voicemail: plain.voicemail,
1256
+ switchboards: plain.switchboards || [],
1257
+ agent: plain.agent,
1258
+ status: '',
1259
+ callPickupTargetUsers: plain.call_pickup_target_users || [],
1260
+ onlineCallRecordEnabled: plain.online_call_record_enabled,
1261
+ });
1361
1262
  }
1362
1263
 
1363
- hasId(id ) {
1364
- return this.uuid === id;
1264
+ static newFrom(profile ) {
1265
+ return newFrom(profile, Profile);
1365
1266
  }
1366
1267
 
1367
- hasNumber(number ) {
1368
- return this.number === number;
1369
- }
1268
+ constructor({
1269
+ id,
1270
+ firstName,
1271
+ lastName,
1272
+ email,
1273
+ lines,
1274
+ username,
1275
+ mobileNumber,
1276
+ forwards,
1277
+ doNotDisturb,
1278
+ state,
1279
+ subscriptionType,
1280
+ voicemail,
1281
+ switchboards,
1282
+ agent,
1283
+ status,
1284
+ sipLines,
1285
+ callPickupTargetUsers,
1286
+ onlineCallRecordEnabled,
1287
+ } = {}) {
1288
+ this.id = id;
1289
+ this.firstName = firstName;
1290
+ this.lastName = lastName;
1291
+ this.email = email;
1292
+ this.lines = lines;
1293
+ this.username = username;
1294
+ this.mobileNumber = mobileNumber;
1295
+ this.forwards = forwards;
1296
+ this.doNotDisturb = doNotDisturb;
1297
+ this.state = state;
1298
+ this.voicemail = voicemail;
1299
+ this.subscriptionType = subscriptionType;
1300
+ this.switchboards = switchboards;
1301
+ this.agent = agent;
1302
+ this.status = status;
1303
+ this.callPickupTargetUsers = callPickupTargetUsers;
1304
+ this.onlineCallRecordEnabled = onlineCallRecordEnabled;
1370
1305
 
1371
- hasEndpointId(endpointId ) {
1372
- return this.endpointId === endpointId;
1306
+ this.sipLines = sipLines || [];
1373
1307
  }
1374
1308
 
1375
- isAvailable() {
1376
- return this.state === STATE.AVAILABLE;
1377
- }
1309
+ static getLinesState(lines ) {
1310
+ let result = LINE_STATE.UNAVAILABLE;
1378
1311
 
1379
- isAway() {
1380
- return this.state === STATE.AWAY;
1381
- }
1312
+ // eslint-disable-next-line
1313
+ for (const line of lines) {
1314
+ if (line.state === LINE_STATE.RINGING) {
1315
+ result = LINE_STATE.RINGING;
1316
+ break;
1317
+ }
1382
1318
 
1383
- isUnavailable() {
1384
- return this.state === STATE.UNAVAILABLE;
1385
- }
1319
+ if (line.state === LINE_STATE.TALKING) {
1320
+ result = LINE_STATE.TALKING;
1321
+ break;
1322
+ }
1386
1323
 
1387
- isInvisible() {
1388
- return this.state === STATE.INVISIBLE;
1389
- }
1324
+ if (line.state === LINE_STATE.AVAILABLE) {
1325
+ result = LINE_STATE.AVAILABLE;
1326
+ }
1327
+ }
1390
1328
 
1391
- isInCall() {
1392
- return this.lineState === LINE_STATE.TALKING || this.lineState === LINE_STATE.HOLDING;
1329
+ return result;
1393
1330
  }
1394
1331
 
1395
- isRinging() {
1396
- return this.ringing;
1332
+ hasId(id ) {
1333
+ return id === this.id;
1397
1334
  }
1398
1335
 
1399
- isInUseOrRinging() {
1400
- return this.lineState === LINE_STATE.TALKING || this.lineState === LINE_STATE.RINGING;
1401
- }
1336
+ setMobileNumber(number ) {
1337
+ this.mobileNumber = number;
1402
1338
 
1403
- isProgressing() {
1404
- return this.lineState === LINE_STATE.PROGRESSING;
1339
+ return this;
1405
1340
  }
1406
1341
 
1407
- merge(old ) {
1408
- Object.keys(old).filter(key => key !== 'lineState').forEach(key => {
1409
- // $FlowFixMe
1410
- this[key] = old[key] || this[key];
1411
- });
1342
+ setForwardOption(forwardOption ) {
1343
+ const updatedForwardOptions = this.forwards.slice();
1344
+ const index = updatedForwardOptions.findIndex(forward => forward.is(forwardOption));
1345
+ updatedForwardOptions.splice(index, 1, forwardOption);
1412
1346
 
1413
- if (old.lineState && !this.lineState) {
1414
- this.lineState = old.lineState;
1415
- }
1347
+ this.forwards = updatedForwardOptions;
1416
1348
 
1417
1349
  return this;
1418
1350
  }
1419
1351
 
1420
- isIntern() {
1421
- return !!this.uuid;
1422
- }
1423
-
1424
- isCallable(session ) {
1425
- return !!this.number && !!session && !session.is(this);
1426
- }
1427
-
1428
- isFromMobile() {
1429
- return this.source === SOURCE_MOBILE;
1430
- }
1352
+ setDoNotDisturb(enabled ) {
1353
+ this.doNotDisturb = enabled;
1431
1354
 
1432
- isFavorite() {
1433
- return this.favorited;
1355
+ return this;
1434
1356
  }
1435
1357
 
1436
- separateName() {
1437
- if (!this.name) {
1438
- return {
1439
- firstName: '',
1440
- lastName: '',
1441
- };
1442
- }
1443
- const names = this.name.split(/\s+/);
1444
- const firstName = names[0];
1445
- const lastName = names.slice(1).join(' ');
1358
+ setState(state ) {
1359
+ this.state = state;
1446
1360
 
1447
- return {
1448
- firstName,
1449
- lastName,
1450
- };
1361
+ return this;
1451
1362
  }
1452
1363
  }
1453
1364
 
1454
- // Can't use `semver` package as Wazo version aren't in semver format
1455
-
1456
- const compareVersions = (a, b) => {
1457
- let i;
1458
- let diff;
1459
- const regExStrip0 = /(\.0+)+$/;
1460
- const segmentsA = a.replace(regExStrip0, '').split('.');
1461
- const segmentsB = b.replace(regExStrip0, '').split('.');
1462
- const l = Math.min(segmentsA.length, segmentsB.length);
1365
+ //
1366
+
1463
1367
 
1464
- for (i = 0; i < l; i++) {
1465
- diff = parseInt(segmentsA[i], 10) - parseInt(segmentsB[i], 10);
1466
- if (diff) {
1467
- return diff;
1468
- }
1469
- }
1470
- return segmentsA.length - segmentsB.length;
1368
+ const BACKEND = {
1369
+ OFFICE365: 'office365',
1370
+ PERSONAL: 'personal',
1371
+ GOOGLE: 'google',
1372
+ WAZO: 'wazo',
1471
1373
  };
1472
1374
 
1473
- //
1375
+
1376
+
1377
+
1378
+
1379
+
1380
+
1381
+
1382
+
1383
+
1384
+
1385
+
1386
+
1474
1387
 
1475
- const swarmKey = jsrsasign.KEYUTIL.getKey(swarmPublicKey);
1476
- const MINIMUM_WAZO_ENGINE_VERSION_FOR_DEFAULT_CONTEXT = '19.08';
1388
+
1389
+
1390
+
1391
+
1392
+
1393
+
1394
+
1395
+
1396
+
1397
+
1398
+
1399
+
1400
+
1401
+
1402
+
1403
+
1404
+
1405
+
1406
+
1407
+
1477
1408
 
1409
+
1410
+
1411
+
1412
+
1413
+
1414
+
1478
1415
 
1416
+
1417
+
1418
+
1419
+
1420
+
1421
+
1422
+
1423
+
1424
+
1425
+
1426
+
1427
+
1428
+
1479
1429
 
1480
1430
 
1481
-
1482
-
1483
-
1484
-
1485
-
1486
-
1487
1431
 
1432
+
1433
+
1434
+
1435
+
1436
+
1437
+
1438
+
1439
+
1440
+
1441
+
1442
+
1443
+
1444
+
1445
+
1446
+
1447
+
1448
+ // @see: https://github.com/rt2zz/react-native-contacts#example-contact-record
1449
+
1450
+
1451
+
1452
+
1453
+
1454
+
1455
+
1488
1456
 
1457
+
1458
+
1459
+
1460
+
1489
1461
 
1490
-
1462
+
1491
1463
 
1464
+
1465
+
1466
+
1492
1467
 
1468
+
1469
+
1493
1470
 
1471
+
1472
+
1494
1473
 
1495
-
1496
-
1497
-
1498
-
1499
-
1474
+
1475
+
1500
1476
 
1501
-
1477
+
1478
+
1479
+
1480
+
1481
+
1482
+
1483
+
1502
1484
 
1503
1485
 
1504
1486
 
1487
+
1488
+
1489
+
1490
+
1491
+
1492
+
1505
1493
 
1506
-
1507
-
1494
+
1495
+
1496
+
1497
+
1498
+
1499
+
1500
+
1501
+
1502
+
1503
+
1504
+
1505
+
1506
+
1507
+
1508
+
1509
+
1510
+
1511
+
1512
+
1513
+
1514
+
1515
+
1508
1516
 
1509
1517
 
1510
1518
 
1519
+
1520
+
1521
+
1522
+
1523
+
1524
+
1511
1525
 
1512
-
1526
+
1527
+
1528
+
1529
+
1530
+
1531
+
1532
+
1533
+
1534
+
1513
1535
 
1536
+
1537
+
1538
+
1539
+
1540
+
1541
+
1542
+
1543
+
1544
+
1545
+
1514
1546
 
1547
+
1548
+
1515
1549
 
1516
1550
 
1517
-
1551
+
1552
+
1553
+
1554
+
1518
1555
 
1519
1556
 
1557
+
1558
+
1559
+
1560
+
1561
+
1562
+
1563
+
1564
+
1565
+
1566
+
1567
+
1568
+
1569
+
1570
+
1520
1571
 
1521
-
1522
-
1572
+
1573
+
1574
+
1575
+
1576
+
1577
+
1523
1578
 
1579
+
1580
+
1581
+
1582
+
1583
+
1524
1584
 
1525
1585
 
1526
- class Session {
1586
+ const SOURCE_MOBILE = 'mobile';
1587
+
1588
+ class Contact {
1589
+
1590
+
1591
+
1527
1592
 
1528
1593
 
1529
-
1594
+
1595
+
1596
+
1597
+
1598
+
1599
+
1600
+
1601
+
1530
1602
 
1531
1603
 
1604
+
1605
+
1606
+
1607
+
1608
+
1609
+
1610
+
1611
+
1612
+
1613
+
1614
+
1615
+
1532
1616
 
1533
-
1534
1617
 
1535
1618
 
1536
1619
 
1537
-
1538
-
1539
- static parse(plain ) {
1540
- const token = plain.data.metadata ? plain.data.metadata.jwt : null;
1541
- let authorizations = [];
1542
-
1543
- // Add authorizations from JWT
1544
- if (token) {
1545
- const isValid = jsrsasign.jws.JWS.verifyJWT(token, swarmKey, { alg: ['RS256'], verifyAt: new Date() });
1546
- if (isValid) {
1547
- const decodedToken = jsrsasign.jws.JWS.readSafeJSONString(jsrsasign.b64utoutf8(token.split('.')[1]));
1548
- authorizations = decodedToken ? decodedToken.authorizations : [];
1549
- }
1550
- }
1551
-
1552
- return new Session({
1553
- token: plain.data.token,
1554
- refreshToken: plain.data.refresh_token || null,
1555
- uuid: plain.data.metadata ? plain.data.metadata.uuid : null,
1556
- sessionUuid: plain.data.session_uuid,
1557
- authorizations,
1558
- acl: plain.data.acls ? plain.data.acls : plain.data.acl ? plain.data.acl : [],
1559
- tenantUuid: plain.data.metadata ? plain.data.metadata.tenant_uuid : undefined,
1560
- expiresAt: new Date(`${plain.data.utc_expires_at}z`),
1561
- engineUuid: plain.data.xivo_uuid,
1562
- });
1563
- }
1564
-
1565
- static newFrom(profile ) {
1566
- return newFrom(profile, Session);
1567
- }
1568
-
1569
- constructor({
1570
- token,
1571
- uuid,
1572
- tenantUuid,
1573
- profile,
1574
- expiresAt,
1575
- authorizations,
1576
- acl,
1577
- engineVersion,
1578
- refreshToken,
1579
- sessionUuid,
1580
- engineUuid,
1581
- } = {}) {
1582
- this.token = token;
1583
- this.uuid = uuid;
1584
- this.tenantUuid = tenantUuid || null;
1585
- this.profile = profile;
1586
- this.expiresAt = expiresAt;
1587
- this.authorizations = authorizations || [];
1588
- this.acl = acl || [];
1589
- this.engineVersion = engineVersion;
1590
- this.refreshToken = refreshToken;
1591
- this.sessionUuid = sessionUuid;
1592
- this.engineUuid = engineUuid;
1593
- }
1594
-
1595
- hasExpired(date = new Date()) {
1596
- return date >= this.expiresAt;
1597
- }
1598
-
1599
- is(contact ) {
1600
- return Boolean(contact) && this.uuid === contact.uuid;
1601
- }
1602
-
1603
- using(profile ) {
1604
- this.profile = profile;
1605
-
1606
- return this;
1607
- }
1608
-
1609
- hasAuthorizations() {
1610
- return this.authorizations && !!this.authorizations.length;
1611
- }
1612
-
1613
- displayName() {
1614
- return this.profile ? `${this.profile.firstName} ${this.profile.lastName}` : '';
1615
- }
1616
-
1617
- hasAccessToVoicemail() {
1618
- if (!this.profile) {
1619
- return false;
1620
- }
1621
- return !!this.profile.voicemail;
1622
- }
1623
-
1624
- primaryLine() {
1625
- return this.profile && this.profile.lines.length > 0 ? this.profile.lines[0] : null;
1626
- }
1627
-
1628
- primarySipLine() {
1629
- return this.profile && this.profile.sipLines.length > 0 ? this.profile.sipLines[0] : null;
1630
- }
1631
-
1632
- primaryWebRtcLine() {
1633
- return this.profile && (this.profile.sipLines || []).find(sipLine => sipLine && sipLine.isWebRtc());
1634
- }
1635
-
1636
- primaryCtiLine() {
1637
- return this.profile && (this.profile.sipLines || []).find(sipLine => sipLine && !sipLine.isWebRtc());
1638
- }
1639
-
1640
- primaryContext() {
1641
- if (this.engineVersion) {
1642
- if (this.hasEngineVersionGte(MINIMUM_WAZO_ENGINE_VERSION_FOR_DEFAULT_CONTEXT)) {
1643
- return 'default';
1644
- }
1645
- }
1646
1620
 
1647
- const line = this.primaryLine();
1621
+ static merge(oldContacts , newContacts ) {
1622
+ return newContacts.map(current => {
1623
+ const old = oldContacts.find(contact => contact && contact.is(current));
1648
1624
 
1649
- return line && line.extensions.length > 0 ? line.extensions[0].context : 'default';
1625
+ return old ? current.merge(old) : current;
1626
+ });
1650
1627
  }
1651
1628
 
1652
- hasEngineVersionGte(version ) {
1653
- return this.engineVersion && compareVersions(String(this.engineVersion), String(version)) >= 0;
1654
- }
1629
+ static sortContacts(a , b ) {
1630
+ const aNames = a.separateName();
1631
+ const bNames = b.separateName();
1632
+ const aLastName = aNames.lastName;
1633
+ const bLastName = bNames.lastName;
1655
1634
 
1656
- primaryNumber() {
1657
- const line = this.primaryLine();
1635
+ // last Name can be empty
1636
+ if (aLastName === bLastName) {
1637
+ return aNames.firstName.localeCompare(bNames.firstName);
1638
+ }
1658
1639
 
1659
- return line && line.extensions.length ? line.extensions[0].exten : null;
1640
+ return aLastName.localeCompare(bLastName);
1660
1641
  }
1661
1642
 
1662
- allLines() {
1663
- return this.profile ? this.profile.lines || [] : [];
1643
+ static parseMany(response ) {
1644
+ return response.results.map(r => Contact.parse(r, response.column_types));
1664
1645
  }
1665
1646
 
1666
- allNumbers() {
1667
- const extensions = this.allLines().map(line => line.extensions.map(extension => extension.exten));
1668
- if (!extensions.length) {
1669
- return [];
1670
- }
1647
+ static manyGraphQlWithNumbersParser(numbers ) {
1648
+ return (response ) => {
1649
+ if (!response.data || !response.data.me || !response.data.me.contacts) {
1650
+ return [];
1651
+ }
1671
1652
 
1672
- return extensions.reduce((a, b) => a.concat(b));
1673
- }
1653
+ return response.data.me.contacts.edges.map((edge, i) => {
1654
+ if (!edge.node) {
1655
+ return null;
1656
+ }
1674
1657
 
1675
- hasExtension(extension ) {
1676
- return this.allNumbers().some(number => number === extension);
1677
- }
1658
+ const { email } = edge.node;
1659
+ const name = edge.node.firstname && edge.node.lastname
1660
+ ? `${edge.node.firstname || ''} ${edge.node.lastname || ''}` : edge.node.wazoReverse;
1678
1661
 
1679
- get acls() {
1680
- console.warn('`acls` property of Session has been removed in Wazo\'s SDK, please use `acl` instead.');
1681
- return this.acl;
1662
+ return new Contact({
1663
+ name,
1664
+ number: numbers[i],
1665
+ numbers: [{ label: 'primary', number: numbers[i] }],
1666
+ backend: edge.node.wazoBackend,
1667
+ source: edge.node.wazoSourceName,
1668
+ sourceId: edge.node.wazoSourceEntryId || '',
1669
+ email: email || '',
1670
+ emails: email ? [{ label: 'primary', email }] : [],
1671
+ uuid: edge.node.userUuid,
1672
+ });
1673
+ }).filter(contact => !!contact);
1674
+ };
1682
1675
  }
1683
- }
1684
1676
 
1685
- //
1686
- /* eslint-disable no-param-reassign */
1677
+ static fetchNumbers(plain , columns ) {
1678
+ const numberColumns = columns
1679
+ .map((e, index) => ({ index, columnName: e }))
1680
+ .filter(e => e.columnName === 'number' || e.columnName === 'callable')
1681
+ .map(e => e.index);
1687
1682
 
1688
- var updateFrom = (instance , from ) => {
1689
- Object.keys(from).forEach(key => {
1690
- // $FlowFixMe
1691
- if (
1692
- (!instance[key] && from[key])
1693
- || (typeof instance[key] !== 'undefined' && typeof from[key] !== 'undefined' && instance[key] !== from[key])
1694
- ) {
1695
- // $FlowFixMe
1696
- instance[key] = from[key];
1697
- }
1698
- });
1699
- };
1683
+ return plain.column_values.filter((e, index) => numberColumns.some(i => i === index) && e !== null);
1684
+ }
1700
1685
 
1701
- /* */
1686
+ static parse(plain , columns ) {
1687
+ const numbers = Contact.fetchNumbers(plain, columns);
1688
+ const email = plain.column_values[columns.indexOf('email')];
1702
1689
 
1703
- class BadResponse extends Error {
1704
- static fromResponse(error , status ) {
1705
- return new BadResponse(
1706
- error.message || JSON.stringify(error),
1707
- status,
1708
- error.timestamp,
1709
- error.error_id,
1710
- error.details,
1711
- error,
1712
- );
1690
+ return new Contact({
1691
+ name: plain.column_values[columns.indexOf('name')],
1692
+ number: numbers.length ? numbers[0] : '',
1693
+ numbers: numbers.map((number, i) => ({ label: i === 0 ? 'primary' : 'secondary', number })),
1694
+ favorited: plain.column_values[columns.indexOf('favorite')],
1695
+ email: email || '',
1696
+ emails: email ? [{ label: 'primary', email }] : [],
1697
+ entreprise: plain.column_values[columns.indexOf('entreprise')] || '',
1698
+ birthday: plain.column_values[columns.indexOf('birthday')] || '',
1699
+ address: plain.column_values[columns.indexOf('address')] || '',
1700
+ note: plain.column_values[columns.indexOf('note')] || '',
1701
+ endpointId: plain.relations.endpoint_id,
1702
+ personal: plain.column_values[columns.indexOf('personal')],
1703
+ source: plain.source,
1704
+ sourceId: plain.relations.source_entry_id || '',
1705
+ uuid: plain.relations.user_uuid,
1706
+ backend: plain.backend || '',
1707
+ });
1713
1708
  }
1714
1709
 
1715
- static fromText(response , status ) {
1716
- return new BadResponse(response, status);
1710
+ static parseManyPersonal(results ) {
1711
+ return results.map(r => Contact.parsePersonal(r));
1717
1712
  }
1718
1713
 
1719
-
1720
-
1721
-
1722
-
1723
-
1724
-
1725
-
1726
- constructor(
1727
- message ,
1728
- status ,
1729
- timestamp = null,
1730
- errorId = null,
1731
- details = null,
1732
- error = null,
1733
- ) {
1734
- super(message);
1735
-
1736
- this.timestamp = timestamp;
1737
- this.status = status;
1738
- this.errorId = errorId;
1739
- this.details = details;
1740
- this.error = error;
1714
+ static parsePersonal(plain ) {
1715
+ return new Contact({
1716
+ name: `${plain.firstName || plain.firstname || ''} ${plain.lastName || plain.lastname || ''}`,
1717
+ number: plain.number || '',
1718
+ numbers: plain.number ? [{ label: 'primary', number: plain.number }] : [],
1719
+ email: plain.email || '',
1720
+ emails: plain.email ? [{ label: 'primary', email: plain.email }] : [],
1721
+ source: 'personal',
1722
+ sourceId: plain.id || '',
1723
+ entreprise: plain.entreprise || '',
1724
+ birthday: plain.birthday || '',
1725
+ address: plain.address || '',
1726
+ note: plain.note || '',
1727
+ favorited: plain.favorited,
1728
+ personal: true,
1729
+ backend: plain.backend || BACKEND.PERSONAL,
1730
+ });
1741
1731
  }
1742
- }
1743
1732
 
1744
- /* */
1733
+ static parseMobile(plain ) {
1734
+ let address = '';
1735
+ if (plain.postalAddresses.length) {
1736
+ const postalAddress = plain.postalAddresses[0];
1745
1737
 
1746
- class ServerError extends BadResponse {
1747
- static fromResponse(error , status ) {
1748
- return new ServerError(
1749
- error.message || JSON.stringify(error),
1750
- status,
1751
- error.timestamp,
1752
- error.error_id,
1753
- error.details,
1754
- );
1755
- }
1738
+ address = `${postalAddress.street} ${postalAddress.city} ${postalAddress.postCode} ${postalAddress.country}`;
1739
+ }
1756
1740
 
1757
- static fromText(response , status ) {
1758
- return new ServerError(response, status);
1741
+ return new Contact({
1742
+ name: `${plain.givenName || ''} ${plain.familyName || ''}`,
1743
+ number: plain.phoneNumbers.length ? plain.phoneNumbers[0].number : '',
1744
+ numbers: plain.phoneNumbers.length ? [{ label: 'primary', number: plain.phoneNumbers[0].number }] : [],
1745
+ email: plain.emailAddresses.length ? plain.emailAddresses[0].email : '',
1746
+ emails: plain.emailAddresses.length ? [{ label: 'primary', email: plain.emailAddresses[0].email }] : [],
1747
+ source: SOURCE_MOBILE,
1748
+ sourceId: plain.recordID || '',
1749
+ birthday: plain.birthday ? `${plain.birthday.year}-${plain.birthday.month}-${plain.birthday.day}` : '',
1750
+ address,
1751
+ note: plain.note || '',
1752
+ favorited: false,
1753
+ personal: true,
1754
+ });
1759
1755
  }
1760
- }
1761
-
1762
- /* */
1763
- /* global navigator */
1764
- const isMobile = () => typeof navigator !== 'undefined' && navigator.product === 'ReactNative';
1765
1756
 
1766
- /* global window */
1757
+ static parseManyOffice365(response , source ) {
1758
+ return response.map(r => Contact.parseOffice365(r, source));
1759
+ }
1767
1760
 
1768
- const TRACE = 'trace';
1769
- const DEBUG = 'debug';
1770
- const INFO = 'info';
1771
- const LOG = 'log';
1772
- const WARN = 'warn';
1773
- const ERROR = 'error';
1774
- const CONSOLE_METHODS = [INFO, LOG, WARN, ERROR];
1775
- const LOG_LEVELS = [TRACE, DEBUG, INFO, LOG, WARN, ERROR];
1776
- const CATEGORY_PREFIX = 'logger-category=';
1761
+ static parseOffice365(single , source ) {
1762
+ const emails = [];
1763
+ const numbers = [];
1777
1764
 
1778
- const MAX_REMOTE_RETRY = 100;
1765
+ if (single.emailAddresses) {
1766
+ single.emailAddresses.map(email => emails.push({ email: email.address }));
1767
+ }
1779
1768
 
1780
- const addLevelsTo = (instance , withMethods = false) => {
1781
- instance.TRACE = TRACE;
1782
- instance.DEBUG = DEBUG;
1783
- instance.INFO = INFO;
1784
- instance.LOG = LOG;
1785
- instance.WARN = WARN;
1786
- instance.ERROR = ERROR;
1769
+ if (single.businessPhones) {
1770
+ single.businessPhones.map(phone => numbers.push({ label: 'business', number: phone }));
1771
+ }
1787
1772
 
1788
- if (withMethods) {
1789
- instance.trace = (...args) => instance.apply(null, [TRACE, ...args]);
1790
- instance.debug = (...args) => instance.apply(null, [DEBUG, ...args]);
1791
- instance.info = (...args) => instance.apply(null, [INFO, ...args]);
1792
- instance.log = (...args) => instance.apply(null, [LOG, ...args]);
1793
- instance.warn = (...args) => instance.apply(null, [WARN, ...args]);
1794
- instance.error = (...args) => instance.apply(null, [ERROR, ...args]);
1795
- }
1773
+ if (single.mobilePhone) {
1774
+ numbers.push({ label: 'mobile', number: single.mobilePhone });
1775
+ }
1796
1776
 
1797
- return instance;
1798
- };
1777
+ if (single.homePhones) {
1778
+ single.homePhones.map(phone => numbers.push({ label: 'home', number: phone }));
1779
+ }
1799
1780
 
1800
- const safeStringify = (object ) => {
1801
- const result = '{"message": "Not parsable JSON"}';
1802
- try {
1803
- return JSON.stringify(object);
1804
- } catch (e) {
1805
- // Nothing to do
1781
+ return new Contact({
1782
+ sourceId: single.id || '',
1783
+ name: single.displayName || '',
1784
+ number: numbers.length ? numbers[0].number : '',
1785
+ numbers,
1786
+ emails,
1787
+ source: source.name,
1788
+ backend: BACKEND.OFFICE365,
1789
+ });
1806
1790
  }
1807
1791
 
1808
- return result;
1809
- };
1792
+ static parseManyGoogle(response , source ) {
1793
+ return response.map(r => Contact.parseGoogle(r, source));
1794
+ }
1810
1795
 
1811
- class IssueReporter {
1812
-
1813
-
1814
-
1815
-
1816
-
1796
+ static parseGoogle(single , source ) {
1797
+ const emails = [];
1798
+ const numbers = [];
1817
1799
 
1818
-
1819
-
1820
-
1800
+ if (single.emails) {
1801
+ single.emails.forEach(email =>
1802
+ (typeof email === 'object' ? { email: email.address, label: email.label } : { email }));
1803
+ }
1821
1804
 
1822
- constructor() {
1823
- addLevelsTo(this);
1805
+ if (single.numbers_by_label) {
1806
+ Object.keys(single.numbers_by_label).forEach(label =>
1807
+ numbers.push({ label, number: single.numbers_by_label[label] }));
1808
+ } else if (single.numbers) {
1809
+ single.numbers.forEach(phone => numbers.push({ number: phone }));
1810
+ }
1824
1811
 
1825
- this.oldConsoleMethods = null;
1826
- this.enabled = false;
1827
- this.remoteClientConfiguration = null;
1812
+ return new Contact({
1813
+ sourceId: single.id || '',
1814
+ name: single.name || '',
1815
+ number: numbers.length ? numbers[0].number : '',
1816
+ numbers,
1817
+ emails,
1818
+ source: source.name,
1819
+ backend: BACKEND.GOOGLE,
1820
+ });
1828
1821
  }
1829
1822
 
1830
- init() {
1831
- this._catchConsole();
1823
+ static parseManyWazo(response , source ) {
1824
+ return response.map(r => Contact.parseWazo(r, source));
1832
1825
  }
1833
1826
 
1834
- configureRemoteClient(configuration = { tag: 'wazo-sdk', host: null, port: null, level: null, extra: {} }) {
1835
- this.remoteClientConfiguration = configuration;
1836
- }
1827
+ static parseWazo(single , source ) {
1828
+ const emails = [];
1829
+ const numbers = [];
1837
1830
 
1838
- enable() {
1839
- if (!this.oldConsoleMethods) {
1840
- this.init();
1831
+ if (single.email) {
1832
+ emails.push({ label: 'email', email: single.email });
1841
1833
  }
1842
1834
 
1843
- this.enabled = true;
1835
+ if (single.exten) {
1836
+ numbers.push({ label: 'exten', number: single.exten });
1837
+ }
1838
+
1839
+ if (single.mobile_phone_number) {
1840
+ numbers.push({ label: 'mobile', number: single.mobile_phone_number });
1841
+ }
1842
+
1843
+ return new Contact({
1844
+ uuid: single.uuid,
1845
+ sourceId: String(single.id) || '',
1846
+ name: `${single.firstname}${single.lastname ? ` ${single.lastname}` : ''}`,
1847
+ number: numbers.length ? numbers[0].number : '',
1848
+ numbers,
1849
+ emails,
1850
+ source: source.name,
1851
+ backend: BACKEND.WAZO,
1852
+ });
1844
1853
  }
1845
1854
 
1846
- disable() {
1847
- this.enabled = false;
1855
+ static parseManyConference(response , source ) {
1856
+ return response.map(r => Contact.parseConference(r, source));
1848
1857
  }
1849
1858
 
1850
- loggerFor(category ) {
1851
- const logger = (level , ...args ) => {
1852
- this.log.apply(this, [level, this._makeCategory(category), ...args]);
1853
- };
1859
+ static parseConference(single , source ) {
1860
+ const numbers = [];
1861
+ let firstNumber = '';
1854
1862
 
1855
- return addLevelsTo(logger, true);
1863
+ if (single && single.extensions && single.extensions.length > 0 && single.extensions[0].exten) {
1864
+ firstNumber = single.extensions[0].exten;
1865
+ numbers.push({ label: 'exten', number: firstNumber });
1866
+ }
1867
+
1868
+ return new Contact({
1869
+ sourceId: String(single.id),
1870
+ name: single.name,
1871
+ number: firstNumber,
1872
+ numbers,
1873
+ source: source.name,
1874
+ backend: 'conference',
1875
+ });
1856
1876
  }
1857
1877
 
1858
- removeSlashes(str ) {
1859
- return str.replace(/"/g, "'").replace(/\\/g, '');
1878
+ static newFrom(contact ) {
1879
+ return newFrom(contact, Contact);
1860
1880
  }
1861
1881
 
1862
- log(level , ...args ) {
1863
- if (!this.enabled) {
1864
- return;
1865
- }
1882
+ constructor({
1883
+ id,
1884
+ uuid,
1885
+ name,
1886
+ number,
1887
+ numbers,
1888
+ email,
1889
+ emails,
1890
+ source,
1891
+ sourceId,
1892
+ entreprise,
1893
+ birthday,
1894
+ address,
1895
+ note,
1896
+ state,
1897
+ lineState,
1898
+ lastActivity,
1899
+ mobile,
1900
+ status,
1901
+ endpointId,
1902
+ personal,
1903
+ favorited,
1904
+ backend,
1905
+ personalStatus,
1906
+ sessions,
1907
+ connected,
1908
+ doNotDisturb,
1909
+ ringing,
1910
+ previousPresential,
1911
+ lines,
1912
+ } = {}) {
1913
+ this.id = id;
1914
+ this.uuid = uuid;
1915
+ this.name = name || '';
1916
+ this.number = number;
1917
+ this.numbers = numbers;
1918
+ this.email = email;
1919
+ this.emails = emails;
1920
+ this.source = source;
1921
+ this.sourceId = sourceId;
1922
+ this.entreprise = entreprise;
1923
+ this.birthday = birthday;
1924
+ this.address = address;
1925
+ this.note = note;
1926
+ this.state = state;
1927
+ this.lineState = lineState;
1928
+ this.lastActivity = lastActivity;
1929
+ this.mobile = mobile;
1930
+ this.status = status;
1931
+ this.endpointId = endpointId;
1932
+ this.personal = personal;
1933
+ this.favorited = favorited;
1934
+ this.backend = backend;
1935
+ this.personalStatus = personalStatus || '';
1936
+ this.sessions = sessions || [];
1937
+ this.connected = connected;
1938
+ this.doNotDisturb = doNotDisturb;
1939
+ this.ringing = ringing;
1940
+ this.previousPresential = previousPresential;
1941
+ this.lines = lines || [];
1866
1942
 
1867
- // Handle category label
1868
- let category = null;
1869
- let extra = {};
1870
- if (args[0].indexOf(CATEGORY_PREFIX) === 0) {
1871
- category = args[0].split('=')[1];
1872
- // eslint-disable-next-line no-param-reassign
1873
- args.splice(0, 1);
1874
- }
1943
+ // Useful to compare instead of instanceof with minified code
1944
+ this.type = 'Contact';
1945
+ }
1875
1946
 
1876
- // Handle extra data as object for the last argument
1877
- const lastArg = args[args.length - 1];
1878
- if (lastArg && ((typeof lastArg === 'object' && Object.keys(lastArg).length) || lastArg instanceof Error)) {
1879
- if (lastArg instanceof Error) {
1880
- extra = {
1881
- errorMessage: lastArg.message,
1882
- errorStack: lastArg.stack,
1883
- errorType: lastArg.constructor.name,
1884
- };
1885
- } else {
1886
- extra = lastArg;
1887
- }
1888
- // eslint-disable-next-line no-param-reassign
1889
- args.splice(1, 1);
1890
- }
1947
+ setFavorite(value ) {
1948
+ this.favorited = value;
1891
1949
 
1892
- const date = moment__default['default']().format('YYYY-MM-DD HH:mm:ss.SSS');
1893
- const message = args.join(', ');
1894
- let consoleMessage = message;
1950
+ return this;
1951
+ }
1895
1952
 
1896
- if (Object.keys(extra).length) {
1897
- const parsedExtra = safeStringify(extra);
1898
- consoleMessage = `${consoleMessage} (${parsedExtra})`;
1899
- }
1953
+ is(other ) {
1954
+ const sameSourceId = this.sourceId !== null && other.sourceId !== null && this.sourceId === other.sourceId;
1955
+ const sameUuid = !!this.uuid && !!other.uuid && this.uuid === other.uuid;
1900
1956
 
1901
- if (category) {
1902
- consoleMessage = `[${category}] ${consoleMessage}`;
1903
- }
1957
+ const hasSameId = sameSourceId || sameUuid;
1958
+ const hasSameBackend = !!this.backend && !!other.backend && this.backend === other.backend;
1959
+ const hasSameSource = !!this.source && !!other.source && this.source === other.source;
1904
1960
 
1905
- // Log the message in the console anyway (but don't console.error on mobile)
1906
- const consoleLevel = isMobile() && level === 'error' ? WARN : level;
1961
+ return !!other && hasSameId && hasSameBackend && hasSameSource;
1962
+ }
1907
1963
 
1908
- // eslint-disable-next-line
1909
- const oldMethod = this.oldConsoleMethods[consoleLevel] || this.oldConsoleMethods.log;
1910
- oldMethod.apply(oldMethod, [date, consoleMessage]);
1964
+ hasId(id ) {
1965
+ return this.uuid === id;
1966
+ }
1911
1967
 
1912
- this._sendToRemoteLogger(level, { date, message, category, ...extra });
1968
+ hasNumber(number ) {
1969
+ return this.number === number;
1913
1970
  }
1914
1971
 
1915
- logRequest(url , options , response , start ) {
1916
- if (!this.enabled) {
1917
- return;
1918
- }
1919
- const { status } = response;
1920
- const duration = new Date() - start;
1972
+ hasEndpointId(endpointId ) {
1973
+ return this.endpointId === endpointId;
1974
+ }
1921
1975
 
1922
- let level = TRACE;
1923
- if (status >= 400 && status < 500) {
1924
- level = WARN;
1925
- } else if (status >= 500) {
1926
- level = ERROR;
1927
- }
1976
+ isAvailable() {
1977
+ return this.state === STATE.AVAILABLE;
1978
+ }
1928
1979
 
1929
- this.log(level, this._makeCategory('http'), url, {
1930
- status,
1931
- body: this.removeSlashes(JSON.stringify(options.body)),
1932
- method: options.method,
1933
- headers: options.headers,
1934
- duration,
1935
- });
1980
+ isAway() {
1981
+ return this.state === STATE.AWAY;
1982
+ }
1983
+
1984
+ isUnavailable() {
1985
+ return this.state === STATE.UNAVAILABLE;
1936
1986
  }
1937
1987
 
1938
- // Logs aren't stored anymore
1939
- getLogs() {
1940
- console.warn('IssueReporter\'s logs aren\'t stored anymore. Please use fluentd to store them');
1941
- return [];
1988
+ isInvisible() {
1989
+ return this.state === STATE.INVISIBLE;
1942
1990
  }
1943
1991
 
1944
- getParsedLogs() {
1945
- console.warn('IssueReporter\'s logs aren\'t stored anymore. Please use fluentd to store them');
1946
- return [];
1992
+ isInCall() {
1993
+ return this.lineState === LINE_STATE.TALKING || this.lineState === LINE_STATE.HOLDING;
1947
1994
  }
1948
1995
 
1949
- getReport() {
1950
- return this.getParsedLogs().join('\r\n');
1996
+ isRinging() {
1997
+ return this.ringing;
1951
1998
  }
1952
1999
 
1953
- _catchConsole() {
1954
- this.oldConsoleMethods = {};
1955
- CONSOLE_METHODS.forEach((methodName ) => {
1956
- // eslint-disable-next-line
1957
- this.oldConsoleMethods[methodName] = console[methodName];
1958
- window.console[methodName] = (...args) => {
1959
- // Store message
1960
- try {
1961
- this.log(methodName, args.join(' '));
1962
- // Use old console method to log it normally
1963
- this.oldConsoleMethods[methodName].apply(null, args);
1964
- } catch (e) {
1965
- // Avoid circular structure issues
1966
- }
1967
- };
1968
- });
2000
+ isInUseOrRinging() {
2001
+ return this.lineState === LINE_STATE.TALKING || this.lineState === LINE_STATE.RINGING;
1969
2002
  }
1970
2003
 
1971
- _sendToRemoteLogger(level , payload , retry = 0) {
1972
- if (!this.remoteClientConfiguration || retry >= MAX_REMOTE_RETRY) {
1973
- return;
1974
- }
2004
+ isProgressing() {
2005
+ return this.lineState === LINE_STATE.PROGRESSING;
2006
+ }
1975
2007
 
1976
- const { tag, host, port, extra, level: minLevel } = this.remoteClientConfiguration;
1977
- if (!minLevel || this._isLevelAbove(minLevel, level)) {
1978
- return;
2008
+ merge(old ) {
2009
+ Object.keys(old).filter(key => key !== 'lineState').forEach(key => {
2010
+ // $FlowFixMe
2011
+ this[key] = old[key] || this[key];
2012
+ });
2013
+
2014
+ if (old.lineState && !this.lineState) {
2015
+ this.lineState = old.lineState;
1979
2016
  }
1980
2017
 
1981
- const isSecure = +port === 443;
1982
- const url = `http${isSecure ? 's' : ''}://${host}${isSecure ? '' : `:${port}`}/${tag}`;
2018
+ return this;
2019
+ }
1983
2020
 
1984
- realFetch()(url, {
1985
- method: 'POST',
1986
- headers: {
1987
- Accept: 'application/json',
1988
- 'Content-Type': 'application/json',
1989
- },
1990
- body: safeStringify({
1991
- level,
1992
- ...payload,
1993
- ...extra,
1994
- }),
1995
- }).catch(() => {
1996
- setTimeout(() => {
1997
- // eslint-disable-next-line no-underscore-dangle
1998
- payload._retry = retry + 1;
1999
- this._sendToRemoteLogger(level, payload, retry + 1);
2000
- }, 5000 + retry * 1000);
2001
- });
2021
+ isIntern() {
2022
+ return !!this.uuid;
2002
2023
  }
2003
2024
 
2004
- _isLevelAbove(level1 , level2 ) {
2005
- return LOG_LEVELS.indexOf(level1) > LOG_LEVELS.indexOf(level2);
2025
+ isCallable(session ) {
2026
+ return !!this.number && !!session && !session.is(this);
2006
2027
  }
2007
2028
 
2008
- _makeCategory(category ) {
2009
- return `${CATEGORY_PREFIX}${category}`;
2029
+ isFromMobile() {
2030
+ return this.source === SOURCE_MOBILE;
2010
2031
  }
2011
- }
2012
2032
 
2013
- var IssueReporter$1 = new IssueReporter();
2033
+ isFavorite() {
2034
+ return this.favorited;
2035
+ }
2014
2036
 
2015
- /* */
2037
+ separateName() {
2038
+ if (!this.name) {
2039
+ return {
2040
+ firstName: '',
2041
+ lastName: '',
2042
+ };
2043
+ }
2044
+ const names = this.name.split(/\s+/);
2045
+ const firstName = names[0];
2046
+ const lastName = names.slice(1).join(' ');
2016
2047
 
2017
-
2018
-
2019
-
2020
-
2021
-
2022
-
2023
-
2024
-
2048
+ return {
2049
+ firstName,
2050
+ lastName,
2051
+ };
2052
+ }
2053
+ }
2025
2054
 
2026
- const methods = ['head', 'get', 'post', 'put', 'delete'];
2055
+ // Can't use `semver` package as Wazo version aren't in semver format
2027
2056
 
2028
- const logger$1 = IssueReporter$1 ? IssueReporter$1.loggerFor('api') : console;
2057
+ const compareVersions = (a, b) => {
2058
+ let i;
2059
+ let diff;
2060
+ const regExStrip0 = /(\.0+)+$/;
2061
+ const segmentsA = a.replace(regExStrip0, '').split('.');
2062
+ const segmentsB = b.replace(regExStrip0, '').split('.');
2063
+ const l = Math.min(segmentsA.length, segmentsB.length);
2029
2064
 
2030
- // Use a function here to be able to mock it in tests
2031
- const realFetch = () => {
2032
- if (typeof document !== 'undefined') {
2033
- // Browser
2034
- return window.fetch;
2065
+ for (i = 0; i < l; i++) {
2066
+ diff = parseInt(segmentsA[i], 10) - parseInt(segmentsB[i], 10);
2067
+ if (diff) {
2068
+ return diff;
2069
+ }
2035
2070
  }
2071
+ return segmentsA.length - segmentsB.length;
2072
+ };
2036
2073
 
2037
- if (isMobile()) {
2038
- // React native
2039
- return fetch;
2040
- }
2074
+ //
2041
2075
 
2042
- // nodejs
2043
- // this package is disable for react-native in package.json because it requires nodejs modules
2044
- // Used to trick the optimizer to avoid requiring `node-fetch/lib/index` directly
2045
- // It causes to require it on browsers when delivered by a nodejs engine (cf: vitejs).
2046
- try {
2047
- // $FlowFixMe
2048
- return require(Math.random() >= 0 ? 'node-fetch/lib/index' : '');
2049
- } catch (e) {
2050
- return fetch;
2051
- }
2052
- };
2076
+ const swarmKey = jsrsasign.KEYUTIL.getKey(swarmPublicKey);
2077
+ const MINIMUM_WAZO_ENGINE_VERSION_FOR_DEFAULT_CONTEXT = '19.08';
2053
2078
 
2054
- class ApiRequester {
2055
2079
 
2080
+
2056
2081
 
2082
+
2083
+
2084
+
2085
+
2086
+
2087
+
2088
+
2057
2089
 
2090
+
2091
+
2058
2092
 
2059
-
2093
+
2094
+
2095
+
2096
+
2097
+
2098
+
2060
2099
 
2061
-
2062
-
2100
+
2101
+
2102
+
2103
+
2104
+
2105
+
2106
+
2107
+
2108
+
2109
+
2110
+
2063
2111
 
2112
+
2113
+
2064
2114
 
2065
2115
 
2116
+
2117
+
2066
2118
 
2067
-
2068
-
2069
-
2070
- // eslint-disable-next-line
2071
- static successResponseParser(response , isJson ) {
2072
- return response.status === 204 || response.status === 201 || response.status === 200;
2073
- }
2074
-
2075
- static defaultParser(response ) {
2076
- return response.json().then((data ) => data);
2077
- }
2119
+
2120
+
2121
+
2122
+
2123
+
2124
+
2125
+
2078
2126
 
2079
- static getQueryString(obj ) {
2080
- return Object.keys(obj)
2081
- .filter(key => obj[key])
2082
- .map(key => `${key}=${encodeURIComponent(obj[key])}`)
2083
- .join('&');
2084
- }
2127
+ class Session {
2128
+
2129
+
2130
+
2131
+
2132
+
2133
+
2134
+
2135
+
2136
+
2137
+
2138
+
2085
2139
 
2086
- static base64Encode(str ) {
2087
- return typeof btoa !== 'undefined' ? btoa(str) : jsBase64.Base64.encode(str);
2088
- }
2140
+ static parse(plain ) {
2141
+ const token = plain.data.metadata ? plain.data.metadata.jwt : null;
2142
+ let authorizations = [];
2089
2143
 
2090
- // @see https://github.com/facebook/flow/issues/183#issuecomment-358607052
2091
- constructor({ server, refreshTokenCallback, clientId, agent = null, token = null, fetchOptions } ) {
2092
- this.server = server;
2093
- this.agent = agent;
2094
- this.clientId = clientId;
2095
- this.refreshTokenCallback = refreshTokenCallback;
2096
- this.refreshTokenPromise = null;
2097
- this.fetchOptions = fetchOptions || {};
2144
+ // Add authorizations from JWT
2098
2145
  if (token) {
2099
- this.token = token;
2146
+ const isValid = jsrsasign.jws.JWS.verifyJWT(token, swarmKey, { alg: ['RS256'], verifyAt: new Date() });
2147
+ if (isValid) {
2148
+ const decodedToken = jsrsasign.jws.JWS.readSafeJSONString(jsrsasign.b64utoutf8(token.split('.')[1]));
2149
+ authorizations = decodedToken ? decodedToken.authorizations : [];
2150
+ }
2100
2151
  }
2101
2152
 
2102
- methods.forEach(method => {
2103
- // $FlowFixMe
2104
- ApiRequester.prototype[method] = function sugar(...args) {
2105
- // Add method in arguments passed to `call`
2106
- args.splice(1, 0, method);
2107
-
2108
- return this.call.call(this, ...args);
2109
- };
2153
+ return new Session({
2154
+ token: plain.data.token,
2155
+ refreshToken: plain.data.refresh_token || null,
2156
+ uuid: plain.data.metadata ? plain.data.metadata.uuid : null,
2157
+ sessionUuid: plain.data.session_uuid,
2158
+ authorizations,
2159
+ acl: plain.data.acls ? plain.data.acls : plain.data.acl ? plain.data.acl : [],
2160
+ tenantUuid: plain.data.metadata ? plain.data.metadata.tenant_uuid : undefined,
2161
+ expiresAt: new Date(`${plain.data.utc_expires_at}z`),
2162
+ engineUuid: plain.data.xivo_uuid,
2110
2163
  });
2111
2164
  }
2112
2165
 
2113
- setTenant(tenant ) {
2114
- this.tenant = tenant;
2166
+ static newFrom(profile ) {
2167
+ return newFrom(profile, Session);
2115
2168
  }
2116
2169
 
2117
- setToken(token ) {
2170
+ constructor({
2171
+ token,
2172
+ uuid,
2173
+ tenantUuid,
2174
+ profile,
2175
+ expiresAt,
2176
+ authorizations,
2177
+ acl,
2178
+ engineVersion,
2179
+ refreshToken,
2180
+ sessionUuid,
2181
+ engineUuid,
2182
+ } = {}) {
2118
2183
  this.token = token;
2184
+ this.uuid = uuid;
2185
+ this.tenantUuid = tenantUuid || null;
2186
+ this.profile = profile;
2187
+ this.expiresAt = expiresAt;
2188
+ this.authorizations = authorizations || [];
2189
+ this.acl = acl || [];
2190
+ this.engineVersion = engineVersion;
2191
+ this.refreshToken = refreshToken;
2192
+ this.sessionUuid = sessionUuid;
2193
+ this.engineUuid = engineUuid;
2119
2194
  }
2120
2195
 
2121
- setFetchOptions(options ) {
2122
- this.fetchOptions = options;
2196
+ hasExpired(date = new Date()) {
2197
+ return date >= this.expiresAt;
2123
2198
  }
2124
2199
 
2125
- async call(
2126
- path ,
2127
- method = 'get',
2128
- body = null,
2129
- headers = null,
2130
- parse = ApiRequester.defaultParser,
2131
- firstCall = true,
2132
- ) {
2133
- const url = this.computeUrl(method, path, body);
2134
- const newHeaders = this.getHeaders(headers);
2135
- let newBody = method === 'get' ? null : body;
2136
- if (newBody && newHeaders['Content-Type'] === 'application/json') {
2137
- newBody = JSON.stringify(newBody);
2138
- }
2139
- const isHead = method === 'head';
2140
- const hasEmptyResponse = method === 'delete' || isHead;
2141
- const newParse = hasEmptyResponse ? ApiRequester.successResponseParser : parse;
2142
-
2143
- const fetchOptions = { ...this.fetchOptions || {} };
2144
- const extraHeaders = fetchOptions.headers || {};
2145
- delete fetchOptions.headers;
2200
+ is(contact ) {
2201
+ return Boolean(contact) && this.uuid === contact.uuid;
2202
+ }
2146
2203
 
2147
- const options = {
2148
- method,
2149
- body: newBody,
2150
- headers: { ...this.getHeaders(headers), ...extraHeaders },
2151
- agent: this.agent,
2152
- ...fetchOptions,
2153
- };
2204
+ using(profile ) {
2205
+ this.profile = profile;
2154
2206
 
2155
- if (this.refreshTokenPromise) {
2156
- logger$1.info('A token is already refreshing, waiting ...', { url });
2157
- await this.refreshTokenPromise;
2158
- }
2207
+ return this;
2208
+ }
2159
2209
 
2160
- const start = new Date();
2210
+ hasAuthorizations() {
2211
+ return this.authorizations && !!this.authorizations.length;
2212
+ }
2161
2213
 
2162
- return realFetch()(url, options).then(response => {
2163
- const contentType = response.headers.get('content-type') || '';
2164
- const isJson = contentType.indexOf('application/json') !== -1;
2214
+ displayName() {
2215
+ return this.profile ? `${this.profile.firstName} ${this.profile.lastName}` : '';
2216
+ }
2165
2217
 
2166
- IssueReporter$1.logRequest(url, options, response, start);
2218
+ hasAccessToVoicemail() {
2219
+ if (!this.profile) {
2220
+ return false;
2221
+ }
2222
+ return !!this.profile.voicemail;
2223
+ }
2167
2224
 
2168
- // Throw an error only if status >= 400
2169
- if ((isHead && response.status >= 500) || (!isHead && response.status >= 400)) {
2170
- const promise = isJson ? response.json() : response.text();
2171
- const exceptionClass = response.status >= 500 ? ServerError : BadResponse;
2225
+ primaryLine() {
2226
+ return this.profile && this.profile.lines.length > 0 ? this.profile.lines[0] : null;
2227
+ }
2172
2228
 
2173
- return promise.then(async (err ) => {
2174
- // Check if the token is still valid
2175
- if (firstCall && this._checkTokenExpired(response, err)) {
2176
- logger$1.warn('token expired', { error: err.reason });
2177
- // Replay the call after refreshing the token
2178
- return this._replayWithNewToken(err, path, method, body, headers, parse);
2179
- }
2229
+ primarySipLine() {
2230
+ return this.profile && this.profile.sipLines.length > 0 ? this.profile.sipLines[0] : null;
2231
+ }
2180
2232
 
2181
- const error = typeof err === 'string'
2182
- ? exceptionClass.fromText(err, response.status)
2183
- : exceptionClass.fromResponse(err, response.status);
2233
+ primaryWebRtcLine() {
2234
+ return this.profile && (this.profile.sipLines || []).find(sipLine => sipLine && sipLine.isWebRtc());
2235
+ }
2184
2236
 
2185
- logger$1.error('API error', error);
2237
+ primaryCtiLine() {
2238
+ return this.profile && (this.profile.sipLines || []).find(sipLine => sipLine && !sipLine.isWebRtc());
2239
+ }
2186
2240
 
2187
- throw error;
2188
- });
2241
+ primaryContext() {
2242
+ if (this.engineVersion) {
2243
+ if (this.hasEngineVersionGte(MINIMUM_WAZO_ENGINE_VERSION_FOR_DEFAULT_CONTEXT)) {
2244
+ return 'default';
2189
2245
  }
2246
+ }
2190
2247
 
2191
- return newParse(response, isJson);
2192
- }).catch(error => {
2193
- logger$1.error('Fetch failed', { url, options, message: error.message, stack: error.stack });
2194
-
2195
- throw error;
2196
- });
2197
- }
2198
-
2199
- _checkTokenExpired(response , err ) {
2200
- // Special case when authenticating form a token: we got a 404
2201
- const isTokenNotFound = response.status === 404 && this._isTokenNotFound(err);
2248
+ const line = this.primaryLine();
2202
2249
 
2203
- return response.status === 401 || isTokenNotFound;
2250
+ return line && line.extensions.length > 0 ? line.extensions[0].context : 'default';
2204
2251
  }
2205
2252
 
2206
- _isTokenNotFound(err ) {
2207
- return err && err.reason && err.reason[0] === 'No such token';
2253
+ hasEngineVersionGte(version ) {
2254
+ return this.engineVersion && compareVersions(String(this.engineVersion), String(version)) >= 0;
2208
2255
  }
2209
2256
 
2210
- _replayWithNewToken(
2211
- err ,
2212
- path ,
2213
- method ,
2214
- body = null,
2215
- headers = null,
2216
- parse ,
2217
- ) {
2218
- const isTokenNotFound = this._isTokenNotFound(err);
2219
- let newPath = path;
2220
- logger$1.info('refreshing token', { inProgress: !!this.refreshTokenPromise });
2221
-
2222
- this.refreshTokenPromise = this.refreshTokenPromise || this.refreshTokenCallback();
2257
+ primaryNumber() {
2258
+ const line = this.primaryLine();
2223
2259
 
2224
- return this.refreshTokenPromise.then(() => {
2225
- this.refreshTokenPromise = null;
2226
- logger$1.info('token refreshed', { isTokenNotFound });
2227
- if (isTokenNotFound) {
2228
- const pathParts = path.split('/');
2229
- pathParts.pop();
2230
- pathParts.push(this.token);
2231
- newPath = pathParts.join('/');
2232
- }
2260
+ return line && line.extensions.length ? line.extensions[0].exten : null;
2261
+ }
2233
2262
 
2234
- return this.call(newPath, method, body, headers, parse, false);
2235
- }).catch((e) => {
2236
- this.refreshTokenPromise = null;
2237
- throw e;
2238
- });
2263
+ allLines() {
2264
+ return this.profile ? this.profile.lines || [] : [];
2239
2265
  }
2240
2266
 
2241
- getHeaders(header ) {
2242
- if (header instanceof Object) {
2243
- return header;
2267
+ allNumbers() {
2268
+ const extensions = this.allLines().map(line => line.extensions.map(extension => extension.exten));
2269
+ if (!extensions.length) {
2270
+ return [];
2244
2271
  }
2245
2272
 
2246
- return {
2247
- 'X-Auth-Token': this.token,
2248
- ...(this.tenant ? { 'Wazo-Tenant': this.tenant } : null),
2249
- Accept: 'application/json',
2250
- 'Content-Type': 'application/json',
2251
- };
2273
+ return extensions.reduce((a, b) => a.concat(b));
2252
2274
  }
2253
2275
 
2254
- computeUrl(method , path , body ) {
2255
- const url = `${this.baseUrl}/${path}`;
2256
-
2257
- return method === 'get' && body && Object.keys(body).length ? `${url}?${ApiRequester.getQueryString(body)}` : url;
2276
+ hasExtension(extension ) {
2277
+ return this.allNumbers().some(number => number === extension);
2258
2278
  }
2259
2279
 
2260
- get baseUrl() {
2261
- return `https://${this.server}/api`;
2280
+ get acls() {
2281
+ console.warn('`acls` property of Session has been removed in Wazo\'s SDK, please use `acl` instead.');
2282
+ return this.acl;
2262
2283
  }
2263
2284
  }
2264
2285
 
@@ -2994,17 +3015,66 @@ class Voicemail {
2994
3015
 
2995
3016
  //
2996
3017
 
3018
+
3019
+
3020
+
3021
+
3022
+
3023
+
3024
+
3025
+
3026
+
3027
+
3028
+
3029
+
3030
+
3031
+
3032
+
3033
+
3034
+ class Recording {
3035
+
3036
+
3037
+
3038
+
3039
+
3040
+
3041
+ static parseMany(recordings ) {
3042
+ return recordings.map(item => Recording.parse(item));
3043
+ }
3044
+
3045
+ static parse(plain ) {
3046
+ return new Recording({
3047
+ deleted: plain.deleted,
3048
+ fileName: plain.filename,
3049
+ end: plain.end_time ? moment__default['default'](plain.end_time).toDate() : null,
3050
+ start: moment__default['default'](plain.start_time).toDate(),
3051
+ uuid: plain.uuid,
3052
+ });
3053
+ }
3054
+
3055
+ constructor({ deleted, end, fileName, start, uuid } ) {
3056
+ this.deleted = deleted;
3057
+ this.end = end;
3058
+ this.fileName = fileName;
3059
+ this.start = start;
3060
+ this.uuid = uuid;
3061
+ }
3062
+ }
3063
+
3064
+ //
3065
+
2997
3066
 
2998
-
3067
+
2999
3068
 
3000
3069
 
3001
3070
 
3002
3071
 
3003
3072
 
3004
-
3073
+
3005
3074
 
3006
3075
 
3007
3076
 
3077
+
3008
3078
 
3009
3079
 
3010
3080
 
@@ -3017,7 +3087,7 @@ class Voicemail {
3017
3087
 
3018
3088
 
3019
3089
 
3020
-
3090
+
3021
3091
 
3022
3092
 
3023
3093
 
@@ -3032,13 +3102,14 @@ class Voicemail {
3032
3102
 
3033
3103
 
3034
3104
 
3035
-
3105
+
3106
+
3036
3107
 
3037
3108
 
3038
3109
  class CallLog {
3039
3110
 
3040
3111
 
3041
-
3112
+
3042
3113
 
3043
3114
 
3044
3115
 
@@ -3055,7 +3126,7 @@ class CallLog {
3055
3126
 
3056
3127
 
3057
3128
 
3058
-
3129
+
3059
3130
 
3060
3131
  static merge(current , toMerge ) {
3061
3132
  const onlyUnique = (value, index, self) => self.indexOf(value) === index;
@@ -3072,7 +3143,7 @@ class CallLog {
3072
3143
 
3073
3144
  static parse(plain ) {
3074
3145
  return new CallLog({
3075
- answer: moment__default['default'](plain.answer).toDate(),
3146
+ answer: plain.answer ? moment__default['default'](plain.answer).toDate() : null,
3076
3147
  answered: plain.answered,
3077
3148
  callDirection: plain.call_direction,
3078
3149
  destination: {
@@ -3086,13 +3157,14 @@ class CallLog {
3086
3157
  id: plain.id,
3087
3158
  duration: (plain.duration || 0) * 1000, // duration is in seconds
3088
3159
  start: moment__default['default'](plain.start).toDate(),
3089
- end: moment__default['default'](plain.end).toDate(),
3160
+ end: plain.end ? moment__default['default'](plain.end).toDate() : null,
3161
+ recordings: Recording.parseMany(plain.recordings),
3090
3162
  });
3091
3163
  }
3092
3164
 
3093
3165
  static parseNew(plain , session ) {
3094
3166
  return new CallLog({
3095
- answer: moment__default['default'](plain.answer).toDate(),
3167
+ answer: plain.answer ? moment__default['default'](plain.answer).toDate() : null,
3096
3168
  answered: plain.answered,
3097
3169
  callDirection: plain.call_direction,
3098
3170
  destination: {
@@ -3106,7 +3178,8 @@ class CallLog {
3106
3178
  id: plain.id,
3107
3179
  duration: (plain.duration || 0) * 1000, // duration is in seconds
3108
3180
  start: moment__default['default'](plain.start).toDate(),
3109
- end: moment__default['default'](plain.end).toDate(),
3181
+ end: plain.end ? moment__default['default'](plain.end).toDate() : null,
3182
+ recordings: Recording.parseMany(plain.recordings),
3110
3183
  // @TODO: FIXME add verification declined vs missed call
3111
3184
  newMissedCall: session
3112
3185
  && session.hasExtension(plain.requested_extension || plain.destination_extension) && !plain.answered,
@@ -4327,6 +4400,10 @@ class ApiClient {
4327
4400
  this.fetchOptions = fetchOptions;
4328
4401
  this.client.setFetchOptions(fetchOptions);
4329
4402
  }
4403
+
4404
+ disableErrorLogging() {
4405
+ this.client.disableErrorLogging();
4406
+ }
4330
4407
  }
4331
4408
 
4332
4409
  /* eslint-disable dot-notation */