@wazo/sdk 1.0.0-alpha.0 → 1.0.0-alpha.2
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 +6 -6
- package/esm/__tests__/__voice__/call.test.js +44 -2
- package/esm/__tests__/api-client.test.js +20 -1
- package/esm/api/auth.js +13 -2
- package/esm/domain/Phone/WebRTCPhone.js +6 -7
- package/esm/index.js +2 -0
- package/esm/simple/Auth.js +6 -12
- package/esm/simple/Phone.js +1 -0
- package/esm/simple/__tests__/Auth.test.js +8 -64
- package/esm/state-machine/call-state-machine.js +1 -1
- package/esm/types/api/auth.d.ts.map +1 -1
- package/esm/types/domain/Phone/WebRTCPhone.d.ts.map +1 -1
- package/esm/types/index.d.ts +2 -0
- package/esm/types/index.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/state-machine/utils.d.ts +1 -1
- package/esm/types/state-machine/utils.d.ts.map +1 -1
- package/esm/types/utils/api-requester.d.ts.map +1 -1
- package/esm/types/utils/sdp.d.ts +2 -0
- package/esm/types/utils/sdp.d.ts.map +1 -1
- package/esm/types/utils/webrtc.d.ts +1 -0
- package/esm/types/utils/webrtc.d.ts.map +1 -1
- package/esm/types/voice/call.d.ts +19 -11
- package/esm/types/voice/call.d.ts.map +1 -1
- package/esm/types/voice/softphone.d.ts +18 -13
- package/esm/types/voice/softphone.d.ts.map +1 -1
- package/esm/utils/__tests__/api-requester.test.js +17 -0
- package/esm/utils/api-requester.js +9 -1
- package/esm/utils/sdp.js +13 -0
- package/esm/utils/webrtc.js +21 -1
- package/esm/voice/call.js +90 -30
- package/esm/voice/softphone.js +94 -23
- package/lib/__tests__/__voice__/call.test.js +43 -1
- package/lib/__tests__/api-client.test.js +20 -1
- package/lib/api/auth.js +13 -2
- package/lib/domain/Phone/WebRTCPhone.js +5 -6
- package/lib/index.js +6 -2
- package/lib/simple/Auth.js +6 -12
- package/lib/simple/Phone.js +1 -0
- package/lib/simple/__tests__/Auth.test.js +11 -64
- package/lib/state-machine/call-state-machine.js +1 -1
- package/lib/types/api/auth.d.ts.map +1 -1
- package/lib/types/domain/Phone/WebRTCPhone.d.ts.map +1 -1
- package/lib/types/index.d.ts +2 -0
- package/lib/types/index.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/state-machine/utils.d.ts +1 -1
- package/lib/types/state-machine/utils.d.ts.map +1 -1
- package/lib/types/utils/api-requester.d.ts.map +1 -1
- package/lib/types/utils/sdp.d.ts +2 -0
- package/lib/types/utils/sdp.d.ts.map +1 -1
- package/lib/types/utils/webrtc.d.ts +1 -0
- package/lib/types/utils/webrtc.d.ts.map +1 -1
- package/lib/types/voice/call.d.ts +19 -11
- package/lib/types/voice/call.d.ts.map +1 -1
- package/lib/types/voice/softphone.d.ts +18 -13
- package/lib/types/voice/softphone.d.ts.map +1 -1
- package/lib/utils/__tests__/api-requester.test.js +17 -0
- package/lib/utils/api-requester.js +9 -1
- package/lib/utils/sdp.js +16 -1
- package/lib/utils/webrtc.js +23 -2
- package/lib/voice/call.js +87 -28
- package/lib/voice/softphone.js +95 -23
- package/package.json +1 -1
- package/src/__tests__/__voice__/call.test.ts +47 -2
- package/src/__tests__/api-client.test.ts +22 -1
- package/src/api/auth.ts +25 -3
- package/src/domain/Phone/WebRTCPhone.ts +9 -9
- package/src/index.ts +2 -0
- package/src/simple/Auth.ts +6 -13
- package/src/simple/Phone.ts +1 -0
- package/src/simple/__tests__/Auth.test.ts +9 -87
- package/src/state-machine/call-state-machine.ts +1 -1
- package/src/state-machine/utils.ts +2 -2
- package/src/utils/__tests__/api-requester.test.ts +20 -0
- package/src/utils/api-requester.ts +9 -1
- package/src/utils/sdp.ts +16 -0
- package/src/utils/webrtc.ts +24 -2
- package/src/voice/call.ts +118 -40
- package/src/voice/softphone.ts +116 -30
- package/src/web-rtc-client.ts +1 -1
|
@@ -131,7 +131,6 @@ describe('With correct API results', () => {
|
|
|
131
131
|
body: JSON.stringify({ saml_session_id: samlSessionId }),
|
|
132
132
|
signal: expect.any(Object),
|
|
133
133
|
headers: {
|
|
134
|
-
'X-Auth-Token': token,
|
|
135
134
|
'Content-Type': 'application/json',
|
|
136
135
|
Accept: 'application/json',
|
|
137
136
|
},
|
|
@@ -140,6 +139,28 @@ describe('With correct API results', () => {
|
|
|
140
139
|
});
|
|
141
140
|
});
|
|
142
141
|
|
|
142
|
+
describe('samlLogIn mobile test', () => {
|
|
143
|
+
it('should retrieve user token', async () => {
|
|
144
|
+
Wazo.Auth.mobile = true;
|
|
145
|
+
const samlSessionId = 'a1b2C3d4';
|
|
146
|
+
const result = await client.auth.samlLogIn(samlSessionId);
|
|
147
|
+
|
|
148
|
+
expect(result).toBeInstanceOf(Session);
|
|
149
|
+
expect(result?.token).toBe(1);
|
|
150
|
+
expect(global.fetch).toBeCalledWith(`https://${server}/api/auth/${authVersion}/token`, {
|
|
151
|
+
method: 'post',
|
|
152
|
+
body: JSON.stringify({ saml_session_id: samlSessionId }),
|
|
153
|
+
signal: expect.any(Object),
|
|
154
|
+
headers: {
|
|
155
|
+
'Content-Type': 'application/json',
|
|
156
|
+
'Wazo-Session-Type': 'mobile',
|
|
157
|
+
Accept: 'application/json',
|
|
158
|
+
},
|
|
159
|
+
agent: null,
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
|
|
143
164
|
describe('logOut test', () => {
|
|
144
165
|
it('should delete the specified token', async () => {
|
|
145
166
|
const oldToken = 123;
|
package/src/api/auth.ts
CHANGED
|
@@ -16,6 +16,12 @@ 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
|
+
|
|
19
25
|
type SendResetPasswordArgs = {
|
|
20
26
|
username?: string,
|
|
21
27
|
email?: string,
|
|
@@ -110,13 +116,29 @@ export default ((client: ApiRequester, baseUrl: string): AuthD => ({
|
|
|
110
116
|
},
|
|
111
117
|
|
|
112
118
|
logOut: (token: Token): Promise<LogoutResponse> => client.delete(`${baseUrl}/token/${token}`, null, {}, ApiRequester.successResponseParser),
|
|
119
|
+
|
|
113
120
|
samlLogIn: async (samlSessionId: string): Promise<Session | null | undefined> => {
|
|
114
|
-
const
|
|
121
|
+
const headers: Record<string, any> = {
|
|
122
|
+
Accept: 'application/json',
|
|
123
|
+
'Content-Type': 'application/json',
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const body: SamlLoginBody = {
|
|
115
127
|
saml_session_id: samlSessionId,
|
|
116
128
|
};
|
|
117
129
|
|
|
118
|
-
|
|
130
|
+
if (client.clientId) {
|
|
131
|
+
body.access_type = 'offline';
|
|
132
|
+
body.client_id = client.clientId;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (Wazo.Auth.mobile) {
|
|
136
|
+
headers['Wazo-Session-Type'] = 'mobile';
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return client.post(`${baseUrl}/token`, body, headers).then(Session.parse);
|
|
119
140
|
},
|
|
141
|
+
|
|
120
142
|
initiateIdpAuthentication: async (domain: string, redirectUrl: string) => {
|
|
121
143
|
const body = {
|
|
122
144
|
domain,
|
|
@@ -128,7 +150,7 @@ export default ((client: ApiRequester, baseUrl: string): AuthD => ({
|
|
|
128
150
|
'Content-Type': 'application/json',
|
|
129
151
|
};
|
|
130
152
|
|
|
131
|
-
return client.post(`${baseUrl}/saml/sso`, body, headers
|
|
153
|
+
return client.post(`${baseUrl}/saml/sso`, body, headers);
|
|
132
154
|
},
|
|
133
155
|
refreshToken: (refreshToken: string, backend: string, expiration: number, isMobile?: boolean, tenantId?: string, domainName?: string): Promise<Session | null | undefined> => {
|
|
134
156
|
const body: Record<string, any> = {
|
|
@@ -12,7 +12,7 @@ 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';
|
|
15
|
+
import { getCallDisplayName, getCallNumber, getSipSessionId } from '../../utils/sdp';
|
|
16
16
|
import { downgradeToAudio, getStreamFromConstraints, getWebRtcStats, setLocalMediaStream, upgradeToVideo } from '../../utils/webrtc';
|
|
17
17
|
|
|
18
18
|
export const ON_USER_AGENT = 'onUserAgent';
|
|
@@ -61,6 +61,8 @@ export const MESSAGE_TYPE_CHAT = 'message/TYPE_CHAT';
|
|
|
61
61
|
export const MESSAGE_TYPE_SIGNAL = 'message/TYPE_SIGNAL';
|
|
62
62
|
export const events = [ON_USER_AGENT, ON_REGISTERED, ON_UNREGISTERED, ON_PROGRESS, ON_CALL_ACCEPTED, ON_CALL_ANSWERED, ON_CALL_INCOMING, ON_CALL_OUTGOING, ON_CALL_MUTED, ON_CALL_UNMUTED, ON_CALL_RESUMED, ON_CALL_HELD, ON_CALL_UNHELD, ON_CAMERA_DISABLED, ON_CALL_FAILED, ON_CALL_ENDED, ON_CALL_REJECTED, ON_MESSAGE, ON_REINVITE, ON_TRACK, ON_AUDIO_STREAM, ON_VIDEO_STREAM, ON_REMOVE_STREAM, ON_SHARE_SCREEN_ENDED, ON_TERMINATE_SOUND, ON_PLAY_RING_SOUND, ON_PLAY_INBOUND_CALL_SIGNAL_SOUND, ON_PLAY_HANGUP_SOUND, ON_PLAY_PROGRESS_SOUND, ON_VIDEO_INPUT_CHANGE, ON_SHARE_SCREEN_STARTED, ON_CALL_ERROR, ON_CHAT, ON_SIGNAL, ON_NETWORK_STATS, ON_DISCONNECTED, ON_EARLY_MEDIA];
|
|
63
63
|
const logger = IssueReporter.loggerFor('webrtc-phone');
|
|
64
|
+
|
|
65
|
+
// @deprecated: Use Wazo.Voice.Softphone instead
|
|
64
66
|
export default class WebRTCPhone extends Emitter implements Phone {
|
|
65
67
|
client: WazoWebRTCClient;
|
|
66
68
|
|
|
@@ -1783,20 +1785,17 @@ export default class WebRTCPhone extends Emitter implements Phone {
|
|
|
1783
1785
|
}
|
|
1784
1786
|
|
|
1785
1787
|
_createCallSession(sipSession: WazoSession, fromSession?: CallSession | null | undefined, extra: Record<string, any> = {}): CallSession {
|
|
1786
|
-
|
|
1787
|
-
const
|
|
1788
|
-
|
|
1789
|
-
const number = identity ? identity.uri._normal.user : null;
|
|
1790
|
-
const {
|
|
1791
|
-
state,
|
|
1792
|
-
} = sipSession || {};
|
|
1788
|
+
const number = getCallNumber(sipSession);
|
|
1789
|
+
const displayName = getCallDisplayName(sipSession);
|
|
1790
|
+
const { state } = sipSession || {};
|
|
1793
1791
|
const sessionId = getSipSessionId(sipSession);
|
|
1792
|
+
|
|
1794
1793
|
fromSession = fromSession || this.callSessions[sessionId];
|
|
1795
1794
|
const callSession = new CallSession({
|
|
1796
1795
|
callId: fromSession && fromSession.callId,
|
|
1797
1796
|
sipCallId: sessionId,
|
|
1798
1797
|
sipStatus: state,
|
|
1799
|
-
displayName
|
|
1798
|
+
displayName,
|
|
1800
1799
|
// @ts-ignore: date / number
|
|
1801
1800
|
startTime: fromSession ? fromSession.startTime : new Date(),
|
|
1802
1801
|
creationTime: fromSession ? fromSession.creationTime : null,
|
|
@@ -1817,6 +1816,7 @@ export default class WebRTCPhone extends Emitter implements Phone {
|
|
|
1817
1816
|
...extra,
|
|
1818
1817
|
});
|
|
1819
1818
|
this.callSessions[sessionId] = callSession;
|
|
1819
|
+
|
|
1820
1820
|
return callSession;
|
|
1821
1821
|
}
|
|
1822
1822
|
|
package/src/index.ts
CHANGED
|
@@ -4,6 +4,8 @@ export { default as Emitter } from './utils/Emitter';
|
|
|
4
4
|
export { default as IssueReporter } from './service/IssueReporter';
|
|
5
5
|
export { default as BadResponse } from './domain/BadResponse';
|
|
6
6
|
export { default as ServerError } from './domain/ServerError';
|
|
7
|
+
export { default as InvalidStateTransition } from './domain/InvalidStateTransition';
|
|
8
|
+
export { default as InvalidState } from './domain/InvalidState';
|
|
7
9
|
export { default as SFUNotAvailableError } from './domain/SFUNotAvailableError';
|
|
8
10
|
export { default as Call } from './domain/Call';
|
|
9
11
|
export { default as CallLog } from './domain/CallLog';
|
package/src/simple/Auth.ts
CHANGED
|
@@ -168,28 +168,21 @@ 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;
|
|
172
171
|
try {
|
|
173
|
-
|
|
172
|
+
return await getApiClient().auth.initiateIdpAuthentication(domain, redirectUrl);
|
|
174
173
|
} catch (e: any) {
|
|
175
|
-
logger.error('Error during IdP authentication
|
|
176
|
-
return;
|
|
177
|
-
}
|
|
174
|
+
logger.error('Error during IdP authentication initialization:', e.message);
|
|
178
175
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
throw new NoSamlRouteError('No route found for saml sso');
|
|
176
|
+
if (e.status === 404) {
|
|
177
|
+
throw new NoSamlRouteError('No route found for SAML SSO');
|
|
182
178
|
}
|
|
183
179
|
|
|
184
|
-
if (
|
|
180
|
+
if (e.status === 500) {
|
|
185
181
|
throw new SamlConfigError('SAML/server configuration error');
|
|
186
182
|
}
|
|
187
183
|
|
|
188
|
-
|
|
189
|
-
throw new Error(error.message || error.reason);
|
|
184
|
+
throw e;
|
|
190
185
|
}
|
|
191
|
-
|
|
192
|
-
return response.json();
|
|
193
186
|
}
|
|
194
187
|
|
|
195
188
|
async logInViaRefreshToken(refreshToken: string): Promise<Session | null> {
|
package/src/simple/Phone.ts
CHANGED
|
@@ -19,6 +19,7 @@ const sipLogger = IssueReporter.loggerFor('sip.js');
|
|
|
19
19
|
const protocolLogger = IssueReporter.loggerFor('sip');
|
|
20
20
|
const protocolDebugMessages = ['Received WebSocket text message:', 'Sending WebSocket message:'];
|
|
21
21
|
|
|
22
|
+
// @deprecated: Use Wazo.Voice.Softphone instead
|
|
22
23
|
export class Phone extends Emitter {
|
|
23
24
|
client: WazoWebRTCClient;
|
|
24
25
|
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import BadResponse from '../../domain/BadResponse';
|
|
2
|
+
import ServerError from '../../domain/ServerError';
|
|
1
3
|
import getApiClient, { setRefreshToken } from '../../service/getApiClient';
|
|
2
4
|
import { Auth as AuthClass } from '../Auth';
|
|
3
5
|
|
|
@@ -124,13 +126,8 @@ describe('simple/Auth', () => {
|
|
|
124
126
|
describe('initiateIdpAuthentication', () => {
|
|
125
127
|
const validResponse = { location: 'idp.com/auth', saml_session_id: 'a1b2C3d4' };
|
|
126
128
|
|
|
127
|
-
it('should return a `location` and `saml_session_id` if the response is
|
|
128
|
-
const initiateIdpAuthentication = jest.fn(() =>
|
|
129
|
-
Promise.resolve({
|
|
130
|
-
ok: true,
|
|
131
|
-
status: 303,
|
|
132
|
-
json: () => Promise.resolve(validResponse),
|
|
133
|
-
}));
|
|
129
|
+
it('should return a `location` and `saml_session_id` if the response is valid', async () => {
|
|
130
|
+
const initiateIdpAuthentication = jest.fn(() => validResponse);
|
|
134
131
|
|
|
135
132
|
(getApiClient as any).mockImplementation(() => ({
|
|
136
133
|
auth: {
|
|
@@ -141,48 +138,9 @@ describe('simple/Auth', () => {
|
|
|
141
138
|
const response = await Auth.initiateIdpAuthentication('example.com', 'https://myapp.xyz');
|
|
142
139
|
expect(response).toBe(validResponse);
|
|
143
140
|
});
|
|
144
|
-
it('should return a `NoSamlRouteError` if the response is a 404', async () => {
|
|
145
|
-
const initiateIdpAuthentication = jest.fn(() =>
|
|
146
|
-
Promise.resolve({
|
|
147
|
-
ok: false,
|
|
148
|
-
status: 404,
|
|
149
|
-
}));
|
|
150
|
-
|
|
151
|
-
(getApiClient as any).mockImplementation(() => ({
|
|
152
|
-
auth: {
|
|
153
|
-
initiateIdpAuthentication,
|
|
154
|
-
},
|
|
155
|
-
}));
|
|
156
|
-
|
|
157
|
-
await expect(Auth.initiateIdpAuthentication('example.com', 'https://myapp.xyz')).rejects.toThrow(
|
|
158
|
-
'No route found for saml sso',
|
|
159
|
-
);
|
|
160
|
-
});
|
|
161
141
|
|
|
162
|
-
it('should return
|
|
163
|
-
const initiateIdpAuthentication = jest.fn(() =>
|
|
164
|
-
Promise.resolve({
|
|
165
|
-
ok: false,
|
|
166
|
-
json: () => Promise.resolve({ message: 'an error message', reason: 'an error reason' }),
|
|
167
|
-
}));
|
|
168
|
-
|
|
169
|
-
(getApiClient as any).mockImplementation(() => ({
|
|
170
|
-
auth: {
|
|
171
|
-
initiateIdpAuthentication,
|
|
172
|
-
},
|
|
173
|
-
}));
|
|
174
|
-
|
|
175
|
-
await expect(Auth.initiateIdpAuthentication('example.com', 'https://myapp.xyz')).rejects.toThrow(
|
|
176
|
-
'an error message',
|
|
177
|
-
);
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
it('should return an error `reason` if there is no `message` and the response is NOT ok', async () => {
|
|
181
|
-
const initiateIdpAuthentication = jest.fn(() =>
|
|
182
|
-
Promise.resolve({
|
|
183
|
-
ok: false,
|
|
184
|
-
json: () => Promise.resolve({ reason: 'an error reason' }),
|
|
185
|
-
}));
|
|
142
|
+
it('should return a `NoSamlRouteError` if the response is a 404', async () => {
|
|
143
|
+
const initiateIdpAuthentication = jest.fn(() => { throw BadResponse.fromText('an error message', 404); });
|
|
186
144
|
|
|
187
145
|
(getApiClient as any).mockImplementation(() => ({
|
|
188
146
|
auth: {
|
|
@@ -191,48 +149,12 @@ describe('simple/Auth', () => {
|
|
|
191
149
|
}));
|
|
192
150
|
|
|
193
151
|
await expect(Auth.initiateIdpAuthentication('example.com', 'https://myapp.xyz')).rejects.toThrow(
|
|
194
|
-
'
|
|
152
|
+
'No route found for SAML SSO',
|
|
195
153
|
);
|
|
196
154
|
});
|
|
197
155
|
|
|
198
|
-
it('should return
|
|
199
|
-
const initiateIdpAuthentication = jest.fn(() =>
|
|
200
|
-
Promise.resolve({
|
|
201
|
-
ok: false,
|
|
202
|
-
json: () => Promise.resolve({}),
|
|
203
|
-
}));
|
|
204
|
-
|
|
205
|
-
(getApiClient as any).mockImplementation(() => ({
|
|
206
|
-
auth: {
|
|
207
|
-
initiateIdpAuthentication,
|
|
208
|
-
},
|
|
209
|
-
}));
|
|
210
|
-
|
|
211
|
-
await expect(Auth.initiateIdpAuthentication('example.com', 'https://myapp.xyz')).rejects.toThrow('');
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
it('should return a NoSAMLRouteError if the response is a 404', async () => {
|
|
215
|
-
const initiateIdpAuthentication = jest.fn(() =>
|
|
216
|
-
Promise.resolve({
|
|
217
|
-
ok: false,
|
|
218
|
-
status: 404,
|
|
219
|
-
}));
|
|
220
|
-
|
|
221
|
-
(getApiClient as any).mockImplementation(() => ({
|
|
222
|
-
auth: {
|
|
223
|
-
initiateIdpAuthentication,
|
|
224
|
-
},
|
|
225
|
-
}));
|
|
226
|
-
|
|
227
|
-
await expect(Auth.initiateIdpAuthentication('example.com', 'https://myapp.xyz')).rejects.toThrow('No route found for saml sso');
|
|
228
|
-
});
|
|
229
|
-
|
|
230
|
-
it('should return a SamlConfigError if the response is a 500', async () => {
|
|
231
|
-
const initiateIdpAuthentication = jest.fn(() =>
|
|
232
|
-
Promise.resolve({
|
|
233
|
-
ok: false,
|
|
234
|
-
status: 500,
|
|
235
|
-
}));
|
|
156
|
+
it('should return a `SamlConfigError` if the response is a 500', async () => {
|
|
157
|
+
const initiateIdpAuthentication = jest.fn(() => { throw ServerError.fromText('an error message', 500); });
|
|
236
158
|
|
|
237
159
|
(getApiClient as any).mockImplementation(() => ({
|
|
238
160
|
auth: {
|
|
@@ -43,7 +43,7 @@ export type EstablishedActionTypes = typeof EstablishedActions[keyof typeof Esta
|
|
|
43
43
|
|
|
44
44
|
const callMachine = setup({
|
|
45
45
|
guards: {
|
|
46
|
-
isRegistered: () => softphone.
|
|
46
|
+
isRegistered: () => softphone.state === SoftphoneStates.REGISTERED,
|
|
47
47
|
},
|
|
48
48
|
}).createMachine({
|
|
49
49
|
id: 'call',
|
|
@@ -8,8 +8,8 @@ import IssueReporter from '../service/IssueReporter';
|
|
|
8
8
|
|
|
9
9
|
const logger = IssueReporter.loggerFor('state-machine');
|
|
10
10
|
|
|
11
|
-
export const getState = (actor: SoftphoneActorRef | CallActorRef): SoftphoneStates | CallStates | EstablishedStateTypes => {
|
|
12
|
-
return actor.getSnapshot().value as SoftphoneStates | CallStates | EstablishedStateTypes
|
|
11
|
+
export const getState = (actor: SoftphoneActorRef | CallActorRef): SoftphoneStates | CallStates | EstablishedStateTypes | Record<CallStates, EstablishedStateTypes> => {
|
|
12
|
+
return actor.getSnapshot().value as SoftphoneStates | CallStates | EstablishedStateTypes | Record<CallStates, EstablishedStateTypes>;
|
|
13
13
|
};
|
|
14
14
|
|
|
15
15
|
export const can = (actor: SoftphoneActorRef | CallActorRef, action: SoftphoneActions | CallActions | EstablishedCallActions): boolean => {
|
|
@@ -158,3 +158,23 @@ describe('With a refresh token', () => {
|
|
|
158
158
|
});
|
|
159
159
|
});
|
|
160
160
|
});
|
|
161
|
+
|
|
162
|
+
describe('base64Encode', () => {
|
|
163
|
+
it('should encode a string using base64', () => {
|
|
164
|
+
const input = 'Hello World!';
|
|
165
|
+
const expectedOutput = 'SGVsbG8gV29ybGQh';
|
|
166
|
+
expect(ApiRequester.base64Encode(input)).toBe(expectedOutput);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it('should handle special characters', () => {
|
|
170
|
+
const input = 'éà$@€';
|
|
171
|
+
const expectedOutput = 'w6nDoCRA4oKs';
|
|
172
|
+
expect(ApiRequester.base64Encode(input)).toBe(expectedOutput);
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it('should handle empty string', () => {
|
|
176
|
+
const input = '';
|
|
177
|
+
const expectedOutput = '';
|
|
178
|
+
expect(ApiRequester.base64Encode(input)).toBe(expectedOutput);
|
|
179
|
+
});
|
|
180
|
+
});
|
|
@@ -64,7 +64,15 @@ export default class ApiRequester {
|
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
static base64Encode(str: string): string {
|
|
67
|
-
|
|
67
|
+
if (typeof btoa !== 'undefined') {
|
|
68
|
+
try {
|
|
69
|
+
return btoa(str);
|
|
70
|
+
} catch (error) {
|
|
71
|
+
// fall back to Base64.encode if btoa fails
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return Base64.encode(str);
|
|
68
76
|
}
|
|
69
77
|
|
|
70
78
|
// @see https://github.com/facebook/flow/issues/183#issuecomment-358607052
|
package/src/utils/sdp.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/* eslint-disable no-underscore-dangle */
|
|
1
2
|
import sdpParser from 'sdp-transform';
|
|
2
3
|
import { UserAgent } from 'sip.js/lib/api/user-agent';
|
|
3
4
|
import { Inviter } from 'sip.js/lib/api';
|
|
@@ -174,3 +175,18 @@ export const replaceLocalIpModifier = (description: Record<string, any>) => Prom
|
|
|
174
175
|
});
|
|
175
176
|
|
|
176
177
|
export const makeURI = (target: string, host: string): URI | undefined => UserAgent.makeURI(`sip:${target}@${host}`);
|
|
178
|
+
|
|
179
|
+
export const getCallNumber = (sipCall: SipCall): string => {
|
|
180
|
+
// eslint-disable-next-line
|
|
181
|
+
const identity = sipCall ? sipCall.remoteIdentity || sipCall.assertedIdentity : null;
|
|
182
|
+
// @ts-ignore: private
|
|
183
|
+
return identity ? identity.uri._normal.user : null;
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
export const getCallDisplayName = (sipCall: SipCall): string => {
|
|
187
|
+
// eslint-disable-next-line
|
|
188
|
+
const identity = sipCall ? sipCall.remoteIdentity || sipCall.assertedIdentity : null;
|
|
189
|
+
const number = getCallNumber(sipCall);
|
|
190
|
+
|
|
191
|
+
return identity ? identity.displayName || number : number;
|
|
192
|
+
};
|
package/src/utils/webrtc.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* eslint-disable no-underscore-dangle, no-param-reassign */
|
|
1
|
+
/* eslint-disable no-underscore-dangle, no-param-reassign, no-restricted-syntax */
|
|
2
2
|
import type { SessionDescriptionHandler } from 'sip.js/lib/platform/web/session-description-handler/session-description-handler';
|
|
3
3
|
import type { SessionDescriptionHandlerFactoryOptions } from 'sip.js/lib/platform/web/session-description-handler/session-description-handler-factory-options';
|
|
4
4
|
import type { SessionDescriptionHandlerConfiguration } from 'sip.js/lib/platform/web/session-description-handler/session-description-handler-configuration';
|
|
@@ -246,6 +246,28 @@ export const toggleVideo = (sipCall: SipCall, muteCamera: boolean): void => {
|
|
|
246
246
|
}
|
|
247
247
|
};
|
|
248
248
|
|
|
249
|
+
export const isVideoMuted = (sipCall: SipCall): boolean => {
|
|
250
|
+
const sdh = sipCall.sessionDescriptionHandler as SessionDescriptionHandler;
|
|
251
|
+
const pc = sdh?.peerConnection as PeerConnection;
|
|
252
|
+
|
|
253
|
+
if (pc?.getSenders) {
|
|
254
|
+
for (const sender of pc.getSenders()) {
|
|
255
|
+
if (sender && sender.track && sender.track.kind === 'video') {
|
|
256
|
+
return !sender.track.enabled;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
} else {
|
|
260
|
+
for (const stream of pc.getLocalStreams()) {
|
|
261
|
+
// eslint-disable-next-line no-unreachable-loop
|
|
262
|
+
for (const track of stream.getVideoTracks()) {
|
|
263
|
+
return !track.enabled;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
return true;
|
|
269
|
+
};
|
|
270
|
+
|
|
249
271
|
export const cleanupStream = (stream: MediaStream): void => {
|
|
250
272
|
stream.getTracks().filter(track => track.enabled).forEach(track => track.stop());
|
|
251
273
|
};
|
|
@@ -329,7 +351,7 @@ export const getMediaConfiguration = (enableVideo: boolean, conference = false,
|
|
|
329
351
|
return {
|
|
330
352
|
constraints: {
|
|
331
353
|
// Exact constraint are not supported with `getDisplayMedia` and we must have a video=false in desktop screenshare
|
|
332
|
-
audio: screenSharing ? !isDesktop :
|
|
354
|
+
audio: screenSharing ? !isDesktop : withAudio ? getAudioConstraints(true) : false,
|
|
333
355
|
video: screenSharing ? isDesktop ? {
|
|
334
356
|
mandatory: {
|
|
335
357
|
chromeMediaSource: 'desktop',
|