@wazo/sdk 0.43.5 → 1.0.0-alpha.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 (155) hide show
  1. package/dist/wazo-sdk.js +3 -3
  2. package/esm/__tests__/__voice__/call.test.js +72 -0
  3. package/esm/__tests__/__voice__/softphone.test.js +66 -0
  4. package/esm/api/auth.js +1 -5
  5. package/esm/checker/checks/webrtc.js +2 -1
  6. package/esm/domain/InvalidState.js +8 -0
  7. package/esm/domain/InvalidStateTransition.js +10 -0
  8. package/esm/domain/Phone/WebRTCPhone.js +33 -35
  9. package/esm/simple/Auth.js +12 -6
  10. package/esm/simple/Phone.js +2 -1
  11. package/esm/simple/__tests__/Auth.test.js +64 -8
  12. package/esm/simple/index.js +8 -2
  13. package/esm/simple/room/Room.js +2 -1
  14. package/esm/simple/room/SipRoom.js +2 -1
  15. package/esm/state-machine/call-state-machine.js +114 -0
  16. package/esm/state-machine/softphone-state-machine.js +39 -0
  17. package/esm/state-machine/utils.js +40 -0
  18. package/esm/types/__tests__/__voice__/call.test.d.ts +2 -0
  19. package/esm/types/__tests__/__voice__/call.test.d.ts.map +1 -0
  20. package/esm/types/__tests__/__voice__/softphone.test.d.ts +2 -0
  21. package/esm/types/__tests__/__voice__/softphone.test.d.ts.map +1 -0
  22. package/esm/types/api/auth.d.ts.map +1 -1
  23. package/esm/types/checker/checks/webrtc.d.ts.map +1 -1
  24. package/esm/types/domain/InvalidState.d.ts +6 -0
  25. package/esm/types/domain/InvalidState.d.ts.map +1 -0
  26. package/esm/types/domain/InvalidStateTransition.d.ts +7 -0
  27. package/esm/types/domain/InvalidStateTransition.d.ts.map +1 -0
  28. package/esm/types/domain/Phone/WebRTCPhone.d.ts +0 -1
  29. package/esm/types/domain/Phone/WebRTCPhone.d.ts.map +1 -1
  30. package/esm/types/domain/types.d.ts +4 -0
  31. package/esm/types/domain/types.d.ts.map +1 -1
  32. package/esm/types/simple/Auth.d.ts.map +1 -1
  33. package/esm/types/simple/Phone.d.ts.map +1 -1
  34. package/esm/types/simple/index.d.ts +7 -2
  35. package/esm/types/simple/index.d.ts.map +1 -1
  36. package/esm/types/simple/room/Room.d.ts.map +1 -1
  37. package/esm/types/simple/room/SipRoom.d.ts +1 -1
  38. package/esm/types/simple/room/SipRoom.d.ts.map +1 -1
  39. package/esm/types/state-machine/call-state-machine.d.ts +43 -0
  40. package/esm/types/state-machine/call-state-machine.d.ts.map +1 -0
  41. package/esm/types/state-machine/softphone-state-machine.d.ts +19 -0
  42. package/esm/types/state-machine/softphone-state-machine.d.ts.map +1 -0
  43. package/esm/types/state-machine/utils.d.ts +9 -0
  44. package/esm/types/state-machine/utils.d.ts.map +1 -0
  45. package/esm/types/utils/api-requester.d.ts.map +1 -1
  46. package/esm/types/utils/sdp.d.ts +6 -0
  47. package/esm/types/utils/sdp.d.ts.map +1 -1
  48. package/esm/types/utils/sip-logger.d.ts +4 -0
  49. package/esm/types/utils/sip-logger.d.ts.map +1 -0
  50. package/esm/types/utils/webrtc.d.ts +40 -0
  51. package/esm/types/utils/webrtc.d.ts.map +1 -1
  52. package/esm/types/voice/call.d.ts +110 -0
  53. package/esm/types/voice/call.d.ts.map +1 -0
  54. package/esm/types/voice/softphone.d.ts +77 -0
  55. package/esm/types/voice/softphone.d.ts.map +1 -0
  56. package/esm/types/web-rtc-client.d.ts +1 -36
  57. package/esm/types/web-rtc-client.d.ts.map +1 -1
  58. package/esm/utils/__tests__/api-requester.test.js +0 -17
  59. package/esm/utils/api-requester.js +1 -9
  60. package/esm/utils/sdp.js +26 -0
  61. package/esm/utils/sip-logger.js +32 -0
  62. package/esm/utils/webrtc.js +465 -0
  63. package/esm/voice/call.js +527 -0
  64. package/esm/voice/softphone.js +347 -0
  65. package/esm/web-rtc-client.js +86 -584
  66. package/lib/__tests__/__voice__/call.test.js +86 -0
  67. package/lib/__tests__/__voice__/softphone.test.js +80 -0
  68. package/lib/api/auth.js +1 -5
  69. package/lib/checker/checks/webrtc.js +2 -1
  70. package/lib/domain/InvalidState.js +9 -0
  71. package/lib/domain/InvalidStateTransition.js +10 -0
  72. package/lib/domain/Phone/WebRTCPhone.js +33 -35
  73. package/lib/simple/Auth.js +12 -6
  74. package/lib/simple/Phone.js +2 -1
  75. package/lib/simple/__tests__/Auth.test.js +64 -11
  76. package/lib/simple/index.js +6 -0
  77. package/lib/simple/room/Room.js +4 -3
  78. package/lib/simple/room/SipRoom.js +2 -1
  79. package/lib/state-machine/call-state-machine.js +120 -0
  80. package/lib/state-machine/softphone-state-machine.js +42 -0
  81. package/lib/state-machine/utils.js +52 -0
  82. package/lib/types/__tests__/__voice__/call.test.d.ts +2 -0
  83. package/lib/types/__tests__/__voice__/call.test.d.ts.map +1 -0
  84. package/lib/types/__tests__/__voice__/softphone.test.d.ts +2 -0
  85. package/lib/types/__tests__/__voice__/softphone.test.d.ts.map +1 -0
  86. package/lib/types/api/auth.d.ts.map +1 -1
  87. package/lib/types/checker/checks/webrtc.d.ts.map +1 -1
  88. package/lib/types/domain/InvalidState.d.ts +6 -0
  89. package/lib/types/domain/InvalidState.d.ts.map +1 -0
  90. package/lib/types/domain/InvalidStateTransition.d.ts +7 -0
  91. package/lib/types/domain/InvalidStateTransition.d.ts.map +1 -0
  92. package/lib/types/domain/Phone/WebRTCPhone.d.ts +0 -1
  93. package/lib/types/domain/Phone/WebRTCPhone.d.ts.map +1 -1
  94. package/lib/types/domain/types.d.ts +4 -0
  95. package/lib/types/domain/types.d.ts.map +1 -1
  96. package/lib/types/simple/Auth.d.ts.map +1 -1
  97. package/lib/types/simple/Phone.d.ts.map +1 -1
  98. package/lib/types/simple/index.d.ts +7 -2
  99. package/lib/types/simple/index.d.ts.map +1 -1
  100. package/lib/types/simple/room/Room.d.ts.map +1 -1
  101. package/lib/types/simple/room/SipRoom.d.ts +1 -1
  102. package/lib/types/simple/room/SipRoom.d.ts.map +1 -1
  103. package/lib/types/state-machine/call-state-machine.d.ts +43 -0
  104. package/lib/types/state-machine/call-state-machine.d.ts.map +1 -0
  105. package/lib/types/state-machine/softphone-state-machine.d.ts +19 -0
  106. package/lib/types/state-machine/softphone-state-machine.d.ts.map +1 -0
  107. package/lib/types/state-machine/utils.d.ts +9 -0
  108. package/lib/types/state-machine/utils.d.ts.map +1 -0
  109. package/lib/types/utils/api-requester.d.ts.map +1 -1
  110. package/lib/types/utils/sdp.d.ts +6 -0
  111. package/lib/types/utils/sdp.d.ts.map +1 -1
  112. package/lib/types/utils/sip-logger.d.ts +4 -0
  113. package/lib/types/utils/sip-logger.d.ts.map +1 -0
  114. package/lib/types/utils/webrtc.d.ts +40 -0
  115. package/lib/types/utils/webrtc.d.ts.map +1 -1
  116. package/lib/types/voice/call.d.ts +110 -0
  117. package/lib/types/voice/call.d.ts.map +1 -0
  118. package/lib/types/voice/softphone.d.ts +77 -0
  119. package/lib/types/voice/softphone.d.ts.map +1 -0
  120. package/lib/types/web-rtc-client.d.ts +1 -36
  121. package/lib/types/web-rtc-client.d.ts.map +1 -1
  122. package/lib/utils/__tests__/api-requester.test.js +0 -17
  123. package/lib/utils/api-requester.js +1 -9
  124. package/lib/utils/sdp.js +27 -1
  125. package/lib/utils/sip-logger.js +37 -0
  126. package/lib/utils/webrtc.js +521 -1
  127. package/lib/voice/call.js +577 -0
  128. package/lib/voice/softphone.js +375 -0
  129. package/lib/web-rtc-client.js +89 -601
  130. package/package.json +3 -2
  131. package/src/__tests__/__voice__/call.test.ts +90 -0
  132. package/src/__tests__/__voice__/softphone.test.ts +82 -0
  133. package/src/api/auth.ts +2 -13
  134. package/src/checker/checks/webrtc.ts +2 -1
  135. package/src/domain/InvalidState.ts +13 -0
  136. package/src/domain/InvalidStateTransition.ts +17 -0
  137. package/src/domain/Phone/WebRTCPhone.ts +34 -36
  138. package/src/domain/types.ts +6 -1
  139. package/src/simple/Auth.ts +13 -6
  140. package/src/simple/Phone.ts +2 -1
  141. package/src/simple/__tests__/Auth.test.ts +87 -9
  142. package/src/simple/index.ts +9 -2
  143. package/src/simple/room/Room.ts +2 -1
  144. package/src/simple/room/SipRoom.ts +2 -1
  145. package/src/state-machine/call-state-machine.ts +131 -0
  146. package/src/state-machine/softphone-state-machine.ts +49 -0
  147. package/src/state-machine/utils.ts +53 -0
  148. package/src/utils/__tests__/api-requester.test.ts +0 -20
  149. package/src/utils/api-requester.ts +1 -9
  150. package/src/utils/sdp.ts +36 -0
  151. package/src/utils/sip-logger.ts +40 -0
  152. package/src/utils/webrtc.ts +571 -0
  153. package/src/voice/call.ts +713 -0
  154. package/src/voice/softphone.ts +485 -0
  155. package/src/web-rtc-client.ts +90 -690
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wazo/sdk",
3
- "version": "0.43.5",
3
+ "version": "1.0.0-alpha.0",
4
4
  "description": "Wazo's JavaScript Software Development Kit.",
5
5
  "main": "index.js",
6
6
  "types": "lib/types/index.d.ts",
@@ -59,7 +59,8 @@
59
59
  "reconnecting-websocket": "^4.4.0",
60
60
  "sdp-transform": "^2.14.2",
61
61
  "sip.js": "^0.21.2",
62
- "webrtc-adapter": "^8.2.3"
62
+ "webrtc-adapter": "^8.2.3",
63
+ "xstate": "^5.13.2"
63
64
  },
64
65
  "devDependencies": {
65
66
  "@babel/core": "^7.24.4",
@@ -0,0 +1,90 @@
1
+ /* eslint-disable no-underscore-dangle */
2
+ import { Inviter, UserAgent, type URI } from 'sip.js';
3
+
4
+ import softphone from '../../voice/softphone';
5
+ import Call from '../../voice/call';
6
+ import { Actions, States } from '../../state-machine/call-state-machine';
7
+ import { Actions as SoftphoneActions } from '../../state-machine/softphone-state-machine';
8
+
9
+ import InvalidStateTransition from '../../domain/InvalidStateTransition';
10
+
11
+ const uaOptions = {
12
+ logBuiltinEnabled: false,
13
+ transportOptions: {
14
+ traceSip: false,
15
+ wsServers: 'ws://localhost',
16
+ },
17
+ };
18
+
19
+ jest.mock('../..', () => ({
20
+ __esModule: true,
21
+ default: {
22
+ Auth: {
23
+ getHost: jest.fn(() => 'my-stack.io'),
24
+ getSession: jest.fn(() => ({
25
+ displayName: () => 'John Doe',
26
+ primaryWebRtcLine: () => ({}),
27
+ })),
28
+ },
29
+ },
30
+ }));
31
+
32
+ jest.mock('../../web-rtc-client', () => jest.fn().mockImplementation(() => ({
33
+ answer: jest.fn(() => Promise.resolve()),
34
+ hangup: jest.fn(() => Promise.resolve()),
35
+ register: jest.fn(() => Promise.resolve()),
36
+ setOnHeartbeatTimeout: jest.fn(() => Promise.resolve()),
37
+ setOnHeartbeatCallback: jest.fn(() => Promise.resolve()),
38
+ on: jest.fn(),
39
+ })));
40
+
41
+ const ua = new UserAgent(uaOptions);
42
+ const sipCall = new Inviter(ua, UserAgent.makeURI('sip:1234@my.stack.io') as URI);
43
+
44
+ softphone.connect();
45
+ softphone._sendAction(SoftphoneActions.REGISTER_DONE);
46
+
47
+ const invalidStateTransition = (state: string, action: string) => new InvalidStateTransition(`Invalid state transition from ${state} with action ${action}`, state, action);
48
+
49
+ describe('Call', () => {
50
+ describe('accept', () => {
51
+ it('Should throw an error when accepting a not ringing call', async () => {
52
+ const call = new Call(sipCall, softphone);
53
+
54
+ await expect(call.accept()).rejects.toThrowError(invalidStateTransition(States.IDLE, Actions.ACCEPT));
55
+ });
56
+
57
+ it.only('Should throw an error when the softphone is not registered', async () => {
58
+ const call = new Call(sipCall, softphone);
59
+ call._sendAction(Actions.INCOMING_CALL);
60
+ softphone._sendAction(SoftphoneActions.TRANSPORT_CLOSED);
61
+
62
+ await expect(call.accept()).rejects.toThrowError(invalidStateTransition(States.RINGING, Actions.ACCEPT));
63
+ });
64
+
65
+ it('Should not throw an error when accepting a not ringing call', async () => {
66
+ const call = new Call(sipCall, softphone);
67
+ call._sendAction(Actions.INCOMING_CALL);
68
+
69
+ await call.accept();
70
+ expect(softphone.client.answer).toHaveBeenCalled();
71
+ });
72
+ });
73
+
74
+ describe('hangup', () => {
75
+ it('Should throw an error when terminating a non established call', async () => {
76
+ const call = new Call(sipCall, softphone);
77
+
78
+ await expect(call.hangup()).rejects.toThrowError(invalidStateTransition(States.IDLE, Actions.HANGUP));
79
+ });
80
+
81
+ it('Should not throw an error when terminating an established call', async () => {
82
+ const call = new Call(sipCall, softphone);
83
+ call._sendAction(Actions.MAKE_CALL);
84
+ call._sendAction(Actions.REMOTLY_ACCEPTED);
85
+
86
+ await call.hangup();
87
+ expect(softphone.client.hangup).toHaveBeenCalled();
88
+ });
89
+ });
90
+ });
@@ -0,0 +1,82 @@
1
+ import { Softphone } from '../../voice/softphone';
2
+ import { Actions, States } from '../../state-machine/softphone-state-machine';
3
+
4
+ import Wazo from '../..';
5
+ import InvalidStateTransition from '../../domain/InvalidStateTransition';
6
+
7
+ const server = 'my.stack.io';
8
+ const displayName = 'John Doe';
9
+ const webRtcLine = {};
10
+ const session = {
11
+ uuid: '1234',
12
+ displayName: jest.fn(() => displayName),
13
+ primaryWebRtcLine: jest.fn(() => webRtcLine),
14
+ };
15
+
16
+ jest.mock('../..', () => ({
17
+ __esModule: true,
18
+ default: {
19
+ Auth: {
20
+ getHost: jest.fn(() => server),
21
+ getSession: jest.fn(() => session),
22
+ },
23
+ },
24
+ }));
25
+
26
+ jest.mock('../../web-rtc-client', () => jest.fn().mockImplementation(() => ({
27
+ register: jest.fn(() => Promise.resolve()),
28
+ unregister: jest.fn(() => Promise.resolve()),
29
+ setOnHeartbeatTimeout: jest.fn(() => Promise.resolve()),
30
+ setOnHeartbeatCallback: jest.fn(() => Promise.resolve()),
31
+ on: jest.fn(),
32
+ INVITE: 'invite',
33
+ })));
34
+
35
+ const invalidStateTransition = (state: string, action: string) => new InvalidStateTransition(`Invalid state transition from ${state} with action ${action}`, state, action);
36
+
37
+ describe('Softphone', () => {
38
+ describe('connect', () => {
39
+ it('Should retrieve information from Wazo.Auth', () => {
40
+ const instance = new Softphone();
41
+ jest.spyOn(instance, 'connectWithCredentials');
42
+ instance.connect({});
43
+
44
+ expect(Wazo.Auth.getHost).toHaveBeenCalled();
45
+ expect(Wazo.Auth.getSession).toHaveBeenCalled();
46
+ expect(instance.connectWithCredentials).toHaveBeenCalledWith(server, webRtcLine, displayName, { userUuid: session.uuid });
47
+ expect(instance.client.on).toHaveBeenCalledWith('invite', expect.anything());
48
+ });
49
+
50
+ it('Should throw an error when already connected', async () => {
51
+ const instance = new Softphone();
52
+ instance.softphoneActor.send({ type: Actions.REGISTER });
53
+
54
+ await expect(instance.connect({})).rejects.toThrowError(invalidStateTransition(States.REGISTERING, Actions.REGISTER));
55
+ });
56
+
57
+ it('Should not allow to register twice', async () => {
58
+ const instance = new Softphone();
59
+ instance.connect({});
60
+
61
+ await expect(instance.connect({})).rejects.toThrowError(invalidStateTransition(States.REGISTERING, Actions.REGISTER));
62
+ });
63
+ });
64
+
65
+ describe('disconnect', () => {
66
+ it('Should throw an error when not connected', async () => {
67
+ const instance = new Softphone();
68
+
69
+ await expect(instance.disconnect()).rejects.toThrowError(invalidStateTransition(States.UNREGISTERED, Actions.UNREGISTER));
70
+ });
71
+
72
+ it('Should unregister the client', async () => {
73
+ const instance = new Softphone();
74
+ instance.connect({});
75
+ instance.softphoneActor.send({ type: Actions.REGISTER_DONE });
76
+
77
+ await instance.disconnect();
78
+
79
+ expect(instance.client.unregister).toHaveBeenCalled();
80
+ });
81
+ });
82
+ });
package/src/api/auth.ts CHANGED
@@ -16,12 +16,6 @@ type LoginParams = {
16
16
  domainName?: string;
17
17
  };
18
18
 
19
- type SamlLoginBody = {
20
- saml_session_id: string,
21
- access_type?: 'online' | 'offline';
22
- client_id?: string;
23
- };
24
-
25
19
  type SendResetPasswordArgs = {
26
20
  username?: string,
27
21
  email?: string,
@@ -117,15 +111,10 @@ export default ((client: ApiRequester, baseUrl: string): AuthD => ({
117
111
 
118
112
  logOut: (token: Token): Promise<LogoutResponse> => client.delete(`${baseUrl}/token/${token}`, null, {}, ApiRequester.successResponseParser),
119
113
  samlLogIn: async (samlSessionId: string): Promise<Session | null | undefined> => {
120
- const body: SamlLoginBody = {
114
+ const body = {
121
115
  saml_session_id: samlSessionId,
122
116
  };
123
117
 
124
- if (client.clientId) {
125
- body.access_type = 'offline';
126
- body.client_id = client.clientId;
127
- }
128
-
129
118
  return client.post(`${baseUrl}/token`, body).then(Session.parse);
130
119
  },
131
120
  initiateIdpAuthentication: async (domain: string, redirectUrl: string) => {
@@ -139,7 +128,7 @@ export default ((client: ApiRequester, baseUrl: string): AuthD => ({
139
128
  'Content-Type': 'application/json',
140
129
  };
141
130
 
142
- return client.post(`${baseUrl}/saml/sso`, body, headers);
131
+ return client.post(`${baseUrl}/saml/sso`, body, headers, (response: any) => response);
143
132
  },
144
133
  refreshToken: (refreshToken: string, backend: string, expiration: number, isMobile?: boolean, tenantId?: string, domainName?: string): Promise<Session | null | undefined> => {
145
134
  const body: Record<string, any> = {
@@ -1,4 +1,5 @@
1
1
  /* global MediaStream */
2
+ import { getSipSessionId } from '../../utils/sdp';
2
3
  import WebRTCClient from '../../web-rtc-client';
3
4
 
4
5
  export default {
@@ -35,7 +36,7 @@ export default {
35
36
  client.on(client.REGISTERED, () => {
36
37
  const sipSession = client.call('*10');
37
38
 
38
- if (!sipSession || !client.getSipSessionId(sipSession)) {
39
+ if (!sipSession || !getSipSessionId(sipSession)) {
39
40
  return handleError('Unable to make call through WebRTC');
40
41
  }
41
42
 
@@ -0,0 +1,13 @@
1
+ class InvalidState extends Error {
2
+
3
+ state: string;
4
+
5
+ constructor(message: string, state: string) {
6
+ super(message);
7
+
8
+ this.state = state;
9
+ }
10
+
11
+ }
12
+
13
+ export default InvalidState;
@@ -0,0 +1,17 @@
1
+ class InvalidStateTransition extends Error {
2
+
3
+ state: string;
4
+
5
+ action: string;
6
+
7
+ constructor(message: string, action: string, state: string) {
8
+ super(message);
9
+
10
+ this.action = action;
11
+
12
+ this.state = state;
13
+ }
14
+
15
+ }
16
+
17
+ export default InvalidStateTransition;
@@ -12,6 +12,8 @@ import WazoWebRTCClient from '../../web-rtc-client';
12
12
  import Emitter from '../../utils/Emitter';
13
13
  import IssueReporter from '../../service/IssueReporter';
14
14
  import { PeerConnection, WazoSession } from '../types';
15
+ import { getSipSessionId } from '../../utils/sdp';
16
+ import { downgradeToAudio, getStreamFromConstraints, getWebRtcStats, setLocalMediaStream, upgradeToVideo } from '../../utils/webrtc';
15
17
 
16
18
  export const ON_USER_AGENT = 'onUserAgent';
17
19
  export const ON_REGISTERED = 'onRegistered';
@@ -282,7 +284,7 @@ export default class WebRTCPhone extends Emitter implements Phone {
282
284
  id: callSession ? callSession.getId() : null,
283
285
  withMessage,
284
286
  });
285
- this.client.downgradeToAudio(sipSession);
287
+ downgradeToAudio(sipSession);
286
288
 
287
289
  if (callSession) {
288
290
  callSession.cameraEnabled = false;
@@ -314,7 +316,7 @@ export default class WebRTCPhone extends Emitter implements Phone {
314
316
  const desktop = constraints && constraints.desktop;
315
317
  const options = sipSession.sessionDescriptionHandlerOptionsReInvite;
316
318
  const wasAudioOnly = (options as SessionDescriptionHandlerOptions & { audioOnly: boolean })?.audioOnly;
317
- const newStream = await this.client.upgradeToVideo(sipSession, constraints, isConference);
319
+ const newStream = await upgradeToVideo(sipSession, constraints, isConference);
318
320
 
319
321
  if (callSession) {
320
322
  callSession.cameraEnabled = true;
@@ -348,6 +350,7 @@ export default class WebRTCPhone extends Emitter implements Phone {
348
350
  id: callSession ? callSession.getId() : null,
349
351
  isUpgrade,
350
352
  });
353
+
351
354
  // Have to send the message after a delay due to latency to update the remote peer
352
355
  setTimeout(() => {
353
356
  this.sendMessage(sipSession, JSON.stringify({
@@ -355,17 +358,16 @@ export default class WebRTCPhone extends Emitter implements Phone {
355
358
  content: {
356
359
  type: ON_MESSAGE_TRACK_UPDATED,
357
360
  update: isUpgrade ? 'upgrade' : 'downgrade',
358
- sipCallId: sipSession && this.getSipSessionId(sipSession),
361
+ sipCallId: sipSession && getSipSessionId(sipSession),
359
362
  callId: callSession ? callSession.callId : null,
360
363
  number: callSession ? callSession.number : null,
361
- callerNumber: callSession ? callSession.callerNumber : null,
362
364
  },
363
365
  }));
364
366
  }, 2500);
365
367
  }
366
368
 
367
369
  _bindEvents(sipSession: WazoSession) {
368
- const sipSessionId = this.getSipSessionId(sipSession);
370
+ const sipSessionId = getSipSessionId(sipSession);
369
371
 
370
372
  if (sipSession instanceof Invitation) {
371
373
  // Monkey patch to know when canceled with the CANCEL message
@@ -433,7 +435,7 @@ export default class WebRTCPhone extends Emitter implements Phone {
433
435
  const [stream] = event.streams;
434
436
  const kind = event && event.track && event.track.kind;
435
437
  logger.info('on track stream called on the peer connection', {
436
- callId: this.getSipSessionId(sipSession),
438
+ callId: getSipSessionId(sipSession),
437
439
  streamId: stream ? stream.id : null,
438
440
  tracks: stream ? stream.getTracks() : null,
439
441
  kind,
@@ -460,7 +462,7 @@ export default class WebRTCPhone extends Emitter implements Phone {
460
462
  constraints,
461
463
  id: callSession ? callSession.getId() : null,
462
464
  });
463
- const screenShareStream = await this.client.getStreamFromConstraints(constraints);
465
+ const screenShareStream = await getStreamFromConstraints(constraints);
464
466
 
465
467
  if (!screenShareStream) {
466
468
  throw new Error(`Can't create media stream for screensharing with: ${JSON.stringify(constraints)}`);
@@ -468,9 +470,9 @@ export default class WebRTCPhone extends Emitter implements Phone {
468
470
 
469
471
  const screenTrack = screenShareStream.getVideoTracks()[0];
470
472
  const sipSession = this.currentSipSession;
471
- const pc = sipSession && this.client.getPeerConnection(this.getSipSessionId(sipSession));
473
+ const pc = sipSession && this.client.getPeerConnection(getSipSessionId(sipSession));
472
474
  const sender = pc && pc.getSenders && pc.getSenders().find((s) => s && s.track && s.track.kind === 'video');
473
- const localStream = sipSession && this.client.getLocalStream(this.getSipSessionId(sipSession));
475
+ const localStream = sipSession && this.client.getLocalStream(getSipSessionId(sipSession));
474
476
  const videoTrack = localStream ? localStream.getTracks().find(track => track.kind === 'video') : null;
475
477
  const hadVideo = !!videoTrack;
476
478
 
@@ -545,12 +547,12 @@ export default class WebRTCPhone extends Emitter implements Phone {
545
547
 
546
548
  _onScreenSharing(screenStream: MediaStream, sipSession: WazoSession, callSession: CallSession | null | undefined, hadVideo: boolean, desktop?: boolean | null | undefined) {
547
549
  const screenTrack = screenStream.getVideoTracks()[0];
548
- const sipSessionId = this.getSipSessionId(sipSession);
550
+ const sipSessionId = getSipSessionId(sipSession);
549
551
  const pc = this.client.getPeerConnection(sipSessionId);
550
552
  const sender = pc && pc.getSenders && pc.getSenders().find((s) => s && s.track && s.track.kind === 'video');
551
553
  logger.info('WebRTC phone - on screensharing', {
552
554
  hadVideo,
553
- id: this.getSipSessionId(sipSession),
555
+ id: getSipSessionId(sipSession),
554
556
  screenTrack,
555
557
  });
556
558
 
@@ -558,7 +560,7 @@ export default class WebRTCPhone extends Emitter implements Phone {
558
560
  screenTrack.onended = () => {
559
561
  logger.info('WebRTC phone - on screenshare ended', {
560
562
  hadVideo,
561
- id: this.getSipSessionId(sipSession),
563
+ id: getSipSessionId(sipSession),
562
564
  screenTrack,
563
565
  });
564
566
  this.eventEmitter.emit(ON_SHARE_SCREEN_ENDING, this._createCallSession(sipSession, callSession));
@@ -572,7 +574,7 @@ export default class WebRTCPhone extends Emitter implements Phone {
572
574
  sipSessionId,
573
575
  desktop,
574
576
  };
575
- this.client.setLocalMediaStream(this.getSipSessionId(sipSession), screenStream);
577
+ setLocalMediaStream(sipSession, screenStream);
576
578
  this.eventEmitter.emit(ON_SHARE_SCREEN_STARTED, this._createCallSession(sipSession, callSession, {
577
579
  screensharing: true,
578
580
  }), screenStream);
@@ -598,7 +600,7 @@ export default class WebRTCPhone extends Emitter implements Phone {
598
600
  this.currentSipSession = sipSession;
599
601
  this.currentCallSession = callSession;
600
602
  this.eventEmitter.emit(ON_TERMINATE_SOUND, callSession, 'call accepted');
601
- const sipSessionId = this.getSipSessionId(sipSession);
603
+ const sipSessionId = getSipSessionId(sipSession);
602
604
 
603
605
  if (sipSessionId) {
604
606
  this.removeIncomingSessions(sipSessionId);
@@ -670,7 +672,7 @@ export default class WebRTCPhone extends Emitter implements Phone {
670
672
  }
671
673
 
672
674
  _onCallTerminated(sipSession: WazoSession): boolean {
673
- const sipSessionId = this.getSipSessionId(sipSession);
675
+ const sipSessionId = getSipSessionId(sipSession);
674
676
 
675
677
  logger.info('WebRTC phone - on call terminated', {
676
678
  sipId: sipSession.id,
@@ -784,7 +786,7 @@ export default class WebRTCPhone extends Emitter implements Phone {
784
786
  return false;
785
787
  }
786
788
 
787
- return this.currentSipSession && this.getSipSessionId(this.currentSipSession) === callSession.getId();
789
+ return this.currentSipSession && getSipSessionId(this.currentSipSession) === callSession.getId();
788
790
  }
789
791
 
790
792
  hasVideo(callSession: CallSession): boolean {
@@ -976,7 +978,7 @@ export default class WebRTCPhone extends Emitter implements Phone {
976
978
  return new Promise((resolve, reject) => reject(new Error('No session to hold')));
977
979
  }
978
980
 
979
- const sessionId = this.getSipSessionId(sipSession);
981
+ const sessionId = getSipSessionId(sipSession);
980
982
  const hasVideo = this.client.hasLocalVideo(sessionId);
981
983
  logger.info('WebRTC hold sip session', {
982
984
  sipId: sipSession.id,
@@ -991,7 +993,7 @@ export default class WebRTCPhone extends Emitter implements Phone {
991
993
 
992
994
  // Downgrade to audio if needed
993
995
  if (hasVideo) {
994
- await this.client.downgradeToAudio(sipSession);
996
+ await downgradeToAudio(sipSession);
995
997
  }
996
998
 
997
999
  const isConference = !!callSession && callSession.isConference();
@@ -1010,7 +1012,7 @@ export default class WebRTCPhone extends Emitter implements Phone {
1010
1012
  }
1011
1013
 
1012
1014
  const isConference = !!callSession && callSession.isConference();
1013
- const sessionId = this.getSipSessionId(sipSession);
1015
+ const sessionId = getSipSessionId(sipSession);
1014
1016
  logger.info('WebRTC unhold sip session', {
1015
1017
  sessionId,
1016
1018
  isConference,
@@ -1029,7 +1031,7 @@ export default class WebRTCPhone extends Emitter implements Phone {
1029
1031
  screen: wasScreensharing,
1030
1032
  desktop: wasDesktop,
1031
1033
  };
1032
- await this.client.upgradeToVideo(sipSession, constraints, isConference);
1034
+ await upgradeToVideo(sipSession, constraints, isConference);
1033
1035
  }
1034
1036
 
1035
1037
  const onScreenSharing = (stream: MediaStream | null | undefined) => {
@@ -1292,7 +1294,7 @@ export default class WebRTCPhone extends Emitter implements Phone {
1292
1294
  console.error('Call is unknown to the WebRTC phone', callSession ? callSession.sipCallId : null, callSession ? callSession.callId : null);
1293
1295
  return false;
1294
1296
  }
1295
- const sipSessionId = this.getSipSessionId(sipSession);
1297
+ const sipSessionId = getSipSessionId(sipSession);
1296
1298
 
1297
1299
  logger.info('WebRTC hangup', {
1298
1300
  sipId: sipSession.id,
@@ -1313,7 +1315,7 @@ export default class WebRTCPhone extends Emitter implements Phone {
1313
1315
 
1314
1316
  async getStats(callSession?: CallSession): Promise<RTCStatsReport | null | undefined> {
1315
1317
  const sipSession = this.findSipSession(callSession);
1316
- return sipSession ? this.client.getStats(sipSession) : null;
1318
+ return sipSession ? getWebRtcStats(sipSession) : null;
1317
1319
  }
1318
1320
 
1319
1321
  startNetworkMonitoring(callSession?: CallSession, interval = 1000) {
@@ -1523,13 +1525,13 @@ export default class WebRTCPhone extends Emitter implements Phone {
1523
1525
  // if `_createCallSession` is called too soon `hasVideo` will return false because the stream doesn't exists yet
1524
1526
  setTimeout(() => {
1525
1527
  // Update callSession
1526
- this._createCallSession(sipSession, this.callSessions[this.getSipSessionId(sipSession)]);
1528
+ this._createCallSession(sipSession, this.callSessions[getSipSessionId(sipSession)]);
1527
1529
 
1528
1530
  this.eventEmitter.emit.apply(this.eventEmitter, [this.client.ON_REINVITE, ...args]);
1529
1531
  }, 2000);
1530
1532
  });
1531
1533
  this.client.on(this.client.ACCEPTED, async (sipSession: WazoSession) => {
1532
- const sessionId = this.getSipSessionId(sipSession);
1534
+ const sessionId = getSipSessionId(sipSession);
1533
1535
  const hasSession = (sessionId in this.callSessions);
1534
1536
  logger.info('WebRTC call accepted', {
1535
1537
  sipId: sessionId,
@@ -1561,7 +1563,7 @@ export default class WebRTCPhone extends Emitter implements Phone {
1561
1563
 
1562
1564
  // `_createCallSession` add the CallSession in `this.callSessions`.
1563
1565
  // so we have to remove it or `callCount` will always return 1 even if the call was rejected
1564
- delete this.callSessions[this.getSipSessionId(session)];
1566
+ delete this.callSessions[getSipSessionId(session)];
1565
1567
 
1566
1568
  callSession.endTime = new Date();
1567
1569
 
@@ -1581,7 +1583,7 @@ export default class WebRTCPhone extends Emitter implements Phone {
1581
1583
  const callSession = this._createCallSession(session, null, {
1582
1584
  incoming: false,
1583
1585
  ringing: true,
1584
- callId: this.getSipSessionId(session),
1586
+ callId: getSipSessionId(session),
1585
1587
  });
1586
1588
 
1587
1589
  this.eventEmitter.emit(ON_PROGRESS, callSession, this.audioOutputDeviceId, this.audioOutputVolume);
@@ -1592,7 +1594,7 @@ export default class WebRTCPhone extends Emitter implements Phone {
1592
1594
  const callSession = this._createCallSession(session, null, {
1593
1595
  incoming: false,
1594
1596
  ringing: true,
1595
- callId: this.getSipSessionId(session),
1597
+ callId: getSipSessionId(session),
1596
1598
  });
1597
1599
 
1598
1600
  this.eventEmitter.emit(ON_EARLY_MEDIA, callSession, this.audioOutputDeviceId, this.audioOutputVolume);
@@ -1653,12 +1655,12 @@ export default class WebRTCPhone extends Emitter implements Phone {
1653
1655
  this.eventEmitter.emit(ON_MESSAGE, message);
1654
1656
  });
1655
1657
  this.client.on(this.client.ON_NETWORK_STATS, (session, stats, previousStats) => {
1656
- const callSession = this.getCallSession(this.getSipSessionId(session));
1658
+ const callSession = this.getCallSession(getSipSessionId(session));
1657
1659
  this.eventEmitter.emit(ON_NETWORK_STATS, callSession, stats, previousStats);
1658
1660
  });
1659
1661
  // Used when upgrading directly in screenshare mode
1660
1662
  this.client.on(this.client.ON_SCREEN_SHARING_REINVITE, (sipSession: WazoSession, response: any, desktop: boolean) => {
1661
- const sipSessionId = this.getSipSessionId(sipSession);
1663
+ const sipSessionId = getSipSessionId(sipSession);
1662
1664
  const localStream = this.client.getLocalStream(sipSessionId);
1663
1665
  const callSession = this.callSessions[sipSessionId];
1664
1666
  logger.info('Updrading directly in screensharing mode', {
@@ -1676,7 +1678,7 @@ export default class WebRTCPhone extends Emitter implements Phone {
1676
1678
  const keyIndex = keys.findIndex(sessionId => callSession && callSession.isId(sessionId));
1677
1679
 
1678
1680
  if (keyIndex === -1) {
1679
- const currentSipSessionId = this.currentSipSession ? this.getSipSessionId(this.currentSipSession) : this.client.getSipSessionIds()[0];
1681
+ const currentSipSessionId = this.currentSipSession ? getSipSessionId(this.currentSipSession) : this.client.getSipSessionIds()[0];
1680
1682
  return currentSipSessionId ? this.client.getSipSession(currentSipSessionId) : null;
1681
1683
  }
1682
1684
 
@@ -1687,13 +1689,9 @@ export default class WebRTCPhone extends Emitter implements Phone {
1687
1689
  return this.callSessions[sipSessionId];
1688
1690
  }
1689
1691
 
1690
- getSipSessionId(sipSession: WazoSession): string {
1691
- return this.client.getSipSessionId(sipSession);
1692
- }
1693
-
1694
1692
  getSipSessionIdFromCallSession(callSession: CallSession): string | null {
1695
1693
  const sipSession = this.findSipSession(callSession);
1696
- return sipSession ? this.getSipSessionId(sipSession) : null;
1694
+ return sipSession ? getSipSessionId(sipSession) : null;
1697
1695
  }
1698
1696
 
1699
1697
  _updateCallSession(callSession: CallSession): void {
@@ -1792,7 +1790,7 @@ export default class WebRTCPhone extends Emitter implements Phone {
1792
1790
  const {
1793
1791
  state,
1794
1792
  } = sipSession || {};
1795
- const sessionId = this.getSipSessionId(sipSession);
1793
+ const sessionId = getSipSessionId(sipSession);
1796
1794
  fromSession = fromSession || this.callSessions[sessionId];
1797
1795
  const callSession = new CallSession({
1798
1796
  callId: fromSession && fromSession.callId,
@@ -264,12 +264,17 @@ export type PeerConnection = RTCPeerConnection & {
264
264
  addStream: (stream: MediaStream) => void;
265
265
  sfu: any;
266
266
  };
267
-
267
+ // @TODO, rename it to SipCall
268
268
  export type WazoSession = (Invitation | Inviter) & {
269
269
  remoteTag?: any;
270
270
  callId?: string;
271
271
  };
272
272
 
273
+ export type SipCall = (Invitation | Inviter) & {
274
+ remoteTag?: any;
275
+ callId?: string;
276
+ };
277
+
273
278
  export type WazoTransport = Transport & {
274
279
  configuration: Record<string, any>;
275
280
  connectPromise: Promise<any>;
@@ -168,21 +168,28 @@ export class Auth {
168
168
  }
169
169
 
170
170
  async initiateIdpAuthentication(domain: string, redirectUrl: string): Promise<{ location: string, saml_session_id: string } | undefined> {
171
+ let response;
171
172
  try {
172
- return await getApiClient().auth.initiateIdpAuthentication(domain, redirectUrl);
173
+ response = await getApiClient().auth.initiateIdpAuthentication(domain, redirectUrl);
173
174
  } catch (e: any) {
174
- logger.error('Error during IdP authentication initialization:', e.message);
175
+ logger.error('Error during IdP authentication initiation:', e.message);
176
+ return;
177
+ }
175
178
 
176
- if (e.status === 404) {
177
- throw new NoSamlRouteError('No route found for SAML SSO');
179
+ if (!response.ok) {
180
+ if (response.status === 404) {
181
+ throw new NoSamlRouteError('No route found for saml sso');
178
182
  }
179
183
 
180
- if (e.status === 500) {
184
+ if (response.status === 500) {
181
185
  throw new SamlConfigError('SAML/server configuration error');
182
186
  }
183
187
 
184
- throw e;
188
+ const error = await response.json();
189
+ throw new Error(error.message || error.reason);
185
190
  }
191
+
192
+ return response.json();
186
193
  }
187
194
 
188
195
  async logInViaRefreshToken(refreshToken: string): Promise<Session | null> {
@@ -12,6 +12,7 @@ import Emitter from '../utils/Emitter';
12
12
  import Wazo from './index';
13
13
  import SFUNotAvailableError from '../domain/SFUNotAvailableError';
14
14
  import { WazoSession, WebRtcConfig } from '../domain/types';
15
+ import { getSipSessionId } from '../utils/sdp';
15
16
 
16
17
  const logger = IssueReporter.loggerFor('simple-phone');
17
18
  const sipLogger = IssueReporter.loggerFor('sip.js');
@@ -332,7 +333,7 @@ export class Phone extends Emitter {
332
333
  if (!sipSession || !this.phone) {
333
334
  return null;
334
335
  }
335
- return this.phone.getSipSessionId(sipSession);
336
+ return getSipSessionId(sipSession);
336
337
  }
337
338
 
338
339
  sendMessage(body: string, sipSession?: WazoSession, contentType = 'text/plain'): void {