@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
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/* eslint-disable no-underscore-dangle */
|
|
2
|
+
import { Inviter, UserAgent } from 'sip.js';
|
|
3
|
+
import softphone from '../../voice/softphone';
|
|
4
|
+
import Call from '../../voice/call';
|
|
5
|
+
import { Actions, States } from '../../state-machine/call-state-machine';
|
|
6
|
+
import { Actions as SoftphoneActions } from '../../state-machine/softphone-state-machine';
|
|
7
|
+
import InvalidStateTransition from '../../domain/InvalidStateTransition';
|
|
8
|
+
const uaOptions = {
|
|
9
|
+
logBuiltinEnabled: false,
|
|
10
|
+
transportOptions: {
|
|
11
|
+
traceSip: false,
|
|
12
|
+
wsServers: 'ws://localhost',
|
|
13
|
+
},
|
|
14
|
+
};
|
|
15
|
+
jest.mock('../..', () => ({
|
|
16
|
+
__esModule: true,
|
|
17
|
+
default: {
|
|
18
|
+
Auth: {
|
|
19
|
+
getHost: jest.fn(() => 'my-stack.io'),
|
|
20
|
+
getSession: jest.fn(() => ({
|
|
21
|
+
displayName: () => 'John Doe',
|
|
22
|
+
primaryWebRtcLine: () => ({}),
|
|
23
|
+
})),
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
}));
|
|
27
|
+
jest.mock('../../web-rtc-client', () => jest.fn().mockImplementation(() => ({
|
|
28
|
+
answer: jest.fn(() => Promise.resolve()),
|
|
29
|
+
hangup: jest.fn(() => Promise.resolve()),
|
|
30
|
+
register: jest.fn(() => Promise.resolve()),
|
|
31
|
+
setOnHeartbeatTimeout: jest.fn(() => Promise.resolve()),
|
|
32
|
+
setOnHeartbeatCallback: jest.fn(() => Promise.resolve()),
|
|
33
|
+
on: jest.fn(),
|
|
34
|
+
})));
|
|
35
|
+
const ua = new UserAgent(uaOptions);
|
|
36
|
+
const sipCall = new Inviter(ua, UserAgent.makeURI('sip:1234@my.stack.io'));
|
|
37
|
+
softphone.connect();
|
|
38
|
+
softphone._sendAction(SoftphoneActions.REGISTER_DONE);
|
|
39
|
+
const invalidStateTransition = (state, action) => new InvalidStateTransition(`Invalid state transition from ${state} with action ${action}`, state, action);
|
|
40
|
+
describe('Call', () => {
|
|
41
|
+
describe('accept', () => {
|
|
42
|
+
it('Should throw an error when accepting a not ringing call', async () => {
|
|
43
|
+
const call = new Call(sipCall, softphone);
|
|
44
|
+
await expect(call.accept()).rejects.toThrowError(invalidStateTransition(States.IDLE, Actions.ACCEPT));
|
|
45
|
+
});
|
|
46
|
+
it.only('Should throw an error when the softphone is not registered', async () => {
|
|
47
|
+
const call = new Call(sipCall, softphone);
|
|
48
|
+
call._sendAction(Actions.INCOMING_CALL);
|
|
49
|
+
softphone._sendAction(SoftphoneActions.TRANSPORT_CLOSED);
|
|
50
|
+
await expect(call.accept()).rejects.toThrowError(invalidStateTransition(States.RINGING, Actions.ACCEPT));
|
|
51
|
+
});
|
|
52
|
+
it('Should not throw an error when accepting a not ringing call', async () => {
|
|
53
|
+
const call = new Call(sipCall, softphone);
|
|
54
|
+
call._sendAction(Actions.INCOMING_CALL);
|
|
55
|
+
await call.accept();
|
|
56
|
+
expect(softphone.client.answer).toHaveBeenCalled();
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
describe('hangup', () => {
|
|
60
|
+
it('Should throw an error when terminating a non established call', async () => {
|
|
61
|
+
const call = new Call(sipCall, softphone);
|
|
62
|
+
await expect(call.hangup()).rejects.toThrowError(invalidStateTransition(States.IDLE, Actions.HANGUP));
|
|
63
|
+
});
|
|
64
|
+
it('Should not throw an error when terminating an established call', async () => {
|
|
65
|
+
const call = new Call(sipCall, softphone);
|
|
66
|
+
call._sendAction(Actions.MAKE_CALL);
|
|
67
|
+
call._sendAction(Actions.REMOTLY_ACCEPTED);
|
|
68
|
+
await call.hangup();
|
|
69
|
+
expect(softphone.client.hangup).toHaveBeenCalled();
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
});
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { Softphone } from '../../voice/softphone';
|
|
2
|
+
import { Actions, States } from '../../state-machine/softphone-state-machine';
|
|
3
|
+
import Wazo from '../..';
|
|
4
|
+
import InvalidStateTransition from '../../domain/InvalidStateTransition';
|
|
5
|
+
const server = 'my.stack.io';
|
|
6
|
+
const displayName = 'John Doe';
|
|
7
|
+
const webRtcLine = {};
|
|
8
|
+
const session = {
|
|
9
|
+
uuid: '1234',
|
|
10
|
+
displayName: jest.fn(() => displayName),
|
|
11
|
+
primaryWebRtcLine: jest.fn(() => webRtcLine),
|
|
12
|
+
};
|
|
13
|
+
jest.mock('../..', () => ({
|
|
14
|
+
__esModule: true,
|
|
15
|
+
default: {
|
|
16
|
+
Auth: {
|
|
17
|
+
getHost: jest.fn(() => server),
|
|
18
|
+
getSession: jest.fn(() => session),
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
}));
|
|
22
|
+
jest.mock('../../web-rtc-client', () => jest.fn().mockImplementation(() => ({
|
|
23
|
+
register: jest.fn(() => Promise.resolve()),
|
|
24
|
+
unregister: jest.fn(() => Promise.resolve()),
|
|
25
|
+
setOnHeartbeatTimeout: jest.fn(() => Promise.resolve()),
|
|
26
|
+
setOnHeartbeatCallback: jest.fn(() => Promise.resolve()),
|
|
27
|
+
on: jest.fn(),
|
|
28
|
+
INVITE: 'invite',
|
|
29
|
+
})));
|
|
30
|
+
const invalidStateTransition = (state, action) => new InvalidStateTransition(`Invalid state transition from ${state} with action ${action}`, state, action);
|
|
31
|
+
describe('Softphone', () => {
|
|
32
|
+
describe('connect', () => {
|
|
33
|
+
it('Should retrieve information from Wazo.Auth', () => {
|
|
34
|
+
const instance = new Softphone();
|
|
35
|
+
jest.spyOn(instance, 'connectWithCredentials');
|
|
36
|
+
instance.connect({});
|
|
37
|
+
expect(Wazo.Auth.getHost).toHaveBeenCalled();
|
|
38
|
+
expect(Wazo.Auth.getSession).toHaveBeenCalled();
|
|
39
|
+
expect(instance.connectWithCredentials).toHaveBeenCalledWith(server, webRtcLine, displayName, { userUuid: session.uuid });
|
|
40
|
+
expect(instance.client.on).toHaveBeenCalledWith('invite', expect.anything());
|
|
41
|
+
});
|
|
42
|
+
it('Should throw an error when already connected', async () => {
|
|
43
|
+
const instance = new Softphone();
|
|
44
|
+
instance.softphoneActor.send({ type: Actions.REGISTER });
|
|
45
|
+
await expect(instance.connect({})).rejects.toThrowError(invalidStateTransition(States.REGISTERING, Actions.REGISTER));
|
|
46
|
+
});
|
|
47
|
+
it('Should not allow to register twice', async () => {
|
|
48
|
+
const instance = new Softphone();
|
|
49
|
+
instance.connect({});
|
|
50
|
+
await expect(instance.connect({})).rejects.toThrowError(invalidStateTransition(States.REGISTERING, Actions.REGISTER));
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
describe('disconnect', () => {
|
|
54
|
+
it('Should throw an error when not connected', async () => {
|
|
55
|
+
const instance = new Softphone();
|
|
56
|
+
await expect(instance.disconnect()).rejects.toThrowError(invalidStateTransition(States.UNREGISTERED, Actions.UNREGISTER));
|
|
57
|
+
});
|
|
58
|
+
it('Should unregister the client', async () => {
|
|
59
|
+
const instance = new Softphone();
|
|
60
|
+
instance.connect({});
|
|
61
|
+
instance.softphoneActor.send({ type: Actions.REGISTER_DONE });
|
|
62
|
+
await instance.disconnect();
|
|
63
|
+
expect(instance.client.unregister).toHaveBeenCalled();
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
});
|
package/esm/api/auth.js
CHANGED
|
@@ -36,10 +36,6 @@ export default ((client, baseUrl) => ({
|
|
|
36
36
|
const body = {
|
|
37
37
|
saml_session_id: samlSessionId,
|
|
38
38
|
};
|
|
39
|
-
if (client.clientId) {
|
|
40
|
-
body.access_type = 'offline';
|
|
41
|
-
body.client_id = client.clientId;
|
|
42
|
-
}
|
|
43
39
|
return client.post(`${baseUrl}/token`, body).then(Session.parse);
|
|
44
40
|
},
|
|
45
41
|
initiateIdpAuthentication: async (domain, redirectUrl) => {
|
|
@@ -51,7 +47,7 @@ export default ((client, baseUrl) => ({
|
|
|
51
47
|
Accept: 'application/json',
|
|
52
48
|
'Content-Type': 'application/json',
|
|
53
49
|
};
|
|
54
|
-
return client.post(`${baseUrl}/saml/sso`, body, headers);
|
|
50
|
+
return client.post(`${baseUrl}/saml/sso`, body, headers, (response) => response);
|
|
55
51
|
},
|
|
56
52
|
refreshToken: (refreshToken, backend, expiration, isMobile, tenantId, domainName) => {
|
|
57
53
|
const body = {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
/* global MediaStream */
|
|
2
|
+
import { getSipSessionId } from '../../utils/sdp';
|
|
2
3
|
import WebRTCClient from '../../web-rtc-client';
|
|
3
4
|
export default {
|
|
4
5
|
name: 'WebRTC',
|
|
@@ -28,7 +29,7 @@ export default {
|
|
|
28
29
|
});
|
|
29
30
|
client.on(client.REGISTERED, () => {
|
|
30
31
|
const sipSession = client.call('*10');
|
|
31
|
-
if (!sipSession || !
|
|
32
|
+
if (!sipSession || !getSipSessionId(sipSession)) {
|
|
32
33
|
return handleError('Unable to make call through WebRTC');
|
|
33
34
|
}
|
|
34
35
|
setTimeout(() => {
|
|
@@ -3,6 +3,8 @@ import { Invitation } from 'sip.js/lib/api';
|
|
|
3
3
|
import CallSession from '../CallSession';
|
|
4
4
|
import Emitter from '../../utils/Emitter';
|
|
5
5
|
import IssueReporter from '../../service/IssueReporter';
|
|
6
|
+
import { getSipSessionId } from '../../utils/sdp';
|
|
7
|
+
import { downgradeToAudio, getStreamFromConstraints, getWebRtcStats, setLocalMediaStream, upgradeToVideo } from '../../utils/webrtc';
|
|
6
8
|
export const ON_USER_AGENT = 'onUserAgent';
|
|
7
9
|
export const ON_REGISTERED = 'onRegistered';
|
|
8
10
|
export const ON_UNREGISTERED = 'onUnRegistered';
|
|
@@ -219,7 +221,7 @@ export default class WebRTCPhone extends Emitter {
|
|
|
219
221
|
id: callSession ? callSession.getId() : null,
|
|
220
222
|
withMessage,
|
|
221
223
|
});
|
|
222
|
-
|
|
224
|
+
downgradeToAudio(sipSession);
|
|
223
225
|
if (callSession) {
|
|
224
226
|
callSession.cameraEnabled = false;
|
|
225
227
|
this._updateCallSession(callSession);
|
|
@@ -244,7 +246,7 @@ export default class WebRTCPhone extends Emitter {
|
|
|
244
246
|
const desktop = constraints && constraints.desktop;
|
|
245
247
|
const options = sipSession.sessionDescriptionHandlerOptionsReInvite;
|
|
246
248
|
const wasAudioOnly = options?.audioOnly;
|
|
247
|
-
const newStream = await
|
|
249
|
+
const newStream = await upgradeToVideo(sipSession, constraints, isConference);
|
|
248
250
|
if (callSession) {
|
|
249
251
|
callSession.cameraEnabled = true;
|
|
250
252
|
this._updateCallSession(callSession);
|
|
@@ -277,16 +279,15 @@ export default class WebRTCPhone extends Emitter {
|
|
|
277
279
|
content: {
|
|
278
280
|
type: ON_MESSAGE_TRACK_UPDATED,
|
|
279
281
|
update: isUpgrade ? 'upgrade' : 'downgrade',
|
|
280
|
-
sipCallId: sipSession &&
|
|
282
|
+
sipCallId: sipSession && getSipSessionId(sipSession),
|
|
281
283
|
callId: callSession ? callSession.callId : null,
|
|
282
284
|
number: callSession ? callSession.number : null,
|
|
283
|
-
callerNumber: callSession ? callSession.callerNumber : null,
|
|
284
285
|
},
|
|
285
286
|
}));
|
|
286
287
|
}, 2500);
|
|
287
288
|
}
|
|
288
289
|
_bindEvents(sipSession) {
|
|
289
|
-
const sipSessionId =
|
|
290
|
+
const sipSessionId = getSipSessionId(sipSession);
|
|
290
291
|
if (sipSession instanceof Invitation) {
|
|
291
292
|
// Monkey patch to know when canceled with the CANCEL message
|
|
292
293
|
const onCancel = sipSession._onCancel.bind(sipSession);
|
|
@@ -340,7 +341,7 @@ export default class WebRTCPhone extends Emitter {
|
|
|
340
341
|
const [stream] = event.streams;
|
|
341
342
|
const kind = event && event.track && event.track.kind;
|
|
342
343
|
logger.info('on track stream called on the peer connection', {
|
|
343
|
-
callId:
|
|
344
|
+
callId: getSipSessionId(sipSession),
|
|
344
345
|
streamId: stream ? stream.id : null,
|
|
345
346
|
tracks: stream ? stream.getTracks() : null,
|
|
346
347
|
kind,
|
|
@@ -363,15 +364,15 @@ export default class WebRTCPhone extends Emitter {
|
|
|
363
364
|
constraints,
|
|
364
365
|
id: callSession ? callSession.getId() : null,
|
|
365
366
|
});
|
|
366
|
-
const screenShareStream = await
|
|
367
|
+
const screenShareStream = await getStreamFromConstraints(constraints);
|
|
367
368
|
if (!screenShareStream) {
|
|
368
369
|
throw new Error(`Can't create media stream for screensharing with: ${JSON.stringify(constraints)}`);
|
|
369
370
|
}
|
|
370
371
|
const screenTrack = screenShareStream.getVideoTracks()[0];
|
|
371
372
|
const sipSession = this.currentSipSession;
|
|
372
|
-
const pc = sipSession && this.client.getPeerConnection(
|
|
373
|
+
const pc = sipSession && this.client.getPeerConnection(getSipSessionId(sipSession));
|
|
373
374
|
const sender = pc && pc.getSenders && pc.getSenders().find((s) => s && s.track && s.track.kind === 'video');
|
|
374
|
-
const localStream = sipSession && this.client.getLocalStream(
|
|
375
|
+
const localStream = sipSession && this.client.getLocalStream(getSipSessionId(sipSession));
|
|
375
376
|
const videoTrack = localStream ? localStream.getTracks().find(track => track.kind === 'video') : null;
|
|
376
377
|
const hadVideo = !!videoTrack;
|
|
377
378
|
// Stop local video tracks
|
|
@@ -433,19 +434,19 @@ export default class WebRTCPhone extends Emitter {
|
|
|
433
434
|
}
|
|
434
435
|
_onScreenSharing(screenStream, sipSession, callSession, hadVideo, desktop) {
|
|
435
436
|
const screenTrack = screenStream.getVideoTracks()[0];
|
|
436
|
-
const sipSessionId =
|
|
437
|
+
const sipSessionId = getSipSessionId(sipSession);
|
|
437
438
|
const pc = this.client.getPeerConnection(sipSessionId);
|
|
438
439
|
const sender = pc && pc.getSenders && pc.getSenders().find((s) => s && s.track && s.track.kind === 'video');
|
|
439
440
|
logger.info('WebRTC phone - on screensharing', {
|
|
440
441
|
hadVideo,
|
|
441
|
-
id:
|
|
442
|
+
id: getSipSessionId(sipSession),
|
|
442
443
|
screenTrack,
|
|
443
444
|
});
|
|
444
445
|
if (screenTrack) {
|
|
445
446
|
screenTrack.onended = () => {
|
|
446
447
|
logger.info('WebRTC phone - on screenshare ended', {
|
|
447
448
|
hadVideo,
|
|
448
|
-
id:
|
|
449
|
+
id: getSipSessionId(sipSession),
|
|
449
450
|
screenTrack,
|
|
450
451
|
});
|
|
451
452
|
this.eventEmitter.emit(ON_SHARE_SCREEN_ENDING, this._createCallSession(sipSession, callSession));
|
|
@@ -458,7 +459,7 @@ export default class WebRTCPhone extends Emitter {
|
|
|
458
459
|
sipSessionId,
|
|
459
460
|
desktop,
|
|
460
461
|
};
|
|
461
|
-
|
|
462
|
+
setLocalMediaStream(sipSession, screenStream);
|
|
462
463
|
this.eventEmitter.emit(ON_SHARE_SCREEN_STARTED, this._createCallSession(sipSession, callSession, {
|
|
463
464
|
screensharing: true,
|
|
464
465
|
}), screenStream);
|
|
@@ -479,7 +480,7 @@ export default class WebRTCPhone extends Emitter {
|
|
|
479
480
|
this.currentSipSession = sipSession;
|
|
480
481
|
this.currentCallSession = callSession;
|
|
481
482
|
this.eventEmitter.emit(ON_TERMINATE_SOUND, callSession, 'call accepted');
|
|
482
|
-
const sipSessionId =
|
|
483
|
+
const sipSessionId = getSipSessionId(sipSession);
|
|
483
484
|
if (sipSessionId) {
|
|
484
485
|
this.removeIncomingSessions(sipSessionId);
|
|
485
486
|
}
|
|
@@ -538,7 +539,7 @@ export default class WebRTCPhone extends Emitter {
|
|
|
538
539
|
return this.currentSipSession && this.client.changeSessionVideoInputDevice(id, this.currentSipSession);
|
|
539
540
|
}
|
|
540
541
|
_onCallTerminated(sipSession) {
|
|
541
|
-
const sipSessionId =
|
|
542
|
+
const sipSessionId = getSipSessionId(sipSession);
|
|
542
543
|
logger.info('WebRTC phone - on call terminated', {
|
|
543
544
|
sipId: sipSession.id,
|
|
544
545
|
sipSessionId,
|
|
@@ -630,7 +631,7 @@ export default class WebRTCPhone extends Emitter {
|
|
|
630
631
|
if (!this.currentSipSession) {
|
|
631
632
|
return false;
|
|
632
633
|
}
|
|
633
|
-
return this.currentSipSession &&
|
|
634
|
+
return this.currentSipSession && getSipSessionId(this.currentSipSession) === callSession.getId();
|
|
634
635
|
}
|
|
635
636
|
hasVideo(callSession) {
|
|
636
637
|
return this.client.hasVideo(callSession.getId());
|
|
@@ -783,7 +784,7 @@ export default class WebRTCPhone extends Emitter {
|
|
|
783
784
|
if (!sipSession) {
|
|
784
785
|
return new Promise((resolve, reject) => reject(new Error('No session to hold')));
|
|
785
786
|
}
|
|
786
|
-
const sessionId =
|
|
787
|
+
const sessionId = getSipSessionId(sipSession);
|
|
787
788
|
const hasVideo = this.client.hasLocalVideo(sessionId);
|
|
788
789
|
logger.info('WebRTC hold sip session', {
|
|
789
790
|
sipId: sipSession.id,
|
|
@@ -796,7 +797,7 @@ export default class WebRTCPhone extends Emitter {
|
|
|
796
797
|
}
|
|
797
798
|
// Downgrade to audio if needed
|
|
798
799
|
if (hasVideo) {
|
|
799
|
-
await
|
|
800
|
+
await downgradeToAudio(sipSession);
|
|
800
801
|
}
|
|
801
802
|
const isConference = !!callSession && callSession.isConference();
|
|
802
803
|
const promise = this.client.hold(sipSession, isConference, hasVideo);
|
|
@@ -810,7 +811,7 @@ export default class WebRTCPhone extends Emitter {
|
|
|
810
811
|
return new Promise((resolve, reject) => reject(new Error('No session to unhold')));
|
|
811
812
|
}
|
|
812
813
|
const isConference = !!callSession && callSession.isConference();
|
|
813
|
-
const sessionId =
|
|
814
|
+
const sessionId = getSipSessionId(sipSession);
|
|
814
815
|
logger.info('WebRTC unhold sip session', {
|
|
815
816
|
sessionId,
|
|
816
817
|
isConference,
|
|
@@ -826,7 +827,7 @@ export default class WebRTCPhone extends Emitter {
|
|
|
826
827
|
screen: wasScreensharing,
|
|
827
828
|
desktop: wasDesktop,
|
|
828
829
|
};
|
|
829
|
-
await
|
|
830
|
+
await upgradeToVideo(sipSession, constraints, isConference);
|
|
830
831
|
}
|
|
831
832
|
const onScreenSharing = (stream) => {
|
|
832
833
|
const hadVideo = this.lastScreenShare && this.lastScreenShare.hadVideo;
|
|
@@ -1033,7 +1034,7 @@ export default class WebRTCPhone extends Emitter {
|
|
|
1033
1034
|
console.error('Call is unknown to the WebRTC phone', callSession ? callSession.sipCallId : null, callSession ? callSession.callId : null);
|
|
1034
1035
|
return false;
|
|
1035
1036
|
}
|
|
1036
|
-
const sipSessionId =
|
|
1037
|
+
const sipSessionId = getSipSessionId(sipSession);
|
|
1037
1038
|
logger.info('WebRTC hangup', {
|
|
1038
1039
|
sipId: sipSession.id,
|
|
1039
1040
|
sipSessionId,
|
|
@@ -1051,7 +1052,7 @@ export default class WebRTCPhone extends Emitter {
|
|
|
1051
1052
|
}
|
|
1052
1053
|
async getStats(callSession) {
|
|
1053
1054
|
const sipSession = this.findSipSession(callSession);
|
|
1054
|
-
return sipSession ?
|
|
1055
|
+
return sipSession ? getWebRtcStats(sipSession) : null;
|
|
1055
1056
|
}
|
|
1056
1057
|
startNetworkMonitoring(callSession, interval = 1000) {
|
|
1057
1058
|
const sipSession = this.findSipSession(callSession);
|
|
@@ -1220,12 +1221,12 @@ export default class WebRTCPhone extends Emitter {
|
|
|
1220
1221
|
// if `_createCallSession` is called too soon `hasVideo` will return false because the stream doesn't exists yet
|
|
1221
1222
|
setTimeout(() => {
|
|
1222
1223
|
// Update callSession
|
|
1223
|
-
this._createCallSession(sipSession, this.callSessions[
|
|
1224
|
+
this._createCallSession(sipSession, this.callSessions[getSipSessionId(sipSession)]);
|
|
1224
1225
|
this.eventEmitter.emit.apply(this.eventEmitter, [this.client.ON_REINVITE, ...args]);
|
|
1225
1226
|
}, 2000);
|
|
1226
1227
|
});
|
|
1227
1228
|
this.client.on(this.client.ACCEPTED, async (sipSession) => {
|
|
1228
|
-
const sessionId =
|
|
1229
|
+
const sessionId = getSipSessionId(sipSession);
|
|
1229
1230
|
const hasSession = (sessionId in this.callSessions);
|
|
1230
1231
|
logger.info('WebRTC call accepted', {
|
|
1231
1232
|
sipId: sessionId,
|
|
@@ -1252,7 +1253,7 @@ export default class WebRTCPhone extends Emitter {
|
|
|
1252
1253
|
const callSession = this._createCallSession(session);
|
|
1253
1254
|
// `_createCallSession` add the CallSession in `this.callSessions`.
|
|
1254
1255
|
// so we have to remove it or `callCount` will always return 1 even if the call was rejected
|
|
1255
|
-
delete this.callSessions[
|
|
1256
|
+
delete this.callSessions[getSipSessionId(session)];
|
|
1256
1257
|
callSession.endTime = new Date();
|
|
1257
1258
|
this.eventEmitter.emit(ON_CALL_REJECTED, callSession);
|
|
1258
1259
|
});
|
|
@@ -1269,7 +1270,7 @@ export default class WebRTCPhone extends Emitter {
|
|
|
1269
1270
|
const callSession = this._createCallSession(session, null, {
|
|
1270
1271
|
incoming: false,
|
|
1271
1272
|
ringing: true,
|
|
1272
|
-
callId:
|
|
1273
|
+
callId: getSipSessionId(session),
|
|
1273
1274
|
});
|
|
1274
1275
|
this.eventEmitter.emit(ON_PROGRESS, callSession, this.audioOutputDeviceId, this.audioOutputVolume);
|
|
1275
1276
|
});
|
|
@@ -1278,7 +1279,7 @@ export default class WebRTCPhone extends Emitter {
|
|
|
1278
1279
|
const callSession = this._createCallSession(session, null, {
|
|
1279
1280
|
incoming: false,
|
|
1280
1281
|
ringing: true,
|
|
1281
|
-
callId:
|
|
1282
|
+
callId: getSipSessionId(session),
|
|
1282
1283
|
});
|
|
1283
1284
|
this.eventEmitter.emit(ON_EARLY_MEDIA, callSession, this.audioOutputDeviceId, this.audioOutputVolume);
|
|
1284
1285
|
});
|
|
@@ -1334,12 +1335,12 @@ export default class WebRTCPhone extends Emitter {
|
|
|
1334
1335
|
this.eventEmitter.emit(ON_MESSAGE, message);
|
|
1335
1336
|
});
|
|
1336
1337
|
this.client.on(this.client.ON_NETWORK_STATS, (session, stats, previousStats) => {
|
|
1337
|
-
const callSession = this.getCallSession(
|
|
1338
|
+
const callSession = this.getCallSession(getSipSessionId(session));
|
|
1338
1339
|
this.eventEmitter.emit(ON_NETWORK_STATS, callSession, stats, previousStats);
|
|
1339
1340
|
});
|
|
1340
1341
|
// Used when upgrading directly in screenshare mode
|
|
1341
1342
|
this.client.on(this.client.ON_SCREEN_SHARING_REINVITE, (sipSession, response, desktop) => {
|
|
1342
|
-
const sipSessionId =
|
|
1343
|
+
const sipSessionId = getSipSessionId(sipSession);
|
|
1343
1344
|
const localStream = this.client.getLocalStream(sipSessionId);
|
|
1344
1345
|
const callSession = this.callSessions[sipSessionId];
|
|
1345
1346
|
logger.info('Updrading directly in screensharing mode', {
|
|
@@ -1354,7 +1355,7 @@ export default class WebRTCPhone extends Emitter {
|
|
|
1354
1355
|
const keys = this.client.getSipSessionIds();
|
|
1355
1356
|
const keyIndex = keys.findIndex(sessionId => callSession && callSession.isId(sessionId));
|
|
1356
1357
|
if (keyIndex === -1) {
|
|
1357
|
-
const currentSipSessionId = this.currentSipSession ?
|
|
1358
|
+
const currentSipSessionId = this.currentSipSession ? getSipSessionId(this.currentSipSession) : this.client.getSipSessionIds()[0];
|
|
1358
1359
|
return currentSipSessionId ? this.client.getSipSession(currentSipSessionId) : null;
|
|
1359
1360
|
}
|
|
1360
1361
|
return this.client.getSipSession(keys[keyIndex]);
|
|
@@ -1362,12 +1363,9 @@ export default class WebRTCPhone extends Emitter {
|
|
|
1362
1363
|
getCallSession(sipSessionId) {
|
|
1363
1364
|
return this.callSessions[sipSessionId];
|
|
1364
1365
|
}
|
|
1365
|
-
getSipSessionId(sipSession) {
|
|
1366
|
-
return this.client.getSipSessionId(sipSession);
|
|
1367
|
-
}
|
|
1368
1366
|
getSipSessionIdFromCallSession(callSession) {
|
|
1369
1367
|
const sipSession = this.findSipSession(callSession);
|
|
1370
|
-
return sipSession ?
|
|
1368
|
+
return sipSession ? getSipSessionId(sipSession) : null;
|
|
1371
1369
|
}
|
|
1372
1370
|
_updateCallSession(callSession) {
|
|
1373
1371
|
const sipSessionId = this.getSipSessionIdFromCallSession(callSession);
|
|
@@ -1445,7 +1443,7 @@ export default class WebRTCPhone extends Emitter {
|
|
|
1445
1443
|
// @ts-ignore: private
|
|
1446
1444
|
const number = identity ? identity.uri._normal.user : null;
|
|
1447
1445
|
const { state, } = sipSession || {};
|
|
1448
|
-
const sessionId =
|
|
1446
|
+
const sessionId = getSipSessionId(sipSession);
|
|
1449
1447
|
fromSession = fromSession || this.callSessions[sessionId];
|
|
1450
1448
|
const callSession = new CallSession({
|
|
1451
1449
|
callId: fromSession && fromSession.callId,
|
package/esm/simple/Auth.js
CHANGED
|
@@ -122,19 +122,25 @@ export class Auth {
|
|
|
122
122
|
return this._onAuthenticated(rawSession);
|
|
123
123
|
}
|
|
124
124
|
async initiateIdpAuthentication(domain, redirectUrl) {
|
|
125
|
+
let response;
|
|
125
126
|
try {
|
|
126
|
-
|
|
127
|
+
response = await getApiClient().auth.initiateIdpAuthentication(domain, redirectUrl);
|
|
127
128
|
}
|
|
128
129
|
catch (e) {
|
|
129
|
-
logger.error('Error during IdP authentication
|
|
130
|
-
|
|
131
|
-
|
|
130
|
+
logger.error('Error during IdP authentication initiation:', e.message);
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
if (!response.ok) {
|
|
134
|
+
if (response.status === 404) {
|
|
135
|
+
throw new NoSamlRouteError('No route found for saml sso');
|
|
132
136
|
}
|
|
133
|
-
if (
|
|
137
|
+
if (response.status === 500) {
|
|
134
138
|
throw new SamlConfigError('SAML/server configuration error');
|
|
135
139
|
}
|
|
136
|
-
|
|
140
|
+
const error = await response.json();
|
|
141
|
+
throw new Error(error.message || error.reason);
|
|
137
142
|
}
|
|
143
|
+
return response.json();
|
|
138
144
|
}
|
|
139
145
|
async logInViaRefreshToken(refreshToken) {
|
|
140
146
|
const rawSession = await getApiClient().auth.refreshToken(refreshToken, '', this.expiration, this.mobile);
|
package/esm/simple/Phone.js
CHANGED
|
@@ -7,6 +7,7 @@ import IssueReporter from '../service/IssueReporter';
|
|
|
7
7
|
import Emitter from '../utils/Emitter';
|
|
8
8
|
import Wazo from './index';
|
|
9
9
|
import SFUNotAvailableError from '../domain/SFUNotAvailableError';
|
|
10
|
+
import { getSipSessionId } from '../utils/sdp';
|
|
10
11
|
const logger = IssueReporter.loggerFor('simple-phone');
|
|
11
12
|
const sipLogger = IssueReporter.loggerFor('sip.js');
|
|
12
13
|
const protocolLogger = IssueReporter.loggerFor('sip');
|
|
@@ -230,7 +231,7 @@ export class Phone extends Emitter {
|
|
|
230
231
|
if (!sipSession || !this.phone) {
|
|
231
232
|
return null;
|
|
232
233
|
}
|
|
233
|
-
return
|
|
234
|
+
return getSipSessionId(sipSession);
|
|
234
235
|
}
|
|
235
236
|
sendMessage(body, sipSession, contentType = 'text/plain') {
|
|
236
237
|
const toSipSession = sipSession || this.getCurrentSipSession();
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import BadResponse from '../../domain/BadResponse';
|
|
2
|
-
import ServerError from '../../domain/ServerError';
|
|
3
1
|
import getApiClient, { setRefreshToken } from '../../service/getApiClient';
|
|
4
2
|
import { Auth as AuthClass } from '../Auth';
|
|
5
3
|
const Auth = new AuthClass();
|
|
@@ -112,8 +110,12 @@ describe('simple/Auth', () => {
|
|
|
112
110
|
});
|
|
113
111
|
describe('initiateIdpAuthentication', () => {
|
|
114
112
|
const validResponse = { location: 'idp.com/auth', saml_session_id: 'a1b2C3d4' };
|
|
115
|
-
it('should return a `location` and `saml_session_id` if the response is
|
|
116
|
-
const initiateIdpAuthentication = jest.fn(() =>
|
|
113
|
+
it('should return a `location` and `saml_session_id` if the response is ok', async () => {
|
|
114
|
+
const initiateIdpAuthentication = jest.fn(() => Promise.resolve({
|
|
115
|
+
ok: true,
|
|
116
|
+
status: 303,
|
|
117
|
+
json: () => Promise.resolve(validResponse),
|
|
118
|
+
}));
|
|
117
119
|
getApiClient.mockImplementation(() => ({
|
|
118
120
|
auth: {
|
|
119
121
|
initiateIdpAuthentication,
|
|
@@ -123,16 +125,70 @@ describe('simple/Auth', () => {
|
|
|
123
125
|
expect(response).toBe(validResponse);
|
|
124
126
|
});
|
|
125
127
|
it('should return a `NoSamlRouteError` if the response is a 404', async () => {
|
|
126
|
-
const initiateIdpAuthentication = jest.fn(() =>
|
|
128
|
+
const initiateIdpAuthentication = jest.fn(() => Promise.resolve({
|
|
129
|
+
ok: false,
|
|
130
|
+
status: 404,
|
|
131
|
+
}));
|
|
132
|
+
getApiClient.mockImplementation(() => ({
|
|
133
|
+
auth: {
|
|
134
|
+
initiateIdpAuthentication,
|
|
135
|
+
},
|
|
136
|
+
}));
|
|
137
|
+
await expect(Auth.initiateIdpAuthentication('example.com', 'https://myapp.xyz')).rejects.toThrow('No route found for saml sso');
|
|
138
|
+
});
|
|
139
|
+
it('should return an error `message` if the response is NOT ok', async () => {
|
|
140
|
+
const initiateIdpAuthentication = jest.fn(() => Promise.resolve({
|
|
141
|
+
ok: false,
|
|
142
|
+
json: () => Promise.resolve({ message: 'an error message', reason: 'an error reason' }),
|
|
143
|
+
}));
|
|
144
|
+
getApiClient.mockImplementation(() => ({
|
|
145
|
+
auth: {
|
|
146
|
+
initiateIdpAuthentication,
|
|
147
|
+
},
|
|
148
|
+
}));
|
|
149
|
+
await expect(Auth.initiateIdpAuthentication('example.com', 'https://myapp.xyz')).rejects.toThrow('an error message');
|
|
150
|
+
});
|
|
151
|
+
it('should return an error `reason` if there is no `message` and the response is NOT ok', async () => {
|
|
152
|
+
const initiateIdpAuthentication = jest.fn(() => Promise.resolve({
|
|
153
|
+
ok: false,
|
|
154
|
+
json: () => Promise.resolve({ reason: 'an error reason' }),
|
|
155
|
+
}));
|
|
127
156
|
getApiClient.mockImplementation(() => ({
|
|
128
157
|
auth: {
|
|
129
158
|
initiateIdpAuthentication,
|
|
130
159
|
},
|
|
131
160
|
}));
|
|
132
|
-
await expect(Auth.initiateIdpAuthentication('example.com', 'https://myapp.xyz')).rejects.toThrow('
|
|
161
|
+
await expect(Auth.initiateIdpAuthentication('example.com', 'https://myapp.xyz')).rejects.toThrow('an error reason');
|
|
133
162
|
});
|
|
134
|
-
it('should return a
|
|
135
|
-
const initiateIdpAuthentication = jest.fn(() =>
|
|
163
|
+
it('should return an error with a null message if there is no `message` and `reason`', async () => {
|
|
164
|
+
const initiateIdpAuthentication = jest.fn(() => Promise.resolve({
|
|
165
|
+
ok: false,
|
|
166
|
+
json: () => Promise.resolve({}),
|
|
167
|
+
}));
|
|
168
|
+
getApiClient.mockImplementation(() => ({
|
|
169
|
+
auth: {
|
|
170
|
+
initiateIdpAuthentication,
|
|
171
|
+
},
|
|
172
|
+
}));
|
|
173
|
+
await expect(Auth.initiateIdpAuthentication('example.com', 'https://myapp.xyz')).rejects.toThrow('');
|
|
174
|
+
});
|
|
175
|
+
it('should return a NoSAMLRouteError if the response is a 404', async () => {
|
|
176
|
+
const initiateIdpAuthentication = jest.fn(() => Promise.resolve({
|
|
177
|
+
ok: false,
|
|
178
|
+
status: 404,
|
|
179
|
+
}));
|
|
180
|
+
getApiClient.mockImplementation(() => ({
|
|
181
|
+
auth: {
|
|
182
|
+
initiateIdpAuthentication,
|
|
183
|
+
},
|
|
184
|
+
}));
|
|
185
|
+
await expect(Auth.initiateIdpAuthentication('example.com', 'https://myapp.xyz')).rejects.toThrow('No route found for saml sso');
|
|
186
|
+
});
|
|
187
|
+
it('should return a SamlConfigError if the response is a 500', async () => {
|
|
188
|
+
const initiateIdpAuthentication = jest.fn(() => Promise.resolve({
|
|
189
|
+
ok: false,
|
|
190
|
+
status: 500,
|
|
191
|
+
}));
|
|
136
192
|
getApiClient.mockImplementation(() => ({
|
|
137
193
|
auth: {
|
|
138
194
|
initiateIdpAuthentication,
|
package/esm/simple/index.js
CHANGED
|
@@ -17,7 +17,7 @@ import { CanceledCallError } from '../web-rtc-client';
|
|
|
17
17
|
import BadResponse from '../domain/BadResponse';
|
|
18
18
|
import ServerError from '../domain/ServerError';
|
|
19
19
|
import SFUNotAvailableError from '../domain/SFUNotAvailableError';
|
|
20
|
-
import
|
|
20
|
+
import ApiCall from '../domain/Call';
|
|
21
21
|
import CallLog from '../domain/CallLog';
|
|
22
22
|
import Recording from '../domain/Recording';
|
|
23
23
|
import ChatMessage from '../domain/ChatMessage';
|
|
@@ -40,6 +40,8 @@ import IssueReporter from '../service/IssueReporter';
|
|
|
40
40
|
import Features from '../domain/Features';
|
|
41
41
|
import Meeting from '../domain/Meeting';
|
|
42
42
|
import Checker from '../checker/Checker';
|
|
43
|
+
import Softphone from '../voice/softphone';
|
|
44
|
+
import Call from '../voice/call';
|
|
43
45
|
const Wazo = {
|
|
44
46
|
Auth,
|
|
45
47
|
Phone,
|
|
@@ -59,11 +61,15 @@ const Wazo = {
|
|
|
59
61
|
loggerFor: IssueReporter.loggerFor.bind(IssueReporter),
|
|
60
62
|
Features,
|
|
61
63
|
Checker,
|
|
64
|
+
Voice: {
|
|
65
|
+
Softphone,
|
|
66
|
+
Call,
|
|
67
|
+
},
|
|
62
68
|
// Domain
|
|
63
69
|
domain: {
|
|
64
70
|
BadResponse,
|
|
65
71
|
ServerError,
|
|
66
|
-
Call,
|
|
72
|
+
Call: ApiCall,
|
|
67
73
|
CallLog,
|
|
68
74
|
CTIPhone,
|
|
69
75
|
Recording,
|