@wazo/sdk 0.41.1 → 0.42.0-rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/dist/wazo-sdk.js +42 -41913
  2. package/esm/__tests__/api-client.test.js +15 -5
  3. package/esm/api/confd.js +2 -0
  4. package/esm/domain/CallerID.js +24 -0
  5. package/esm/domain/Features.js +7 -1
  6. package/esm/index.js +1 -0
  7. package/esm/service/__tests__/IssueReporter.test.js +5 -1
  8. package/esm/types/api/confd.d.ts +2 -0
  9. package/esm/types/api/confd.d.ts.map +1 -1
  10. package/esm/types/domain/CallerID.d.ts +22 -0
  11. package/esm/types/domain/CallerID.d.ts.map +1 -0
  12. package/esm/types/domain/Features.d.ts +2 -0
  13. package/esm/types/domain/Features.d.ts.map +1 -1
  14. package/esm/types/index.d.ts +1 -0
  15. package/esm/types/index.d.ts.map +1 -1
  16. package/esm/types/service/getApiClient.d.ts.map +1 -1
  17. package/esm/types/simple/index.d.ts +2 -0
  18. package/esm/types/simple/index.d.ts.map +1 -1
  19. package/esm/types/utils/PhoneNumberUtil.d.ts +5 -6
  20. package/esm/types/utils/PhoneNumberUtil.d.ts.map +1 -1
  21. package/esm/types/utils/array.d.ts.map +1 -1
  22. package/esm/utils/PhoneNumberUtil.js +27 -19
  23. package/esm/utils/__tests__/PhoneNumberUtil.test.js +23 -6
  24. package/esm/utils/__tests__/api-requester.test.js +19 -15
  25. package/esm/web-rtc-client.js +1 -1
  26. package/esm/websocket-client.js +1 -1
  27. package/lib/__tests__/api-client.test.js +15 -5
  28. package/lib/api/confd.js +2 -0
  29. package/lib/checker/Checker.js +2 -2
  30. package/lib/domain/CallerID.js +27 -0
  31. package/lib/domain/Features.js +6 -1
  32. package/lib/domain/Phone/WebRTCPhone.js +12 -12
  33. package/lib/index.js +4 -2
  34. package/lib/service/CallApi.js +4 -4
  35. package/lib/service/__tests__/IssueReporter.test.js +5 -1
  36. package/lib/simple/Auth.js +5 -5
  37. package/lib/simple/Phone.js +9 -9
  38. package/lib/simple/room/Participant.js +2 -2
  39. package/lib/simple/room/Room.js +8 -8
  40. package/lib/simple/room/SipRoom.js +2 -2
  41. package/lib/simple/utils.js +1 -1
  42. package/lib/types/api/confd.d.ts +2 -0
  43. package/lib/types/api/confd.d.ts.map +1 -1
  44. package/lib/types/domain/CallerID.d.ts +22 -0
  45. package/lib/types/domain/CallerID.d.ts.map +1 -0
  46. package/lib/types/domain/Features.d.ts +2 -0
  47. package/lib/types/domain/Features.d.ts.map +1 -1
  48. package/lib/types/index.d.ts +1 -0
  49. package/lib/types/index.d.ts.map +1 -1
  50. package/lib/types/service/getApiClient.d.ts.map +1 -1
  51. package/lib/types/simple/index.d.ts +2 -0
  52. package/lib/types/simple/index.d.ts.map +1 -1
  53. package/lib/types/utils/PhoneNumberUtil.d.ts +5 -6
  54. package/lib/types/utils/PhoneNumberUtil.d.ts.map +1 -1
  55. package/lib/types/utils/array.d.ts.map +1 -1
  56. package/lib/utils/PhoneNumberUtil.js +28 -25
  57. package/lib/utils/__tests__/PhoneNumberUtil.test.js +22 -5
  58. package/lib/utils/__tests__/api-requester.test.js +19 -15
  59. package/lib/utils/api-requester.js +2 -2
  60. package/lib/web-rtc-client.js +23 -27
  61. package/lib/websocket-client.js +1 -1
  62. package/package.json +23 -21
  63. package/src/__tests__/api-client.test.ts +15 -5
  64. package/src/api/confd.ts +3 -0
  65. package/src/domain/CallerID.ts +49 -0
  66. package/src/domain/Features.ts +9 -1
  67. package/src/index.ts +1 -0
  68. package/src/service/__tests__/IssueReporter.test.ts +5 -1
  69. package/src/utils/PhoneNumberUtil.ts +31 -24
  70. package/src/utils/__tests__/PhoneNumberUtil.test.ts +31 -6
  71. package/src/utils/__tests__/api-requester.test.ts +20 -16
  72. package/src/web-rtc-client.ts +1 -1
  73. package/src/websocket-client.ts +1 -1
@@ -13,7 +13,7 @@ describe('Formatting phone numbers', () => {
13
13
  expect((0, PhoneNumberUtil_1.getDisplayableNumber)('+33675456545', 'FR')).toBe('06 75 45 65 45');
14
14
  expect((0, PhoneNumberUtil_1.getDisplayableNumber)('0675456545', 'FR')).toBe('06 75 45 65 45');
15
15
  expect((0, PhoneNumberUtil_1.getDisplayableNumber)('+33675456545', 'US')).toBe('+33 6 75 45 65 45');
16
- expect((0, PhoneNumberUtil_1.getDisplayableNumber)('0675456545', 'US')).toBe('+1 0675456545');
16
+ expect((0, PhoneNumberUtil_1.getDisplayableNumber)('0675456545', 'US')).toBe('+33 6 75 45 65 45');
17
17
  expect((0, PhoneNumberUtil_1.getDisplayableNumber)('4188884356', 'US')).toBe('(418) 888-4356');
18
18
  });
19
19
  it('should format real phone numbers when typing', () => {
@@ -21,12 +21,12 @@ describe('Formatting phone numbers', () => {
21
21
  expect((0, PhoneNumberUtil_1.getDisplayableNumber)('067545', 'FR', true)).toBe('06 75 45');
22
22
  expect((0, PhoneNumberUtil_1.getDisplayableNumber)('+3367545', 'US', true)).toBe('+33 6 75 45');
23
23
  expect((0, PhoneNumberUtil_1.getDisplayableNumber)('067545', 'US', true)).toBe('067545');
24
- expect((0, PhoneNumberUtil_1.getDisplayableNumber)('418808', 'US', true)).toBe('418-808');
24
+ expect((0, PhoneNumberUtil_1.getDisplayableNumber)('418808', 'US', true)).toBe('(418) 808');
25
25
  });
26
26
  it('should use the international number if the country number is different from the given country', () => {
27
- expect((0, PhoneNumberUtil_1.getDisplayableNumber)('00201005803648', 'FR')).toBe('+20 100 580 3648');
28
- expect((0, PhoneNumberUtil_1.getDisplayableNumber)('+14188004554', 'FR')).toBe('+1 418-800-4554');
29
- expect((0, PhoneNumberUtil_1.getDisplayableNumber)('00201005803648', 'EG')).toBe('0100 580 3648');
27
+ expect((0, PhoneNumberUtil_1.getDisplayableNumber)('00201005803648', 'FR')).toBe('+20 10 05803648');
28
+ expect((0, PhoneNumberUtil_1.getDisplayableNumber)('+14188004554', 'FR')).toBe('+1 418 800 4554');
29
+ expect((0, PhoneNumberUtil_1.getDisplayableNumber)('00201005803648', 'EG')).toBe('010 05803648');
30
30
  expect((0, PhoneNumberUtil_1.getDisplayableNumber)('0634543450', 'FR')).toBe('06 34 54 34 50');
31
31
  expect((0, PhoneNumberUtil_1.getDisplayableNumber)('4188034302', 'US')).toBe('(418) 803-4302');
32
32
  expect((0, PhoneNumberUtil_1.getDisplayableNumber)('8775863230', 'CA')).toBe('(877) 586-3230');
@@ -53,3 +53,20 @@ describe('getCallableNumber', () => {
53
53
  expect((0, PhoneNumberUtil_1.getCallableNumber)('9000')).toBe('9000');
54
54
  });
55
55
  });
56
+ describe('guessParsePhoneNumber', () => {
57
+ it('should guess with default countries', () => {
58
+ const parsedNumber = (0, PhoneNumberUtil_1.guessParsePhoneNumber)('0675456545');
59
+ expect(parsedNumber === null || parsedNumber === void 0 ? void 0 : parsedNumber.country).toBe('FR');
60
+ expect(parsedNumber === null || parsedNumber === void 0 ? void 0 : parsedNumber.isValid()).toBe(true);
61
+ });
62
+ it('should guess with specified countries', () => {
63
+ const parsedNumber = (0, PhoneNumberUtil_1.guessParsePhoneNumber)('752097514', undefined, ['JP']);
64
+ expect(parsedNumber === null || parsedNumber === void 0 ? void 0 : parsedNumber.country).toBe('JP');
65
+ expect(parsedNumber === null || parsedNumber === void 0 ? void 0 : parsedNumber.isValid()).toBe(true);
66
+ });
67
+ it('should used use defaultCountry has guesser', () => {
68
+ const parsedNumber = (0, PhoneNumberUtil_1.guessParsePhoneNumber)('752097514', 'JP', []);
69
+ expect(parsedNumber === null || parsedNumber === void 0 ? void 0 : parsedNumber.country).toBe('JP');
70
+ expect(parsedNumber === null || parsedNumber === void 0 ? void 0 : parsedNumber.isValid()).toBe(true);
71
+ });
72
+ });
@@ -88,12 +88,14 @@ describe('Retrieving headers', () => {
88
88
  });
89
89
  describe('Calling fetch', () => {
90
90
  it('should call fetch without body but query string in get method', () => __awaiter(void 0, void 0, void 0, function* () {
91
- global.fetch = jest.fn(() => Promise.resolve({
92
- json: () => Promise.resolve({}),
93
- headers: {
94
- get: () => '',
95
- },
96
- }));
91
+ Object.defineProperty(global, 'fetch', {
92
+ value: jest.fn(() => Promise.resolve({
93
+ json: () => Promise.resolve({}),
94
+ headers: {
95
+ get: () => '',
96
+ },
97
+ })),
98
+ });
97
99
  yield new api_requester_1.default({
98
100
  server,
99
101
  agent: null,
@@ -113,15 +115,17 @@ describe('Calling fetch', () => {
113
115
  describe('With a refresh token', () => {
114
116
  it('should retry the call with a new token', () => __awaiter(void 0, void 0, void 0, function* () {
115
117
  let calls = 0;
116
- global.fetch = jest.fn(() => {
117
- calls++;
118
- return Promise.resolve({
119
- headers: {
120
- get: () => 'application/json',
121
- },
122
- status: calls === 1 ? 401 : 200,
123
- json: () => Promise.resolve({}),
124
- });
118
+ Object.defineProperty(global, 'fetch', {
119
+ value: jest.fn(() => {
120
+ calls++;
121
+ return Promise.resolve({
122
+ headers: {
123
+ get: () => 'application/json',
124
+ },
125
+ status: calls === 1 ? 401 : 200,
126
+ json: () => Promise.resolve({}),
127
+ });
128
+ }),
125
129
  });
126
130
  const requester = new api_requester_1.default({
127
131
  server,
@@ -73,8 +73,8 @@ class ApiRequester {
73
73
  enableErrorLogging() {
74
74
  this.shouldLogErrors = true;
75
75
  }
76
- call(path, method = 'get', body = null, headers = null, parse = ApiRequester.defaultParser, firstCall = true) {
77
- return __awaiter(this, void 0, void 0, function* () {
76
+ call(path_1) {
77
+ return __awaiter(this, arguments, void 0, function* (path, method = 'get', body = null, headers = null, parse = ApiRequester.defaultParser, firstCall = true) {
78
78
  const url = this.computeUrl(method, path, body);
79
79
  const newHeaders = this.getHeaders(headers);
80
80
  let newBody = method === 'get' ? null : body;
@@ -249,8 +249,8 @@ class WebRTCClient extends Emitter_1.default {
249
249
  return this.register();
250
250
  }
251
251
  onDisconnect(error) {
252
- var _a;
253
252
  return __awaiter(this, void 0, void 0, function* () {
253
+ var _a;
254
254
  logger.info('sdk webrtc disconnected', { method: 'delegate.onConnect', clientId: this.clientId, error });
255
255
  this.connectionPromise = null;
256
256
  // The UA will attempt to reconnect automatically when an error occurred
@@ -266,9 +266,9 @@ class WebRTCClient extends Emitter_1.default {
266
266
  }
267
267
  });
268
268
  }
269
- register(tries = 0) {
270
- var _a;
271
- return __awaiter(this, void 0, void 0, function* () {
269
+ register() {
270
+ return __awaiter(this, arguments, void 0, function* (tries = 0) {
271
+ var _a;
272
272
  const logInfo = {
273
273
  clientId: this.clientId,
274
274
  userAgent: !!this.userAgent,
@@ -449,14 +449,10 @@ class WebRTCClient extends Emitter_1.default {
449
449
  conference,
450
450
  options,
451
451
  });
452
- const inviterOptions = {
453
- sessionDescriptionHandlerOptionsReInvite: {
452
+ const inviterOptions = Object.assign({ sessionDescriptionHandlerOptionsReInvite: {
454
453
  conference,
455
454
  audioOnly,
456
- },
457
- earlyMedia: true,
458
- options,
459
- };
455
+ }, earlyMedia: true }, options);
460
456
  if (audioOnly) {
461
457
  inviterOptions.sessionDescriptionHandlerModifiersReInvite = [modifiers_1.stripVideo];
462
458
  }
@@ -576,8 +572,8 @@ class WebRTCClient extends Emitter_1.default {
576
572
  });
577
573
  }
578
574
  getStats(session) {
579
- var _a;
580
575
  return __awaiter(this, void 0, void 0, function* () {
576
+ var _a;
581
577
  const pc = (_a = session.sessionDescriptionHandler) === null || _a === void 0 ? void 0 : _a.peerConnection;
582
578
  if (!pc) {
583
579
  return null;
@@ -628,8 +624,8 @@ class WebRTCClient extends Emitter_1.default {
628
624
  }
629
625
  });
630
626
  }
631
- close(force = false) {
632
- return __awaiter(this, void 0, void 0, function* () {
627
+ close() {
628
+ return __awaiter(this, arguments, void 0, function* (force = false) {
633
629
  logger.info('sdk webrtc closing client', {
634
630
  clientId: this.clientId,
635
631
  userAgent: !!this.userAgent,
@@ -817,8 +813,8 @@ class WebRTCClient extends Emitter_1.default {
817
813
  }
818
814
  // Returns true if a re-INVITE is required
819
815
  upgradeToVideo(session, constraints, isConference) {
820
- var _a, _b;
821
816
  return __awaiter(this, void 0, void 0, function* () {
817
+ var _a, _b;
822
818
  const pc = (_a = session.sessionDescriptionHandler) === null || _a === void 0 ? void 0 : _a.peerConnection;
823
819
  // Check if a video sender already exists
824
820
  let videoSender;
@@ -875,8 +871,8 @@ class WebRTCClient extends Emitter_1.default {
875
871
  localStream.removeTrack(videoTrack);
876
872
  });
877
873
  }
878
- getStreamFromConstraints(constraints, conference = false) {
879
- return __awaiter(this, void 0, void 0, function* () {
874
+ getStreamFromConstraints(constraints_1) {
875
+ return __awaiter(this, arguments, void 0, function* (constraints, conference = false) {
880
876
  const video = constraints && constraints.video;
881
877
  const { constraints: newConstraints, } = this.getMediaConfiguration(video, conference, constraints);
882
878
  let newStream = null;
@@ -2032,9 +2028,9 @@ class WebRTCClient extends Emitter_1.default {
2032
2028
  _makeURI(target) {
2033
2029
  return user_agent_1.UserAgent.makeURI(`sip:${target}@${this.config.host}`);
2034
2030
  }
2035
- _disconnectTransport(force = false) {
2036
- var _a;
2037
- return __awaiter(this, void 0, void 0, function* () {
2031
+ _disconnectTransport() {
2032
+ return __awaiter(this, arguments, void 0, function* (force = false) {
2033
+ var _a;
2038
2034
  const transport = (_a = this.userAgent) === null || _a === void 0 ? void 0 : _a.transport;
2039
2035
  if (force && transport) {
2040
2036
  // Bypass sip.js state machine that prevent to close WS with the state `Connecting`
@@ -2155,8 +2151,8 @@ class WebRTCClient extends Emitter_1.default {
2155
2151
  }
2156
2152
  });
2157
2153
  }
2158
- _accept(session, options = {}) {
2159
- return __awaiter(this, void 0, void 0, function* () {
2154
+ _accept(session_1) {
2155
+ return __awaiter(this, arguments, void 0, function* (session, options = {}) {
2160
2156
  if (session.state === session_state_1.SessionState.Terminated || session.state === session_state_1.SessionState.Terminating) {
2161
2157
  const error = 'Trying to accept a terminated sipSession.';
2162
2158
  logger.warn(error, { state: session.state, sessionId: session.id });
@@ -2181,8 +2177,8 @@ class WebRTCClient extends Emitter_1.default {
2181
2177
  }
2182
2178
  });
2183
2179
  }
2184
- _reject(session, options = {}) {
2185
- return __awaiter(this, void 0, void 0, function* () {
2180
+ _reject(session_1) {
2181
+ return __awaiter(this, arguments, void 0, function* (session, options = {}) {
2186
2182
  if (session.state !== session_state_1.SessionState.Initial && session.state !== session_state_1.SessionState.Establishing) {
2187
2183
  logger.warn('Trying to reject a session in a wrong state', { state: session.state, sessionId: session.id });
2188
2184
  return;
@@ -2200,8 +2196,8 @@ class WebRTCClient extends Emitter_1.default {
2200
2196
  }
2201
2197
  });
2202
2198
  }
2203
- _cancel(session, options = {}) {
2204
- return __awaiter(this, void 0, void 0, function* () {
2199
+ _cancel(session_1) {
2200
+ return __awaiter(this, arguments, void 0, function* (session, options = {}) {
2205
2201
  if (session.state !== session_state_1.SessionState.Initial && session.state !== session_state_1.SessionState.Establishing) {
2206
2202
  logger.warn('Trying to cancel a session in a wrong state', { state: session.state, sessionId: session.id });
2207
2203
  return;
@@ -2214,8 +2210,8 @@ class WebRTCClient extends Emitter_1.default {
2214
2210
  }
2215
2211
  });
2216
2212
  }
2217
- _bye(session, options = {}) {
2218
- return __awaiter(this, void 0, void 0, function* () {
2213
+ _bye(session_1) {
2214
+ return __awaiter(this, arguments, void 0, function* (session, options = {}) {
2219
2215
  if (session.state !== session_state_1.SessionState.Established) {
2220
2216
  logger.warn('Trying to end a session in a wrong state', { state: session.state, sessionId: session.id });
2221
2217
  return null;
@@ -220,7 +220,7 @@ class WebSocketClient extends Emitter_1.default {
220
220
  logger.warn('Trying to close an already closed websocket, bailing.', { url: this._getUrl() });
221
221
  return;
222
222
  }
223
- this.socket.close();
223
+ this.socket.close(1000);
224
224
  this.initialized = false;
225
225
  if (force) {
226
226
  this.host = null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wazo/sdk",
3
- "version": "0.41.1",
3
+ "version": "0.42.0-rc.0",
4
4
  "description": "Wazo's JavaScript Software Development Kit.",
5
5
  "main": "index.js",
6
6
  "types": "lib/types/index.d.ts",
@@ -50,27 +50,26 @@
50
50
  "events": "^3.3.0",
51
51
  "fstream": "^1.0.12",
52
52
  "getstats": "wazo-platform/getStats#fix_stats_mobile",
53
- "google-libphonenumber": "^3.2.32",
54
- "js-base64": "^3.7.5",
53
+ "js-base64": "^3.7.7",
55
54
  "json-to-graphql-query": "^2.2.5",
56
- "jsrsasign": "^11.0.0",
57
- "moment": "^2.29.1",
55
+ "jsrsasign": "^11.1.0",
56
+ "libphonenumber-js": "^1.10.60",
57
+ "moment": "^2.30.1",
58
58
  "reconnecting-websocket": "^4.4.0",
59
- "sdp-transform": "^2.14.1",
59
+ "sdp-transform": "^2.14.2",
60
60
  "sip.js": "^0.21.2",
61
61
  "webrtc-adapter": "^8.2.3"
62
62
  },
63
63
  "devDependencies": {
64
- "@babel/core": "^7.22.8",
65
- "@babel/preset-env": "^7.22.7",
66
- "@babel/preset-typescript": "^7.22.5",
67
- "@rollup/plugin-commonjs": "^25.0.2",
68
- "@rollup/plugin-json": "^6.0.0",
69
- "@rollup/plugin-node-resolve": "^15.1.0",
70
- "@rollup/plugin-terser": "^0.4.3",
71
- "@rollup/plugin-typescript": "^11.1.2",
72
- "@types/events": "^3.0.0",
73
- "@types/google-libphonenumber": "^7.4.23",
64
+ "@babel/core": "^7.24.4",
65
+ "@babel/preset-env": "^7.24.4",
66
+ "@babel/preset-typescript": "^7.24.1",
67
+ "@rollup/plugin-commonjs": "^25.0.7",
68
+ "@rollup/plugin-json": "^6.1.0",
69
+ "@rollup/plugin-node-resolve": "^15.2.3",
70
+ "@rollup/plugin-terser": "^0.4.4",
71
+ "@rollup/plugin-typescript": "^11.1.6",
72
+ "@types/events": "^3.0.3",
74
73
  "@types/jest": "^29.5.2",
75
74
  "@types/jsrsasign": "^10.5.8",
76
75
  "@types/node": "^20.4.0",
@@ -79,20 +78,23 @@
79
78
  "@typescript-eslint/parser": "^5.61.0",
80
79
  "babel-core": "^7.0.0-bridge.0",
81
80
  "babel-eslint": "10.1.0",
82
- "babel-loader": "^9.1.2",
81
+ "babel-loader": "^9.1.3",
83
82
  "eslint": "^8.44.0",
84
83
  "eslint-config-airbnb-base": "^15.0.0",
85
84
  "eslint-config-airbnb-typescript": "^17.0.0",
86
85
  "eslint-config-prettier": "^8.8.0",
87
- "eslint-plugin-import": "^2.27.5",
86
+ "eslint-plugin-import": "^2.29.1",
88
87
  "eslint-plugin-prettier": "^4.2.1",
89
88
  "jest": "^29.6.1",
90
89
  "jsonlint": "^1.6.3",
91
90
  "prettier-eslint-cli": "^7.1.0",
92
- "rimraf": "^5.0.1",
91
+ "rimraf": "^5.0.5",
93
92
  "rollup": "^3.26.2",
94
- "ts-jest": "^29.1.1",
95
- "typescript": "^5.1.6"
93
+ "ts-jest": "^29.1.2",
94
+ "typescript": "^5.4.5"
95
+ },
96
+ "resolutions": {
97
+ "underscore": "1.12.1"
96
98
  },
97
99
  "engines": {
98
100
  "node": ">=18.0.0"
@@ -87,7 +87,9 @@ client.setToken(token);
87
87
  describe('With correct API results', () => {
88
88
  beforeEach(() => {
89
89
  jest.resetModules();
90
- global.fetch = jest.fn(() => Promise.resolve(mockedJson)) as any;
90
+ Object.defineProperty(global, 'fetch', {
91
+ value: jest.fn(() => Promise.resolve(mockedJson) as any),
92
+ });
91
93
  });
92
94
 
93
95
  describe('logIn test', () => {
@@ -135,7 +137,9 @@ describe('With correct API results', () => {
135
137
  describe('With unAuthorized API results', () => {
136
138
  beforeEach(() => {
137
139
  jest.resetModules();
138
- global.fetch = jest.fn(() => Promise.resolve(mockedUnAuthorized)) as any;
140
+ Object.defineProperty(global, 'fetch', {
141
+ value: jest.fn(() => Promise.resolve(mockedUnAuthorized) as any),
142
+ });
139
143
  });
140
144
 
141
145
  describe('checkLogin test', () => {
@@ -157,7 +161,9 @@ describe('With unAuthorized API results', () => {
157
161
  describe('With not found API results', () => {
158
162
  beforeEach(() => {
159
163
  jest.resetModules();
160
- global.fetch = jest.fn(() => Promise.resolve(mockedNotFound)) as any;
164
+ Object.defineProperty(global, 'fetch', {
165
+ value: jest.fn(() => Promise.resolve(mockedNotFound) as any),
166
+ });
161
167
  });
162
168
 
163
169
  describe('fetchVoicemail test', () => {
@@ -192,7 +198,9 @@ describe('With not found API results', () => {
192
198
  describe('With erroneous text API results', () => {
193
199
  beforeEach(() => {
194
200
  jest.resetModules();
195
- global.fetch = jest.fn(() => Promise.resolve(mockedTextError)) as any;
201
+ Object.defineProperty(global, 'fetch', {
202
+ value: jest.fn(() => Promise.resolve(mockedTextError) as any),
203
+ });
196
204
  });
197
205
 
198
206
  it('throw an exception when the response is >= 500', async () => {
@@ -217,7 +225,9 @@ describe('With erroneous text API results', () => {
217
225
  describe('With erroneous json API results', () => {
218
226
  beforeEach(() => {
219
227
  jest.resetModules();
220
- global.fetch = jest.fn(() => Promise.resolve(mockedJsonError)) as any;
228
+ Object.defineProperty(global, 'fetch', {
229
+ value: jest.fn(() => Promise.resolve(mockedJsonError) as any),
230
+ });
221
231
  });
222
232
 
223
233
  it('throw an exception when the response is >= 500', async () => {
package/src/api/confd.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import ApiRequester from '../utils/api-requester';
2
2
  import type { UUID, ListConfdUsersResponse, ListApplicationsResponse } from '../domain/types';
3
3
  import type { MeetingCreateArguments, MeetingUpdateArguments } from '../domain/Meeting';
4
+ import CallerID from '../domain/CallerID';
4
5
  import Profile from '../domain/Profile';
5
6
  import SipLine from '../domain/SipLine';
6
7
  import ExternalApp from '../domain/ExternalApp';
@@ -35,6 +36,7 @@ export interface ConfD {
35
36
  guestGetMeeting: (meetingUuid: string) => Promise<Meeting>;
36
37
  guestAuthorizationRequest: (userUuid: string, meetingUuid: string, username: string) => Promise<any>;
37
38
  guestAuthorizationCheck: (userUuid: string, meetingUuid: string, authorizationUuid: string) => Promise<any>;
39
+ getOutgoingCallerIDs: (userUuid: string) => Promise<CallerID[]>;
38
40
  }
39
41
 
40
42
  export default ((client: ApiRequester, baseUrl: string): ConfD => ({
@@ -107,4 +109,5 @@ export default ((client: ApiRequester, baseUrl: string): ConfD => ({
107
109
  guest_name: username,
108
110
  }).then(MeetingAuthorization.parse),
109
111
  guestAuthorizationCheck: (userUuid: string, meetingUuid: string, authorizationUuid: string): Promise<any> => client.get(`${baseUrl}/guests/${userUuid}/meetings/${meetingUuid}/authorizations/${authorizationUuid}`, null),
112
+ getOutgoingCallerIDs: (userUuid: string): Promise<CallerID[]> => client.get(`${baseUrl}/users/${userUuid}/callerids/outgoing`, null).then(CallerID.parseMany),
110
113
  }));
@@ -0,0 +1,49 @@
1
+ import newFrom from '../utils/new-from';
2
+
3
+ type Response = {
4
+ number?: string;
5
+ type: 'main' | 'associated' | 'anonymous';
6
+ };
7
+
8
+ type ListResponse = {
9
+ items: Response[];
10
+ };
11
+
12
+ type Arguments = {
13
+ number?: string;
14
+ type: 'main' | 'associated' | 'anonymous';
15
+ };
16
+
17
+ export default class CallerID {
18
+ number?: string;
19
+
20
+ idType: 'main' | 'associated' | 'anonymous';
21
+
22
+ type: string;
23
+
24
+ static parse(plain: Response): CallerID {
25
+ return new CallerID({
26
+ number: plain.number,
27
+ type: plain.type,
28
+ });
29
+ }
30
+
31
+ static parseMany(plain: ListResponse): CallerID[] {
32
+ return plain.items.map(item => CallerID.parse(item));
33
+ }
34
+
35
+ static newFrom(profile: CallerID) {
36
+ return newFrom(profile, CallerID);
37
+ }
38
+
39
+ constructor({
40
+ type,
41
+ number,
42
+ }: Arguments) {
43
+ this.idType = type;
44
+ this.number = number;
45
+ // Useful to compare instead of instanceof with minified code
46
+ this.type = 'CallerID';
47
+ }
48
+
49
+ }
@@ -1,6 +1,6 @@
1
1
  import getApiClient from '../service/getApiClient';
2
2
 
3
- export const FEATURES = ['chat', 'video', 'call_recording', 'fax', 'mobile_double_call', 'mobile_gsm', 'meeting'];
3
+ export const FEATURES = ['chat', 'video', 'call_recording', 'fax', 'mobile_double_call', 'mobile_gsm', 'meeting', 'caller_id'];
4
4
  export const getScopeName = (featureName: string) => `enterprise.app.${featureName}`;
5
5
  const scopesToCheck = FEATURES.map(getScopeName); // Handle available features on the engine.
6
6
 
@@ -19,6 +19,8 @@ class Features {
19
19
 
20
20
  _hasMeeting: boolean;
21
21
 
22
+ _hasCallerID: boolean;
23
+
22
24
  constructor() {
23
25
  this._hasChat = true;
24
26
  this._hasVideo = true;
@@ -27,6 +29,7 @@ class Features {
27
29
  this._hasMobileDoubleCall = true;
28
30
  this._hasMobileGsm = true;
29
31
  this._hasMeeting = true;
32
+ this._hasCallerID = false;
30
33
  }
31
34
 
32
35
  async fetchAccess() {
@@ -51,6 +54,7 @@ class Features {
51
54
  this._hasMobileDoubleCall = this._hasFeatures(scopes, 'mobile_double_call');
52
55
  this._hasMobileGsm = this._hasFeatures(scopes, 'mobile_gsm');
53
56
  this._hasMeeting = this._hasFeatures(scopes, 'meeting');
57
+ this._hasCallerID = this._hasFeatures(scopes, 'caller_id');
54
58
  }
55
59
 
56
60
  hasChat() {
@@ -81,6 +85,10 @@ class Features {
81
85
  return this._hasMeeting;
82
86
  }
83
87
 
88
+ hasCallerID() {
89
+ return this._hasCallerID;
90
+ }
91
+
84
92
  _hasFeatures(scopes: Record<string, any>, featureName: string) {
85
93
  const scopeName = getScopeName(featureName);
86
94
 
package/src/index.ts CHANGED
@@ -24,6 +24,7 @@ export { default as Room } from './domain/Room';
24
24
  export { default as CallSession } from './domain/CallSession';
25
25
  export { default as IndirectTransfer } from './domain/IndirectTransfer';
26
26
  export { default as SwitchboardCall } from './domain/SwitchboardCall';
27
+ export { default as CallerID } from './domain/CallerID';
27
28
  export { default as WebRTCPhone } from './domain/Phone/WebRTCPhone';
28
29
  export { default as CTIPhone } from './domain/Phone/CTIPhone';
29
30
  export { default as Meeting, MeetingCreateArguments, MeetingUpdateArguments } from './domain/Meeting';
@@ -24,7 +24,11 @@ describe('IssueReporter', () => {
24
24
  });
25
25
  beforeEach(() => {
26
26
  jest.resetAllMocks();
27
- jest.spyOn(global, 'fetch');
27
+ Object.defineProperty(global, 'fetch', {
28
+ value: jest.fn(() => ({
29
+ catch: () => {},
30
+ })),
31
+ });
28
32
  });
29
33
  afterAll(() => {
30
34
  console.log = oldLog;
@@ -1,12 +1,8 @@
1
- import LibPhoneNumber from 'google-libphonenumber';
1
+ import { AsYouType, parsePhoneNumber as libParsePhoneNumber, CountryCode } from 'libphonenumber-js';
2
2
 
3
- const PhoneNumberUtil = LibPhoneNumber.PhoneNumberUtil.getInstance();
4
- const {
5
- PhoneNumberFormat,
6
- AsYouTypeFormatter,
7
- } = LibPhoneNumber;
8
3
  // eslint-disable-next-line
9
4
  const EXTRA_CHAR_REGEXP = /[^+*\d]/g;
5
+ const DEFAULT_GUESSING_COUNTRIES: CountryCode[] = ['US', 'FR', 'GB', 'AU'];
10
6
 
11
7
  const shouldBeFormatted = (number: string | null | undefined) => {
12
8
  if (!number || number.length <= 5) {
@@ -24,7 +20,23 @@ const isSameCountry = (country1: string, country2: string) => {
24
20
  return country1 === country2;
25
21
  };
26
22
 
27
- const getDisplayableNumber = (rawNumber: string, country: string, asYouType = false): string => {
23
+ const guessParsePhoneNumber = (number: string, defaultCountry?: CountryCode, guessingCountries: CountryCode[] = DEFAULT_GUESSING_COUNTRIES) => {
24
+ const mergedCountries = [...new Set([defaultCountry, ...guessingCountries])].filter(Boolean) as CountryCode[];
25
+
26
+ let parsedNumber;
27
+ for (let i = 0; i < mergedCountries.length; i++) {
28
+ const country = mergedCountries[i];
29
+
30
+ parsedNumber = libParsePhoneNumber(number, country);
31
+ if (parsedNumber.isValid()) {
32
+ break;
33
+ }
34
+ }
35
+
36
+ return parsedNumber;
37
+ };
38
+
39
+ const getDisplayableNumber = (rawNumber: string, country: CountryCode, asYouType = false, guessingCountries: CountryCode[] | undefined = DEFAULT_GUESSING_COUNTRIES): string => {
28
40
  if (!rawNumber) {
29
41
  return rawNumber;
30
42
  }
@@ -38,18 +50,16 @@ const getDisplayableNumber = (rawNumber: string, country: string, asYouType = fa
38
50
  let displayValue = '';
39
51
 
40
52
  if (asYouType) {
41
- const formatter = new AsYouTypeFormatter(country);
42
- number.split('').forEach(char => {
43
- displayValue = formatter.inputDigit(char);
44
- });
53
+ displayValue = new AsYouType(country).input(number);
45
54
  } else {
46
55
  try {
47
- const parsedNumber = PhoneNumberUtil.parseAndKeepRawInput(number, country);
48
- const numberCountry = PhoneNumberUtil.getRegionCodeForNumber(parsedNumber);
49
- const format = isSameCountry(String(numberCountry), country) ? PhoneNumberFormat.NATIONAL : PhoneNumberFormat.INTERNATIONAL;
50
- displayValue = PhoneNumberUtil.format(parsedNumber, format);
51
- } catch (_) {
52
- // Avoid to crash when phone number like `0080510` can't be parsed
56
+ const parsedNumber = guessParsePhoneNumber(number, country, guessingCountries);
57
+ if (parsedNumber?.isValid()) {
58
+ displayValue = isSameCountry(String(parsedNumber.country), country) ? parsedNumber.formatNational() : parsedNumber.formatInternational();
59
+ } else {
60
+ displayValue = rawNumber;
61
+ }
62
+ } catch (error) {
53
63
  displayValue = rawNumber;
54
64
  }
55
65
  }
@@ -59,16 +69,13 @@ const getDisplayableNumber = (rawNumber: string, country: string, asYouType = fa
59
69
 
60
70
  const parsePhoneNumber = (phoneNumber: string): string => phoneNumber.replace(EXTRA_CHAR_REGEXP, '');
61
71
 
62
- const getCallableNumber = (number: string, country?: string | null): string | null | undefined => {
72
+ const getCallableNumber = (number: string, country?: CountryCode): string => {
63
73
  try {
64
- if (country) {
65
- return getDisplayableNumber(number, country).replace(EXTRA_CHAR_REGEXP, '');
66
- }
67
-
68
- return parsePhoneNumber(number);
74
+ const callableNumber = country ? getDisplayableNumber(number, country) : number;
75
+ return parsePhoneNumber(callableNumber);
69
76
  } catch (_) {
70
77
  return number;
71
78
  }
72
79
  };
73
80
 
74
- export { PhoneNumberUtil, PhoneNumberFormat, parsePhoneNumber, AsYouTypeFormatter, getDisplayableNumber, getCallableNumber };
81
+ export { parsePhoneNumber, getDisplayableNumber, guessParsePhoneNumber, getCallableNumber, type CountryCode };