@wazo/sdk 0.37.0 → 0.37.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (144) hide show
  1. package/dist/wazo-sdk.js +5 -5
  2. package/esm/__tests__/api-client.test.js +91 -23
  3. package/esm/__tests__/web-rtc-client.test.js +165 -56
  4. package/esm/api/agentd.js +7 -1
  5. package/esm/api/amid.js +7 -1
  6. package/esm/api/application.js +7 -1
  7. package/esm/api/auth.js +7 -1
  8. package/esm/api/call-logd.js +17 -23
  9. package/esm/api/calld.js +10 -18
  10. package/esm/api/chatd.js +7 -1
  11. package/esm/api/confd.js +78 -1
  12. package/esm/api/ctid-ng.js +7 -1
  13. package/esm/api/dird.js +7 -1
  14. package/esm/api/webhookd.js +7 -1
  15. package/esm/api-client.js +91 -23
  16. package/esm/checker/Checker.js +169 -56
  17. package/esm/checker/checks/aor.js +91 -23
  18. package/esm/checker/checks/api.js +91 -23
  19. package/esm/checker/checks/index.js +169 -56
  20. package/esm/checker/checks/wazo-websocket.js +11 -1
  21. package/esm/checker/checks/webrtc-transport.js +165 -56
  22. package/esm/checker/checks/webrtc.js +165 -56
  23. package/esm/domain/AdHocAPIConference.js +1722 -1654
  24. package/esm/domain/CallLog.js +10 -22
  25. package/esm/domain/CallSession.js +1 -18
  26. package/esm/domain/Features.js +91 -23
  27. package/esm/domain/IndirectTransfer.js +0 -17
  28. package/esm/domain/Meeting.js +71 -0
  29. package/esm/domain/Phone/CTIPhone.js +98 -26
  30. package/esm/domain/Phone/Phone.js +0 -17
  31. package/esm/domain/Phone/WebRTCPhone.js +47 -27
  32. package/esm/domain/Room.js +0 -17
  33. package/esm/domain/SwitchboardCall.js +0 -17
  34. package/esm/domain/__tests__/CallLog.test.js +10 -22
  35. package/esm/domain/__tests__/CallSession.test.js +1 -18
  36. package/esm/domain/__tests__/IndirectTransfer.test.js +1 -18
  37. package/esm/domain/__tests__/Room.test.js +1 -18
  38. package/esm/index.js +3880 -3111
  39. package/esm/lib/WazoSessionDescriptionHandler.js +24 -10
  40. package/esm/service/CallApi.js +91 -23
  41. package/esm/service/IssueReporter.js +7 -1
  42. package/esm/service/__tests__/IssueReporter.test.js +9 -1
  43. package/esm/service/getApiClient.js +91 -23
  44. package/esm/simple/Auth.js +1126 -360
  45. package/esm/simple/Configuration.js +1126 -360
  46. package/esm/simple/Directory.js +91 -23
  47. package/esm/simple/Phone.js +3724 -2958
  48. package/esm/simple/Websocket.js +13 -1
  49. package/esm/simple/index.js +1128 -362
  50. package/esm/simple/room/LocalParticipant.js +1097 -331
  51. package/esm/simple/room/Participant.js +1097 -331
  52. package/esm/simple/room/RemoteParticipant.js +1097 -331
  53. package/esm/simple/room/Room.js +1097 -331
  54. package/esm/simple/room/SipRoom.js +12996 -0
  55. package/esm/simple/room/Stream.js +1128 -362
  56. package/esm/simple/room/__tests__/RemoteParticipant.test.js +5157 -4389
  57. package/esm/simple/utils.js +1128 -362
  58. package/esm/utils/__tests__/api-requester.test.js +7 -1
  59. package/esm/utils/api-requester.js +7 -1
  60. package/esm/web-rtc-client.js +165 -56
  61. package/esm/websocket-client.js +12 -2
  62. package/lib/__tests__/api-client.test.js +91 -23
  63. package/lib/__tests__/web-rtc-client.test.js +165 -56
  64. package/lib/api/agentd.js +7 -1
  65. package/lib/api/amid.js +7 -1
  66. package/lib/api/application.js +7 -1
  67. package/lib/api/auth.js +7 -1
  68. package/lib/api/call-logd.js +17 -23
  69. package/lib/api/calld.js +10 -18
  70. package/lib/api/chatd.js +7 -1
  71. package/lib/api/confd.js +78 -1
  72. package/lib/api/ctid-ng.js +7 -1
  73. package/lib/api/dird.js +7 -1
  74. package/lib/api/webhookd.js +7 -1
  75. package/lib/api-client.js +91 -23
  76. package/lib/checker/Checker.js +169 -56
  77. package/lib/checker/checks/aor.js +91 -23
  78. package/lib/checker/checks/api.js +91 -23
  79. package/lib/checker/checks/index.js +169 -56
  80. package/lib/checker/checks/wazo-websocket.js +11 -1
  81. package/lib/checker/checks/webrtc-transport.js +165 -56
  82. package/lib/checker/checks/webrtc.js +165 -56
  83. package/lib/domain/AdHocAPIConference.js +1722 -1654
  84. package/lib/domain/CallLog.js +10 -22
  85. package/lib/domain/CallSession.js +1 -18
  86. package/lib/domain/Features.js +91 -23
  87. package/lib/domain/IndirectTransfer.js +0 -17
  88. package/lib/domain/Meeting.js +73 -0
  89. package/lib/domain/Phone/CTIPhone.js +98 -26
  90. package/lib/domain/Phone/Phone.js +0 -16
  91. package/lib/domain/Phone/WebRTCPhone.js +47 -27
  92. package/lib/domain/Room.js +0 -17
  93. package/lib/domain/SwitchboardCall.js +0 -17
  94. package/lib/domain/__tests__/CallLog.test.js +10 -22
  95. package/lib/domain/__tests__/CallSession.test.js +1 -18
  96. package/lib/domain/__tests__/IndirectTransfer.test.js +1 -18
  97. package/lib/domain/__tests__/Room.test.js +1 -18
  98. package/lib/index.js +3880 -3111
  99. package/lib/lib/WazoSessionDescriptionHandler.js +24 -10
  100. package/lib/service/CallApi.js +91 -23
  101. package/lib/service/IssueReporter.js +7 -1
  102. package/lib/service/__tests__/IssueReporter.test.js +9 -1
  103. package/lib/service/getApiClient.js +91 -23
  104. package/lib/simple/Auth.js +1126 -360
  105. package/lib/simple/Configuration.js +1126 -360
  106. package/lib/simple/Directory.js +91 -23
  107. package/lib/simple/Phone.js +3724 -2958
  108. package/lib/simple/Websocket.js +13 -1
  109. package/lib/simple/index.js +1128 -362
  110. package/lib/simple/room/LocalParticipant.js +1097 -331
  111. package/lib/simple/room/Participant.js +1097 -331
  112. package/lib/simple/room/RemoteParticipant.js +1097 -331
  113. package/lib/simple/room/Room.js +1097 -331
  114. package/lib/simple/room/SipRoom.js +13006 -0
  115. package/lib/simple/room/Stream.js +1128 -362
  116. package/lib/simple/room/__tests__/RemoteParticipant.test.js +5157 -4389
  117. package/lib/simple/utils.js +1128 -362
  118. package/lib/utils/__tests__/api-requester.test.js +7 -1
  119. package/lib/utils/api-requester.js +7 -1
  120. package/lib/web-rtc-client.js +165 -56
  121. package/lib/websocket-client.js +13 -1
  122. package/package.json +1 -1
  123. package/src/api/calld.js +3 -0
  124. package/src/api/confd.js +13 -0
  125. package/src/domain/CallLog.js +10 -22
  126. package/src/domain/CallSession.js +1 -2
  127. package/src/domain/Meeting.js +59 -0
  128. package/src/domain/Phone/CTIPhone.js +6 -2
  129. package/src/domain/Phone/Phone.js +2 -0
  130. package/src/domain/Phone/WebRTCPhone.js +22 -8
  131. package/src/index.js +6 -0
  132. package/src/lib/WazoSessionDescriptionHandler.js +17 -9
  133. package/src/service/IssueReporter.js +7 -1
  134. package/src/service/__tests__/IssueReporter.test.js +2 -0
  135. package/src/simple/Phone.js +13 -4
  136. package/src/simple/index.js +8 -0
  137. package/src/simple/room/Participant.js +7 -0
  138. package/src/simple/room/Room.js +157 -79
  139. package/src/simple/room/SipRoom.js +147 -0
  140. package/src/simple/room/Stream.js +1 -3
  141. package/src/simple/room/__tests__/RemoteParticipant.test.js +3 -2
  142. package/src/types/WebSocketMessage.js +21 -0
  143. package/src/web-rtc-client.js +57 -24
  144. package/src/websocket-client.js +4 -0
@@ -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,2067 +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;
704
-
705
- this.sipLines = sipLines || [];
616
+ static getQueryString(obj ) {
617
+ return Object.keys(obj)
618
+ .filter(key => obj[key])
619
+ .map(key => `${key}=${encodeURIComponent(obj[key])}`)
620
+ .join('&');
706
621
  }
707
622
 
708
- static getLinesState(lines ) {
709
- let result = LINE_STATE.UNAVAILABLE;
623
+ static base64Encode(str ) {
624
+ return typeof btoa !== 'undefined' ? btoa(str) : jsBase64.Base64.encode(str);
625
+ }
710
626
 
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
- }
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
+ }
717
638
 
718
- if (line.state === LINE_STATE.TALKING) {
719
- result = LINE_STATE.TALKING;
720
- break;
721
- }
639
+ this.shouldLogErrors = true;
722
640
 
723
- if (line.state === LINE_STATE.AVAILABLE) {
724
- result = LINE_STATE.AVAILABLE;
725
- }
726
- }
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);
727
646
 
728
- return result;
647
+ return this.call.call(this, ...args);
648
+ };
649
+ });
729
650
  }
730
651
 
731
- hasId(id ) {
732
- return id === this.id;
652
+ setTenant(tenant ) {
653
+ this.tenant = tenant;
733
654
  }
734
655
 
735
- setMobileNumber(number ) {
736
- this.mobileNumber = number;
737
-
738
- return this;
656
+ setToken(token ) {
657
+ this.token = token;
739
658
  }
740
659
 
741
- setForwardOption(forwardOption ) {
742
- const updatedForwardOptions = this.forwards.slice();
743
- const index = updatedForwardOptions.findIndex(forward => forward.is(forwardOption));
744
- updatedForwardOptions.splice(index, 1, forwardOption);
660
+ setFetchOptions(options ) {
661
+ this.fetchOptions = options;
662
+ }
745
663
 
746
- this.forwards = updatedForwardOptions;
664
+ disableErrorLogging() {
665
+ this.shouldLogErrors = false;
666
+ }
747
667
 
748
- return this;
668
+ enableErrorLogging() {
669
+ this.shouldLogErrors = true;
749
670
  }
750
671
 
751
- setDoNotDisturb(enabled ) {
752
- this.doNotDisturb = enabled;
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;
753
689
 
754
- 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
+ });
755
748
  }
756
749
 
757
- setState(state ) {
758
- this.state = state;
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);
759
753
 
760
- return this;
754
+ return response.status === 401 || isTokenNotFound;
755
+ }
756
+
757
+ _isTokenNotFound(err ) {
758
+ return err && err.reason && err.reason[0] === 'No such token';
759
+ }
760
+
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
909
 
896
-
897
-
898
-
899
-
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
-
945
-
946
973
 
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
-
1024
- return old ? current.merge(old) : current;
1025
- });
1026
- }
1027
-
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
1014
 
1164
- if (single.emailAddresses) {
1165
- single.emailAddresses.map(email => emails.push({ email: email.address }));
1166
- }
1015
+ static parse(plain ) {
1016
+ let { username, secret, host } = plain;
1167
1017
 
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
1620
 
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
- }
1621
+ static merge(oldContacts , newContacts ) {
1622
+ return newContacts.map(current => {
1623
+ const old = oldContacts.find(contact => contact && contact.is(current));
1551
1624
 
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,
1625
+ return old ? current.merge(old) : current;
1562
1626
  });
1563
1627
  }
1564
1628
 
1565
- static newFrom(profile ) {
1566
- return newFrom(profile, Session);
1567
- }
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;
1568
1634
 
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
- }
1635
+ // last Name can be empty
1636
+ if (aLastName === bLastName) {
1637
+ return aNames.firstName.localeCompare(bNames.firstName);
1638
+ }
1594
1639
 
1595
- hasExpired(date = new Date()) {
1596
- return date >= this.expiresAt;
1640
+ return aLastName.localeCompare(bLastName);
1597
1641
  }
1598
1642
 
1599
- is(contact ) {
1600
- return Boolean(contact) && this.uuid === contact.uuid;
1643
+ static parseMany(response ) {
1644
+ return response.results.map(r => Contact.parse(r, response.column_types));
1601
1645
  }
1602
1646
 
1603
- using(profile ) {
1604
- this.profile = profile;
1605
-
1606
- return this;
1607
- }
1647
+ static manyGraphQlWithNumbersParser(numbers ) {
1648
+ return (response ) => {
1649
+ if (!response.data || !response.data.me || !response.data.me.contacts) {
1650
+ return [];
1651
+ }
1608
1652
 
1609
- hasAuthorizations() {
1610
- return this.authorizations && !!this.authorizations.length;
1611
- }
1653
+ return response.data.me.contacts.edges.map((edge, i) => {
1654
+ if (!edge.node) {
1655
+ return null;
1656
+ }
1612
1657
 
1613
- displayName() {
1614
- return this.profile ? `${this.profile.firstName} ${this.profile.lastName}` : '';
1615
- }
1658
+ const { email } = edge.node;
1659
+ const name = edge.node.firstname && edge.node.lastname
1660
+ ? `${edge.node.firstname || ''} ${edge.node.lastname || ''}` : edge.node.wazoReverse;
1616
1661
 
1617
- hasAccessToVoicemail() {
1618
- if (!this.profile) {
1619
- return false;
1620
- }
1621
- return !!this.profile.voicemail;
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
+ };
1622
1675
  }
1623
1676
 
1624
- primaryLine() {
1625
- return this.profile && this.profile.lines.length > 0 ? this.profile.lines[0] : null;
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);
1682
+
1683
+ return plain.column_values.filter((e, index) => numberColumns.some(i => i === index) && e !== null);
1626
1684
  }
1627
1685
 
1628
- primarySipLine() {
1629
- return this.profile && this.profile.sipLines.length > 0 ? this.profile.sipLines[0] : null;
1686
+ static parse(plain , columns ) {
1687
+ const numbers = Contact.fetchNumbers(plain, columns);
1688
+ const email = plain.column_values[columns.indexOf('email')];
1689
+
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
+ });
1630
1708
  }
1631
1709
 
1632
- primaryWebRtcLine() {
1633
- return this.profile && (this.profile.sipLines || []).find(sipLine => sipLine && sipLine.isWebRtc());
1710
+ static parseManyPersonal(results ) {
1711
+ return results.map(r => Contact.parsePersonal(r));
1634
1712
  }
1635
1713
 
1636
- primaryCtiLine() {
1637
- return this.profile && (this.profile.sipLines || []).find(sipLine => sipLine && !sipLine.isWebRtc());
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
+ });
1638
1731
  }
1639
1732
 
1640
- primaryContext() {
1641
- if (this.engineVersion) {
1642
- if (this.hasEngineVersionGte(MINIMUM_WAZO_ENGINE_VERSION_FOR_DEFAULT_CONTEXT)) {
1643
- return 'default';
1644
- }
1645
- }
1733
+ static parseMobile(plain ) {
1734
+ let address = '';
1735
+ if (plain.postalAddresses.length) {
1736
+ const postalAddress = plain.postalAddresses[0];
1646
1737
 
1647
- const line = this.primaryLine();
1738
+ address = `${postalAddress.street} ${postalAddress.city} ${postalAddress.postCode} ${postalAddress.country}`;
1739
+ }
1648
1740
 
1649
- return line && line.extensions.length > 0 ? line.extensions[0].context : 'default';
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
+ });
1650
1755
  }
1651
1756
 
1652
- hasEngineVersionGte(version ) {
1653
- return this.engineVersion && compareVersions(String(this.engineVersion), String(version)) >= 0;
1757
+ static parseManyOffice365(response , source ) {
1758
+ return response.map(r => Contact.parseOffice365(r, source));
1654
1759
  }
1655
1760
 
1656
- primaryNumber() {
1657
- const line = this.primaryLine();
1761
+ static parseOffice365(single , source ) {
1762
+ const emails = [];
1763
+ const numbers = [];
1658
1764
 
1659
- return line && line.extensions.length ? line.extensions[0].exten : null;
1660
- }
1765
+ if (single.emailAddresses) {
1766
+ single.emailAddresses.map(email => emails.push({ email: email.address }));
1767
+ }
1661
1768
 
1662
- allLines() {
1663
- return this.profile ? this.profile.lines || [] : [];
1664
- }
1769
+ if (single.businessPhones) {
1770
+ single.businessPhones.map(phone => numbers.push({ label: 'business', number: phone }));
1771
+ }
1665
1772
 
1666
- allNumbers() {
1667
- const extensions = this.allLines().map(line => line.extensions.map(extension => extension.exten));
1668
- if (!extensions.length) {
1669
- return [];
1773
+ if (single.mobilePhone) {
1774
+ numbers.push({ label: 'mobile', number: single.mobilePhone });
1670
1775
  }
1671
1776
 
1672
- return extensions.reduce((a, b) => a.concat(b));
1673
- }
1777
+ if (single.homePhones) {
1778
+ single.homePhones.map(phone => numbers.push({ label: 'home', number: phone }));
1779
+ }
1674
1780
 
1675
- hasExtension(extension ) {
1676
- return this.allNumbers().some(number => number === extension);
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
+ });
1677
1790
  }
1678
1791
 
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;
1792
+ static parseManyGoogle(response , source ) {
1793
+ return response.map(r => Contact.parseGoogle(r, source));
1682
1794
  }
1683
- }
1684
1795
 
1685
- //
1686
- /* eslint-disable no-param-reassign */
1796
+ static parseGoogle(single , source ) {
1797
+ const emails = [];
1798
+ const numbers = [];
1687
1799
 
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];
1800
+ if (single.emails) {
1801
+ single.emails.forEach(email =>
1802
+ (typeof email === 'object' ? { email: email.address, label: email.label } : { email }));
1697
1803
  }
1698
- });
1699
- };
1700
1804
 
1701
- /* */
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
+ }
1702
1811
 
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
- );
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
+ });
1713
1821
  }
1714
1822
 
1715
- static fromText(response , status ) {
1716
- return new BadResponse(response, status);
1823
+ static parseManyWazo(response , source ) {
1824
+ return response.map(r => Contact.parseWazo(r, source));
1717
1825
  }
1718
1826
 
1719
-
1720
-
1721
-
1722
-
1723
-
1724
-
1827
+ static parseWazo(single , source ) {
1828
+ const emails = [];
1829
+ const numbers = [];
1725
1830
 
1726
- constructor(
1727
- message ,
1728
- status ,
1729
- timestamp = null,
1730
- errorId = null,
1731
- details = null,
1732
- error = null,
1733
- ) {
1734
- super(message);
1831
+ if (single.email) {
1832
+ emails.push({ label: 'email', email: single.email });
1833
+ }
1735
1834
 
1736
- this.timestamp = timestamp;
1737
- this.status = status;
1738
- this.errorId = errorId;
1739
- this.details = details;
1740
- this.error = error;
1741
- }
1742
- }
1835
+ if (single.exten) {
1836
+ numbers.push({ label: 'exten', number: single.exten });
1837
+ }
1743
1838
 
1744
- /* */
1839
+ if (single.mobile_phone_number) {
1840
+ numbers.push({ label: 'mobile', number: single.mobile_phone_number });
1841
+ }
1745
1842
 
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
- );
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
+ });
1755
1853
  }
1756
1854
 
1757
- static fromText(response , status ) {
1758
- return new ServerError(response, status);
1855
+ static parseManyConference(response , source ) {
1856
+ return response.map(r => Contact.parseConference(r, source));
1759
1857
  }
1760
- }
1761
-
1762
- /* */
1763
- /* global navigator */
1764
- const isMobile = () => typeof navigator !== 'undefined' && navigator.product === 'ReactNative';
1765
-
1766
- /* global window */
1767
-
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=';
1777
1858
 
1778
- const MAX_REMOTE_RETRY = 100;
1859
+ static parseConference(single , source ) {
1860
+ const numbers = [];
1861
+ let firstNumber = '';
1779
1862
 
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;
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
+ }
1787
1867
 
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]);
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
+ });
1795
1876
  }
1796
1877
 
1797
- return instance;
1798
- };
1878
+ static newFrom(contact ) {
1879
+ return newFrom(contact, Contact);
1880
+ }
1799
1881
 
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
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 || [];
1942
+
1943
+ // Useful to compare instead of instanceof with minified code
1944
+ this.type = 'Contact';
1806
1945
  }
1807
1946
 
1808
- return result;
1809
- };
1947
+ setFavorite(value ) {
1948
+ this.favorited = value;
1810
1949
 
1811
- class IssueReporter {
1812
-
1813
-
1814
-
1815
-
1816
-
1950
+ return this;
1951
+ }
1817
1952
 
1818
-
1819
-
1820
-
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;
1821
1956
 
1822
- constructor() {
1823
- addLevelsTo(this);
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;
1824
1960
 
1825
- this.oldConsoleMethods = null;
1826
- this.enabled = false;
1827
- this.remoteClientConfiguration = null;
1961
+ return !!other && hasSameId && hasSameBackend && hasSameSource;
1828
1962
  }
1829
1963
 
1830
- init() {
1831
- this._catchConsole();
1964
+ hasId(id ) {
1965
+ return this.uuid === id;
1832
1966
  }
1833
1967
 
1834
- configureRemoteClient(configuration = { tag: 'wazo-sdk', host: null, port: null, level: null, extra: {} }) {
1835
- this.remoteClientConfiguration = configuration;
1968
+ hasNumber(number ) {
1969
+ return this.number === number;
1836
1970
  }
1837
1971
 
1838
- enable() {
1839
- if (!this.oldConsoleMethods) {
1840
- this.init();
1841
- }
1842
-
1843
- this.enabled = true;
1972
+ hasEndpointId(endpointId ) {
1973
+ return this.endpointId === endpointId;
1844
1974
  }
1845
1975
 
1846
- disable() {
1847
- this.enabled = false;
1976
+ isAvailable() {
1977
+ return this.state === STATE.AVAILABLE;
1848
1978
  }
1849
1979
 
1850
- loggerFor(category ) {
1851
- const logger = (level , ...args ) => {
1852
- this.log.apply(this, [level, this._makeCategory(category), ...args]);
1853
- };
1854
-
1855
- return addLevelsTo(logger, true);
1980
+ isAway() {
1981
+ return this.state === STATE.AWAY;
1856
1982
  }
1857
1983
 
1858
- removeSlashes(str ) {
1859
- return str.replace(/"/g, "'").replace(/\\/g, '');
1984
+ isUnavailable() {
1985
+ return this.state === STATE.UNAVAILABLE;
1860
1986
  }
1861
1987
 
1862
- log(level , ...args ) {
1863
- if (!this.enabled) {
1864
- return;
1865
- }
1866
-
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
- }
1875
-
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
- }
1891
-
1892
- const date = moment__default['default']().format('YYYY-MM-DD HH:mm:ss.SSS');
1893
- const message = args.join(', ');
1894
- let consoleMessage = message;
1895
-
1896
- if (Object.keys(extra).length) {
1897
- const parsedExtra = safeStringify(extra);
1898
- consoleMessage = `${consoleMessage} (${parsedExtra})`;
1899
- }
1988
+ isInvisible() {
1989
+ return this.state === STATE.INVISIBLE;
1990
+ }
1900
1991
 
1901
- if (category) {
1902
- consoleMessage = `[${category}] ${consoleMessage}`;
1903
- }
1992
+ isInCall() {
1993
+ return this.lineState === LINE_STATE.TALKING || this.lineState === LINE_STATE.HOLDING;
1994
+ }
1904
1995
 
1905
- // Log the message in the console anyway (but don't console.error on mobile)
1906
- const consoleLevel = isMobile() && level === 'error' ? WARN : level;
1996
+ isRinging() {
1997
+ return this.ringing;
1998
+ }
1907
1999
 
1908
- // eslint-disable-next-line
1909
- const oldMethod = this.oldConsoleMethods[consoleLevel] || this.oldConsoleMethods.log;
1910
- oldMethod.apply(oldMethod, [date, consoleMessage]);
2000
+ isInUseOrRinging() {
2001
+ return this.lineState === LINE_STATE.TALKING || this.lineState === LINE_STATE.RINGING;
2002
+ }
1911
2003
 
1912
- this._sendToRemoteLogger(level, { date, message, category, ...extra });
2004
+ isProgressing() {
2005
+ return this.lineState === LINE_STATE.PROGRESSING;
1913
2006
  }
1914
2007
 
1915
- logRequest(url , options , response , start ) {
1916
- if (!this.enabled) {
1917
- return;
1918
- }
1919
- const { status } = response;
1920
- const duration = new Date() - start;
2008
+ merge(old ) {
2009
+ Object.keys(old).filter(key => key !== 'lineState').forEach(key => {
2010
+ // $FlowFixMe
2011
+ this[key] = old[key] || this[key];
2012
+ });
1921
2013
 
1922
- let level = TRACE;
1923
- if (status >= 400 && status < 500) {
1924
- level = WARN;
1925
- } else if (status >= 500) {
1926
- level = ERROR;
2014
+ if (old.lineState && !this.lineState) {
2015
+ this.lineState = old.lineState;
1927
2016
  }
1928
2017
 
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
- });
1936
- }
1937
-
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 [];
2018
+ return this;
1942
2019
  }
1943
2020
 
1944
- getParsedLogs() {
1945
- console.warn('IssueReporter\'s logs aren\'t stored anymore. Please use fluentd to store them');
1946
- return [];
2021
+ isIntern() {
2022
+ return !!this.uuid;
1947
2023
  }
1948
2024
 
1949
- getReport() {
1950
- return this.getParsedLogs().join('\r\n');
2025
+ isCallable(session ) {
2026
+ return !!this.number && !!session && !session.is(this);
1951
2027
  }
1952
2028
 
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
- });
2029
+ isFromMobile() {
2030
+ return this.source === SOURCE_MOBILE;
1969
2031
  }
1970
2032
 
1971
- _sendToRemoteLogger(level , payload , retry = 0) {
1972
- if (!this.remoteClientConfiguration || retry >= MAX_REMOTE_RETRY) {
1973
- return;
1974
- }
2033
+ isFavorite() {
2034
+ return this.favorited;
2035
+ }
1975
2036
 
1976
- const { tag, host, port, extra, level: minLevel } = this.remoteClientConfiguration;
1977
- if (!minLevel || this._isLevelAbove(minLevel, level)) {
1978
- return;
2037
+ separateName() {
2038
+ if (!this.name) {
2039
+ return {
2040
+ firstName: '',
2041
+ lastName: '',
2042
+ };
1979
2043
  }
2044
+ const names = this.name.split(/\s+/);
2045
+ const firstName = names[0];
2046
+ const lastName = names.slice(1).join(' ');
1980
2047
 
1981
- const isSecure = +port === 443;
1982
- const url = `http${isSecure ? 's' : ''}://${host}${isSecure ? '' : `:${port}`}/${tag}`;
1983
-
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
- });
2048
+ return {
2049
+ firstName,
2050
+ lastName,
2051
+ };
2002
2052
  }
2053
+ }
2003
2054
 
2004
- _isLevelAbove(level1 , level2 ) {
2005
- return LOG_LEVELS.indexOf(level1) > LOG_LEVELS.indexOf(level2);
2006
- }
2055
+ // Can't use `semver` package as Wazo version aren't in semver format
2007
2056
 
2008
- _makeCategory(category ) {
2009
- return `${CATEGORY_PREFIX}${category}`;
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);
2064
+
2065
+ for (i = 0; i < l; i++) {
2066
+ diff = parseInt(segmentsA[i], 10) - parseInt(segmentsB[i], 10);
2067
+ if (diff) {
2068
+ return diff;
2069
+ }
2010
2070
  }
2011
- }
2071
+ return segmentsA.length - segmentsB.length;
2072
+ };
2012
2073
 
2013
- var IssueReporter$1 = new IssueReporter();
2074
+ //
2014
2075
 
2015
- /* */
2076
+ const swarmKey = jsrsasign.KEYUTIL.getKey(swarmPublicKey);
2077
+ const MINIMUM_WAZO_ENGINE_VERSION_FOR_DEFAULT_CONTEXT = '19.08';
2016
2078
 
2017
-
2018
2079
 
2080
+
2019
2081
 
2082
+
2083
+
2084
+
2085
+
2086
+
2087
+
2088
+
2020
2089
 
2021
-
2022
-
2090
+
2091
+
2092
+
2093
+
2023
2094
 
2095
+
2096
+
2097
+
2098
+
2099
+
2100
+
2101
+
2102
+
2103
+
2024
2104
 
2025
2105
 
2026
- const methods = ['head', 'get', 'post', 'put', 'delete'];
2027
-
2028
- const logger$1 = IssueReporter$1 ? IssueReporter$1.loggerFor('api') : console;
2029
-
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;
2035
- }
2036
-
2037
- if (isMobile()) {
2038
- // React native
2039
- return fetch;
2040
- }
2041
-
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
- };
2106
+
2107
+
2108
+
2109
+
2110
+
2053
2111
 
2054
- class ApiRequester {
2055
-
2112
+
2113
+
2056
2114
 
2057
-
2058
2115
 
2116
+
2117
+
2118
+
2119
+
2120
+
2059
2121
 
2122
+
2123
+
2060
2124
 
2061
-
2062
-
2063
-
2125
+
2064
2126
 
2065
-
2127
+ class Session {
2066
2128
 
2067
-
2068
2129
 
2069
-
2070
-
2071
- // eslint-disable-next-line
2072
- static successResponseParser(response , isJson ) {
2073
- return response.status === 204 || response.status === 201 || response.status === 200;
2074
- }
2075
-
2076
- static defaultParser(response ) {
2077
- return response.json().then((data ) => data);
2078
- }
2079
-
2080
- static getQueryString(obj ) {
2081
- return Object.keys(obj)
2082
- .filter(key => obj[key])
2083
- .map(key => `${key}=${encodeURIComponent(obj[key])}`)
2084
- .join('&');
2085
- }
2130
+
2131
+
2132
+
2133
+
2134
+
2135
+
2136
+
2137
+
2138
+
2086
2139
 
2087
- static base64Encode(str ) {
2088
- return typeof btoa !== 'undefined' ? btoa(str) : jsBase64.Base64.encode(str);
2089
- }
2140
+ static parse(plain ) {
2141
+ const token = plain.data.metadata ? plain.data.metadata.jwt : null;
2142
+ let authorizations = [];
2090
2143
 
2091
- // @see https://github.com/facebook/flow/issues/183#issuecomment-358607052
2092
- constructor({ server, refreshTokenCallback, clientId, agent = null, token = null, fetchOptions } ) {
2093
- this.server = server;
2094
- this.agent = agent;
2095
- this.clientId = clientId;
2096
- this.refreshTokenCallback = refreshTokenCallback;
2097
- this.refreshTokenPromise = null;
2098
- this.fetchOptions = fetchOptions || {};
2144
+ // Add authorizations from JWT
2099
2145
  if (token) {
2100
- 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
+ }
2101
2151
  }
2102
2152
 
2103
- this.shouldLogErrors = true;
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,
2163
+ });
2164
+ }
2104
2165
 
2105
- methods.forEach(method => {
2106
- // $FlowFixMe
2107
- ApiRequester.prototype[method] = function sugar(...args) {
2108
- // Add method in arguments passed to `call`
2109
- args.splice(1, 0, method);
2166
+ static newFrom(profile ) {
2167
+ return newFrom(profile, Session);
2168
+ }
2110
2169
 
2111
- return this.call.call(this, ...args);
2112
- };
2113
- });
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
+ } = {}) {
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;
2114
2194
  }
2115
2195
 
2116
- setTenant(tenant ) {
2117
- this.tenant = tenant;
2196
+ hasExpired(date = new Date()) {
2197
+ return date >= this.expiresAt;
2118
2198
  }
2119
2199
 
2120
- setToken(token ) {
2121
- this.token = token;
2200
+ is(contact ) {
2201
+ return Boolean(contact) && this.uuid === contact.uuid;
2122
2202
  }
2123
2203
 
2124
- setFetchOptions(options ) {
2125
- this.fetchOptions = options;
2126
- }
2204
+ using(profile ) {
2205
+ this.profile = profile;
2127
2206
 
2128
- disableErrorLogging() {
2129
- this.shouldLogErrors = false;
2207
+ return this;
2130
2208
  }
2131
2209
 
2132
- enableErrorLogging() {
2133
- this.shouldLogErrors = true;
2210
+ hasAuthorizations() {
2211
+ return this.authorizations && !!this.authorizations.length;
2134
2212
  }
2135
2213
 
2136
- async call(
2137
- path ,
2138
- method = 'get',
2139
- body = null,
2140
- headers = null,
2141
- parse = ApiRequester.defaultParser,
2142
- firstCall = true,
2143
- ) {
2144
- const url = this.computeUrl(method, path, body);
2145
- const newHeaders = this.getHeaders(headers);
2146
- let newBody = method === 'get' ? null : body;
2147
- if (newBody && newHeaders['Content-Type'] === 'application/json') {
2148
- newBody = JSON.stringify(newBody);
2149
- }
2150
- const isHead = method === 'head';
2151
- const hasEmptyResponse = method === 'delete' || isHead;
2152
- const newParse = hasEmptyResponse ? ApiRequester.successResponseParser : parse;
2153
-
2154
- const fetchOptions = { ...this.fetchOptions || {} };
2155
- const extraHeaders = fetchOptions.headers || {};
2156
- delete fetchOptions.headers;
2157
-
2158
- const options = {
2159
- method,
2160
- body: newBody,
2161
- headers: { ...this.getHeaders(headers), ...extraHeaders },
2162
- agent: this.agent,
2163
- ...fetchOptions,
2164
- };
2214
+ displayName() {
2215
+ return this.profile ? `${this.profile.firstName} ${this.profile.lastName}` : '';
2216
+ }
2165
2217
 
2166
- if (this.refreshTokenPromise) {
2167
- logger$1.info('A token is already refreshing, waiting ...', { url });
2168
- await this.refreshTokenPromise;
2218
+ hasAccessToVoicemail() {
2219
+ if (!this.profile) {
2220
+ return false;
2169
2221
  }
2222
+ return !!this.profile.voicemail;
2223
+ }
2170
2224
 
2171
- const start = new Date();
2172
-
2173
- return realFetch()(url, options).then(response => {
2174
- const contentType = response.headers.get('content-type') || '';
2175
- const isJson = contentType.indexOf('application/json') !== -1;
2176
-
2177
- IssueReporter$1.logRequest(url, options, response, start);
2178
-
2179
- // Throw an error only if status >= 400
2180
- if ((isHead && response.status >= 500) || (!isHead && response.status >= 400)) {
2181
- const promise = isJson ? response.json() : response.text();
2182
- const exceptionClass = response.status >= 500 ? ServerError : BadResponse;
2183
-
2184
- return promise.then(async (err ) => {
2185
- // Check if the token is still valid
2186
- if (firstCall && this._checkTokenExpired(response, err)) {
2187
- logger$1.warn('token expired', { error: err.reason });
2188
- // Replay the call after refreshing the token
2189
- return this._replayWithNewToken(err, path, method, body, headers, parse);
2190
- }
2225
+ primaryLine() {
2226
+ return this.profile && this.profile.lines.length > 0 ? this.profile.lines[0] : null;
2227
+ }
2191
2228
 
2192
- const error = typeof err === 'string'
2193
- ? exceptionClass.fromText(err, response.status)
2194
- : exceptionClass.fromResponse(err, response.status);
2229
+ primarySipLine() {
2230
+ return this.profile && this.profile.sipLines.length > 0 ? this.profile.sipLines[0] : null;
2231
+ }
2195
2232
 
2196
- if (this.shouldLogErrors) {
2197
- logger$1.error('API error', error);
2198
- }
2233
+ primaryWebRtcLine() {
2234
+ return this.profile && (this.profile.sipLines || []).find(sipLine => sipLine && sipLine.isWebRtc());
2235
+ }
2199
2236
 
2200
- throw error;
2201
- });
2202
- }
2237
+ primaryCtiLine() {
2238
+ return this.profile && (this.profile.sipLines || []).find(sipLine => sipLine && !sipLine.isWebRtc());
2239
+ }
2203
2240
 
2204
- return newParse(response, isJson);
2205
- }).catch(error => {
2206
- if (this.shouldLogErrors) {
2207
- logger$1.error('Fetch failed', { url, options, message: error.message, stack: error.stack });
2241
+ primaryContext() {
2242
+ if (this.engineVersion) {
2243
+ if (this.hasEngineVersionGte(MINIMUM_WAZO_ENGINE_VERSION_FOR_DEFAULT_CONTEXT)) {
2244
+ return 'default';
2208
2245
  }
2246
+ }
2209
2247
 
2210
- throw error;
2211
- });
2212
- }
2213
-
2214
- _checkTokenExpired(response , err ) {
2215
- // Special case when authenticating form a token: we got a 404
2216
- const isTokenNotFound = response.status === 404 && this._isTokenNotFound(err);
2248
+ const line = this.primaryLine();
2217
2249
 
2218
- return response.status === 401 || isTokenNotFound;
2250
+ return line && line.extensions.length > 0 ? line.extensions[0].context : 'default';
2219
2251
  }
2220
2252
 
2221
- _isTokenNotFound(err ) {
2222
- return err && err.reason && err.reason[0] === 'No such token';
2253
+ hasEngineVersionGte(version ) {
2254
+ return this.engineVersion && compareVersions(String(this.engineVersion), String(version)) >= 0;
2223
2255
  }
2224
2256
 
2225
- _replayWithNewToken(
2226
- err ,
2227
- path ,
2228
- method ,
2229
- body = null,
2230
- headers = null,
2231
- parse ,
2232
- ) {
2233
- const isTokenNotFound = this._isTokenNotFound(err);
2234
- let newPath = path;
2235
- logger$1.info('refreshing token', { inProgress: !!this.refreshTokenPromise });
2236
-
2237
- this.refreshTokenPromise = this.refreshTokenPromise || this.refreshTokenCallback();
2257
+ primaryNumber() {
2258
+ const line = this.primaryLine();
2238
2259
 
2239
- return this.refreshTokenPromise.then(() => {
2240
- this.refreshTokenPromise = null;
2241
- logger$1.info('token refreshed', { isTokenNotFound });
2242
- if (isTokenNotFound) {
2243
- const pathParts = path.split('/');
2244
- pathParts.pop();
2245
- pathParts.push(this.token);
2246
- newPath = pathParts.join('/');
2247
- }
2260
+ return line && line.extensions.length ? line.extensions[0].exten : null;
2261
+ }
2248
2262
 
2249
- return this.call(newPath, method, body, headers, parse, false);
2250
- }).catch((e) => {
2251
- this.refreshTokenPromise = null;
2252
- throw e;
2253
- });
2263
+ allLines() {
2264
+ return this.profile ? this.profile.lines || [] : [];
2254
2265
  }
2255
2266
 
2256
- getHeaders(header ) {
2257
- if (header instanceof Object) {
2258
- return header;
2267
+ allNumbers() {
2268
+ const extensions = this.allLines().map(line => line.extensions.map(extension => extension.exten));
2269
+ if (!extensions.length) {
2270
+ return [];
2259
2271
  }
2260
2272
 
2261
- return {
2262
- 'X-Auth-Token': this.token,
2263
- ...(this.tenant ? { 'Wazo-Tenant': this.tenant } : null),
2264
- Accept: 'application/json',
2265
- 'Content-Type': 'application/json',
2266
- };
2273
+ return extensions.reduce((a, b) => a.concat(b));
2267
2274
  }
2268
2275
 
2269
- computeUrl(method , path , body ) {
2270
- const url = `${this.baseUrl}/${path}`;
2271
-
2272
- return method === 'get' && body && Object.keys(body).length ? `${url}?${ApiRequester.getQueryString(body)}` : url;
2276
+ hasExtension(extension ) {
2277
+ return this.allNumbers().some(number => number === extension);
2273
2278
  }
2274
2279
 
2275
- get baseUrl() {
2276
- 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;
2277
2283
  }
2278
2284
  }
2279
2285
 
@@ -2699,6 +2705,65 @@ class ExternalApp {
2699
2705
  }
2700
2706
  }
2701
2707
 
2708
+ //
2709
+
2710
+
2711
+
2712
+
2713
+
2714
+
2715
+
2716
+
2717
+
2718
+
2719
+
2720
+ class Meeting {
2721
+
2722
+
2723
+
2724
+
2725
+
2726
+
2727
+
2728
+
2729
+
2730
+
2731
+ static parse(plain ) {
2732
+ return new Meeting({
2733
+ guestSipAuthorization: plain.guest_sip_authorization,
2734
+ hostname: plain.hostname,
2735
+ name: plain.name,
2736
+ ownerUuids: plain.owner_uuids,
2737
+ port: plain.port,
2738
+ uuid: plain.uuid,
2739
+ number: plain.number,
2740
+ });
2741
+ }
2742
+
2743
+ static newFrom(meeting ) {
2744
+ return newFrom(meeting, Meeting);
2745
+ }
2746
+
2747
+ constructor({ uuid, name, guestSipAuthorization, ownerUuids, port, hostname, number } = {}) {
2748
+ this.guestSipAuthorization = guestSipAuthorization;
2749
+ this.hostname = hostname;
2750
+ this.name = name;
2751
+ this.ownerUuids = ownerUuids;
2752
+ this.port = port;
2753
+ this.uuid = uuid;
2754
+ this.number = number;
2755
+
2756
+ // Useful to compare instead of instanceof with minified code
2757
+ this.type = 'Meeting';
2758
+ }
2759
+
2760
+ getGuestSipCredentials() {
2761
+ // eslint-disable-next-line no-undef
2762
+ const [username, secret] = atob(this.guestSipAuthorization).split(':');
2763
+ return { username, secret };
2764
+ }
2765
+ }
2766
+
2702
2767
  /* */
2703
2768
 
2704
2769
  var confdMethods = (client , baseUrl ) => ({
@@ -2763,6 +2828,18 @@ var confdMethods = (client , baseUrl ) => ({
2763
2828
  return null;
2764
2829
  }
2765
2830
  },
2831
+
2832
+ createMyMeeting: (name ) =>
2833
+ client.post(`${baseUrl}/users/me/meetings`, { name }).then(Meeting.parse),
2834
+
2835
+ deleteMyMeeting: (meetingUuid ) =>
2836
+ client.delete(`${baseUrl}/users/me/meetings/${meetingUuid}`, null),
2837
+
2838
+ getMeeting: (meetingUuid ) =>
2839
+ client.get(`${baseUrl}/meetings/${meetingUuid}`, null).then(Meeting.parse),
2840
+
2841
+ guestGetMeeting: (meetingUuid ) =>
2842
+ client.get(`${baseUrl}/guests/me/meetings/${meetingUuid}`, null).then(Meeting.parse),
2766
2843
  });
2767
2844
 
2768
2845
  //
@@ -3141,7 +3218,11 @@ class CallLog {
3141
3218
  answered: plain.answered,
3142
3219
  callDirection: plain.call_direction,
3143
3220
  destination: {
3144
- extension: plain.requested_extension || plain.destination_extension,
3221
+ // @TEMP: Temporarily assuming empty numbers are meetings
3222
+ // which is admittedly a very dangerous assumption. Did i mention it was temporary?
3223
+ extension: plain.requested_extension
3224
+ || plain.destination_extension
3225
+ || `meeting-${plain.requested_name || plain.destination_name || ''}`,
3145
3226
  name: plain.requested_name || plain.destination_name || '',
3146
3227
  },
3147
3228
  source: {
@@ -3157,27 +3238,11 @@ class CallLog {
3157
3238
  }
3158
3239
 
3159
3240
  static parseNew(plain , session ) {
3160
- return new CallLog({
3161
- answer: plain.answer ? moment__default['default'](plain.answer).toDate() : null,
3162
- answered: plain.answered,
3163
- callDirection: plain.call_direction,
3164
- destination: {
3165
- extension: plain.requested_extension || plain.destination_extension,
3166
- name: plain.requested_name || plain.destination_name || '',
3167
- },
3168
- source: {
3169
- extension: plain.source_extension,
3170
- name: plain.source_name,
3171
- },
3172
- id: plain.id,
3173
- duration: (plain.duration || 0) * 1000, // duration is in seconds
3174
- start: moment__default['default'](plain.start).toDate(),
3175
- end: plain.end ? moment__default['default'](plain.end).toDate() : null,
3176
- recordings: Recording.parseMany(plain.recordings),
3177
- // @TODO: FIXME add verification declined vs missed call
3178
- newMissedCall: session
3179
- && session.hasExtension(plain.requested_extension || plain.destination_extension) && !plain.answered,
3180
- });
3241
+ const callLog = CallLog.parse(plain);
3242
+ // @TODO: FIXME add verification declined vs missed call
3243
+ callLog.newMissedCall = session
3244
+ && session.hasExtension(plain.requested_extension || plain.destination_extension) && !plain.answered;
3245
+ return callLog;
3181
3246
  }
3182
3247
 
3183
3248
  static newFrom(profile ) {
@@ -3919,6 +3984,9 @@ var calldMethods = (client , baseUrl ) => ({
3919
3984
  getConferenceParticipantsAsUser: async (conferenceId ) =>
3920
3985
  client.get(`${baseUrl}/users/me/conferences/${conferenceId}/participants`),
3921
3986
 
3987
+ getMeetingParticipantsAsUser: async (meetingUuid ) =>
3988
+ client.get(`${baseUrl}/users/me/meetings/${meetingUuid}/participants`),
3989
+
3922
3990
  listTrunks: () => client.get(`${baseUrl}/trunks`),
3923
3991
 
3924
3992
  mute: (callId ) =>