@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.
- package/dist/wazo-sdk.js +3 -3
- package/esm/__tests__/__voice__/call.test.js +72 -0
- package/esm/__tests__/__voice__/softphone.test.js +66 -0
- package/esm/api/auth.js +1 -5
- package/esm/checker/checks/webrtc.js +2 -1
- package/esm/domain/InvalidState.js +8 -0
- package/esm/domain/InvalidStateTransition.js +10 -0
- package/esm/domain/Phone/WebRTCPhone.js +33 -35
- package/esm/simple/Auth.js +12 -6
- package/esm/simple/Phone.js +2 -1
- package/esm/simple/__tests__/Auth.test.js +64 -8
- package/esm/simple/index.js +8 -2
- package/esm/simple/room/Room.js +2 -1
- package/esm/simple/room/SipRoom.js +2 -1
- package/esm/state-machine/call-state-machine.js +114 -0
- package/esm/state-machine/softphone-state-machine.js +39 -0
- package/esm/state-machine/utils.js +40 -0
- package/esm/types/__tests__/__voice__/call.test.d.ts +2 -0
- package/esm/types/__tests__/__voice__/call.test.d.ts.map +1 -0
- package/esm/types/__tests__/__voice__/softphone.test.d.ts +2 -0
- package/esm/types/__tests__/__voice__/softphone.test.d.ts.map +1 -0
- package/esm/types/api/auth.d.ts.map +1 -1
- package/esm/types/checker/checks/webrtc.d.ts.map +1 -1
- package/esm/types/domain/InvalidState.d.ts +6 -0
- package/esm/types/domain/InvalidState.d.ts.map +1 -0
- package/esm/types/domain/InvalidStateTransition.d.ts +7 -0
- package/esm/types/domain/InvalidStateTransition.d.ts.map +1 -0
- package/esm/types/domain/Phone/WebRTCPhone.d.ts +0 -1
- package/esm/types/domain/Phone/WebRTCPhone.d.ts.map +1 -1
- package/esm/types/domain/types.d.ts +4 -0
- package/esm/types/domain/types.d.ts.map +1 -1
- package/esm/types/simple/Auth.d.ts.map +1 -1
- package/esm/types/simple/Phone.d.ts.map +1 -1
- package/esm/types/simple/index.d.ts +7 -2
- package/esm/types/simple/index.d.ts.map +1 -1
- package/esm/types/simple/room/Room.d.ts.map +1 -1
- package/esm/types/simple/room/SipRoom.d.ts +1 -1
- package/esm/types/simple/room/SipRoom.d.ts.map +1 -1
- package/esm/types/state-machine/call-state-machine.d.ts +43 -0
- package/esm/types/state-machine/call-state-machine.d.ts.map +1 -0
- package/esm/types/state-machine/softphone-state-machine.d.ts +19 -0
- package/esm/types/state-machine/softphone-state-machine.d.ts.map +1 -0
- package/esm/types/state-machine/utils.d.ts +9 -0
- package/esm/types/state-machine/utils.d.ts.map +1 -0
- package/esm/types/utils/api-requester.d.ts.map +1 -1
- package/esm/types/utils/sdp.d.ts +6 -0
- package/esm/types/utils/sdp.d.ts.map +1 -1
- package/esm/types/utils/sip-logger.d.ts +4 -0
- package/esm/types/utils/sip-logger.d.ts.map +1 -0
- package/esm/types/utils/webrtc.d.ts +40 -0
- package/esm/types/utils/webrtc.d.ts.map +1 -1
- package/esm/types/voice/call.d.ts +110 -0
- package/esm/types/voice/call.d.ts.map +1 -0
- package/esm/types/voice/softphone.d.ts +77 -0
- package/esm/types/voice/softphone.d.ts.map +1 -0
- package/esm/types/web-rtc-client.d.ts +1 -36
- package/esm/types/web-rtc-client.d.ts.map +1 -1
- package/esm/utils/__tests__/api-requester.test.js +0 -17
- package/esm/utils/api-requester.js +1 -9
- package/esm/utils/sdp.js +26 -0
- package/esm/utils/sip-logger.js +32 -0
- package/esm/utils/webrtc.js +465 -0
- package/esm/voice/call.js +527 -0
- package/esm/voice/softphone.js +347 -0
- package/esm/web-rtc-client.js +86 -584
- package/lib/__tests__/__voice__/call.test.js +86 -0
- package/lib/__tests__/__voice__/softphone.test.js +80 -0
- package/lib/api/auth.js +1 -5
- package/lib/checker/checks/webrtc.js +2 -1
- package/lib/domain/InvalidState.js +9 -0
- package/lib/domain/InvalidStateTransition.js +10 -0
- package/lib/domain/Phone/WebRTCPhone.js +33 -35
- package/lib/simple/Auth.js +12 -6
- package/lib/simple/Phone.js +2 -1
- package/lib/simple/__tests__/Auth.test.js +64 -11
- package/lib/simple/index.js +6 -0
- package/lib/simple/room/Room.js +4 -3
- package/lib/simple/room/SipRoom.js +2 -1
- package/lib/state-machine/call-state-machine.js +120 -0
- package/lib/state-machine/softphone-state-machine.js +42 -0
- package/lib/state-machine/utils.js +52 -0
- package/lib/types/__tests__/__voice__/call.test.d.ts +2 -0
- package/lib/types/__tests__/__voice__/call.test.d.ts.map +1 -0
- package/lib/types/__tests__/__voice__/softphone.test.d.ts +2 -0
- package/lib/types/__tests__/__voice__/softphone.test.d.ts.map +1 -0
- package/lib/types/api/auth.d.ts.map +1 -1
- package/lib/types/checker/checks/webrtc.d.ts.map +1 -1
- package/lib/types/domain/InvalidState.d.ts +6 -0
- package/lib/types/domain/InvalidState.d.ts.map +1 -0
- package/lib/types/domain/InvalidStateTransition.d.ts +7 -0
- package/lib/types/domain/InvalidStateTransition.d.ts.map +1 -0
- package/lib/types/domain/Phone/WebRTCPhone.d.ts +0 -1
- package/lib/types/domain/Phone/WebRTCPhone.d.ts.map +1 -1
- package/lib/types/domain/types.d.ts +4 -0
- package/lib/types/domain/types.d.ts.map +1 -1
- package/lib/types/simple/Auth.d.ts.map +1 -1
- package/lib/types/simple/Phone.d.ts.map +1 -1
- package/lib/types/simple/index.d.ts +7 -2
- package/lib/types/simple/index.d.ts.map +1 -1
- package/lib/types/simple/room/Room.d.ts.map +1 -1
- package/lib/types/simple/room/SipRoom.d.ts +1 -1
- package/lib/types/simple/room/SipRoom.d.ts.map +1 -1
- package/lib/types/state-machine/call-state-machine.d.ts +43 -0
- package/lib/types/state-machine/call-state-machine.d.ts.map +1 -0
- package/lib/types/state-machine/softphone-state-machine.d.ts +19 -0
- package/lib/types/state-machine/softphone-state-machine.d.ts.map +1 -0
- package/lib/types/state-machine/utils.d.ts +9 -0
- package/lib/types/state-machine/utils.d.ts.map +1 -0
- package/lib/types/utils/api-requester.d.ts.map +1 -1
- package/lib/types/utils/sdp.d.ts +6 -0
- package/lib/types/utils/sdp.d.ts.map +1 -1
- package/lib/types/utils/sip-logger.d.ts +4 -0
- package/lib/types/utils/sip-logger.d.ts.map +1 -0
- package/lib/types/utils/webrtc.d.ts +40 -0
- package/lib/types/utils/webrtc.d.ts.map +1 -1
- package/lib/types/voice/call.d.ts +110 -0
- package/lib/types/voice/call.d.ts.map +1 -0
- package/lib/types/voice/softphone.d.ts +77 -0
- package/lib/types/voice/softphone.d.ts.map +1 -0
- package/lib/types/web-rtc-client.d.ts +1 -36
- package/lib/types/web-rtc-client.d.ts.map +1 -1
- package/lib/utils/__tests__/api-requester.test.js +0 -17
- package/lib/utils/api-requester.js +1 -9
- package/lib/utils/sdp.js +27 -1
- package/lib/utils/sip-logger.js +37 -0
- package/lib/utils/webrtc.js +521 -1
- package/lib/voice/call.js +577 -0
- package/lib/voice/softphone.js +375 -0
- package/lib/web-rtc-client.js +89 -601
- package/package.json +3 -2
- package/src/__tests__/__voice__/call.test.ts +90 -0
- package/src/__tests__/__voice__/softphone.test.ts +82 -0
- package/src/api/auth.ts +2 -13
- package/src/checker/checks/webrtc.ts +2 -1
- package/src/domain/InvalidState.ts +13 -0
- package/src/domain/InvalidStateTransition.ts +17 -0
- package/src/domain/Phone/WebRTCPhone.ts +34 -36
- package/src/domain/types.ts +6 -1
- package/src/simple/Auth.ts +13 -6
- package/src/simple/Phone.ts +2 -1
- package/src/simple/__tests__/Auth.test.ts +87 -9
- package/src/simple/index.ts +9 -2
- package/src/simple/room/Room.ts +2 -1
- package/src/simple/room/SipRoom.ts +2 -1
- package/src/state-machine/call-state-machine.ts +131 -0
- package/src/state-machine/softphone-state-machine.ts +49 -0
- package/src/state-machine/utils.ts +53 -0
- package/src/utils/__tests__/api-requester.test.ts +0 -20
- package/src/utils/api-requester.ts +1 -9
- package/src/utils/sdp.ts +36 -0
- package/src/utils/sip-logger.ts +40 -0
- package/src/utils/webrtc.ts +571 -0
- package/src/voice/call.ts +713 -0
- package/src/voice/softphone.ts +485 -0
- 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.
|
|
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
|
|
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 || !
|
|
39
|
+
if (!sipSession || !getSipSessionId(sipSession)) {
|
|
39
40
|
return handleError('Unable to make call through WebRTC');
|
|
40
41
|
}
|
|
41
42
|
|
|
@@ -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
|
-
|
|
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
|
|
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 &&
|
|
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 =
|
|
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:
|
|
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
|
|
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(
|
|
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(
|
|
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 =
|
|
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:
|
|
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:
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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 &&
|
|
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 =
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
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 ?
|
|
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[
|
|
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 =
|
|
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[
|
|
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:
|
|
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:
|
|
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(
|
|
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 =
|
|
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 ?
|
|
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 ?
|
|
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 =
|
|
1793
|
+
const sessionId = getSipSessionId(sipSession);
|
|
1796
1794
|
fromSession = fromSession || this.callSessions[sessionId];
|
|
1797
1795
|
const callSession = new CallSession({
|
|
1798
1796
|
callId: fromSession && fromSession.callId,
|
package/src/domain/types.ts
CHANGED
|
@@ -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>;
|
package/src/simple/Auth.ts
CHANGED
|
@@ -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
|
-
|
|
173
|
+
response = await getApiClient().auth.initiateIdpAuthentication(domain, redirectUrl);
|
|
173
174
|
} catch (e: any) {
|
|
174
|
-
logger.error('Error during IdP authentication
|
|
175
|
+
logger.error('Error during IdP authentication initiation:', e.message);
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
175
178
|
|
|
176
|
-
|
|
177
|
-
|
|
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 (
|
|
184
|
+
if (response.status === 500) {
|
|
181
185
|
throw new SamlConfigError('SAML/server configuration error');
|
|
182
186
|
}
|
|
183
187
|
|
|
184
|
-
|
|
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> {
|
package/src/simple/Phone.ts
CHANGED
|
@@ -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
|
|
336
|
+
return getSipSessionId(sipSession);
|
|
336
337
|
}
|
|
337
338
|
|
|
338
339
|
sendMessage(body: string, sipSession?: WazoSession, contentType = 'text/plain'): void {
|