@wazo/sdk 0.39.0-beta.9 → 0.39.1
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/README.md +7 -7
- package/dist/wazo-sdk.js +12 -12
- package/esm/__tests__/api-client.test.js +7 -1
- package/esm/__tests__/web-rtc-client.test.js +10 -0
- package/esm/api/dird.js +2 -2
- package/esm/api-client.js +4 -0
- package/esm/checker/checks/ice-ipv4.js +2 -1
- package/esm/domain/Contact.js +15 -8
- package/esm/domain/Phone/WebRTCPhone.js +9 -11
- package/esm/domain/Session.js +24 -0
- package/esm/domain/__tests__/Contact.test.js +200 -48
- package/esm/lib/WazoSessionDescriptionHandler.js +14 -11
- package/esm/service/IssueReporter.js +12 -2
- package/esm/service/__tests__/IssueReporter.test.js +12 -0
- package/esm/service/getApiClient.js +9 -0
- package/esm/simple/Auth.js +40 -5
- package/esm/simple/Phone.js +3 -1
- package/esm/simple/Websocket.js +1 -0
- package/esm/simple/room/Stream.js +3 -3
- package/esm/types/api/dird.d.ts.map +1 -1
- package/esm/types/api-client.d.ts +3 -2
- package/esm/types/api-client.d.ts.map +1 -1
- package/esm/types/checker/checks/ice-ipv4.d.ts.map +1 -1
- package/esm/types/domain/Contact.d.ts +1 -1
- package/esm/types/domain/Contact.d.ts.map +1 -1
- package/esm/types/domain/Phone/WebRTCPhone.d.ts +1 -1
- package/esm/types/domain/Phone/WebRTCPhone.d.ts.map +1 -1
- package/esm/types/domain/Session.d.ts +2 -0
- package/esm/types/domain/Session.d.ts.map +1 -1
- package/esm/types/domain/types.d.ts +40 -14
- package/esm/types/domain/types.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/lib/WazoSessionDescriptionHandler.d.ts +1 -1
- package/esm/types/lib/WazoSessionDescriptionHandler.d.ts.map +1 -1
- package/esm/types/service/IssueReporter.d.ts +1 -0
- package/esm/types/service/IssueReporter.d.ts.map +1 -1
- package/esm/types/service/getApiClient.d.ts +2 -0
- package/esm/types/service/getApiClient.d.ts.map +1 -1
- package/esm/types/simple/Auth.d.ts.map +1 -1
- package/esm/types/simple/Phone.d.ts +4 -3
- package/esm/types/simple/Phone.d.ts.map +1 -1
- package/esm/types/simple/Websocket.d.ts.map +1 -1
- package/esm/types/simple/index.d.ts +1 -0
- package/esm/types/simple/index.d.ts.map +1 -1
- package/esm/types/utils/Emitter.d.ts +1 -1
- package/esm/types/utils/Heartbeat.d.ts +3 -2
- package/esm/types/utils/Heartbeat.d.ts.map +1 -1
- package/esm/types/utils/api-requester.d.ts +5 -1
- package/esm/types/utils/api-requester.d.ts.map +1 -1
- package/esm/types/web-rtc-client.d.ts +5 -29
- package/esm/types/web-rtc-client.d.ts.map +1 -1
- package/esm/types/websocket-client.d.ts +5 -1
- package/esm/types/websocket-client.d.ts.map +1 -1
- package/esm/utils/__tests__/api-requester.test.js +7 -2
- package/esm/utils/api-requester.js +23 -5
- package/esm/web-rtc-client.js +74 -41
- package/esm/websocket-client.js +9 -2
- package/lib/__tests__/api-client.test.js +7 -1
- package/lib/__tests__/web-rtc-client.test.js +16 -0
- package/lib/api/dird.js +2 -2
- package/lib/api-client.js +4 -0
- package/lib/checker/checks/ice-ipv4.js +2 -1
- package/lib/domain/Contact.js +15 -8
- package/lib/domain/Phone/WebRTCPhone.js +9 -11
- package/lib/domain/Session.js +23 -0
- package/lib/domain/__tests__/Contact.test.js +175 -50
- package/lib/lib/WazoSessionDescriptionHandler.js +15 -12
- package/lib/service/IssueReporter.js +12 -2
- package/lib/service/__tests__/IssueReporter.test.js +12 -0
- package/lib/service/getApiClient.js +12 -1
- package/lib/simple/Auth.js +34 -4
- package/lib/simple/Phone.js +3 -1
- package/lib/simple/Websocket.js +1 -0
- package/lib/simple/room/Stream.js +3 -3
- package/lib/types/api/dird.d.ts.map +1 -1
- package/lib/types/api-client.d.ts +3 -2
- package/lib/types/api-client.d.ts.map +1 -1
- package/lib/types/checker/checks/ice-ipv4.d.ts.map +1 -1
- package/lib/types/domain/Contact.d.ts +1 -1
- package/lib/types/domain/Contact.d.ts.map +1 -1
- package/lib/types/domain/Phone/WebRTCPhone.d.ts +1 -1
- package/lib/types/domain/Phone/WebRTCPhone.d.ts.map +1 -1
- package/lib/types/domain/Session.d.ts +2 -0
- package/lib/types/domain/Session.d.ts.map +1 -1
- package/lib/types/domain/types.d.ts +40 -14
- package/lib/types/domain/types.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/lib/WazoSessionDescriptionHandler.d.ts +1 -1
- package/lib/types/lib/WazoSessionDescriptionHandler.d.ts.map +1 -1
- package/lib/types/service/IssueReporter.d.ts +1 -0
- package/lib/types/service/IssueReporter.d.ts.map +1 -1
- package/lib/types/service/getApiClient.d.ts +2 -0
- package/lib/types/service/getApiClient.d.ts.map +1 -1
- package/lib/types/simple/Auth.d.ts.map +1 -1
- package/lib/types/simple/Phone.d.ts +4 -3
- package/lib/types/simple/Phone.d.ts.map +1 -1
- package/lib/types/simple/Websocket.d.ts.map +1 -1
- package/lib/types/simple/index.d.ts +1 -0
- package/lib/types/simple/index.d.ts.map +1 -1
- package/lib/types/utils/Emitter.d.ts +1 -1
- package/lib/types/utils/Heartbeat.d.ts +3 -2
- package/lib/types/utils/Heartbeat.d.ts.map +1 -1
- package/lib/types/utils/api-requester.d.ts +5 -1
- package/lib/types/utils/api-requester.d.ts.map +1 -1
- package/lib/types/web-rtc-client.d.ts +5 -29
- package/lib/types/web-rtc-client.d.ts.map +1 -1
- package/lib/types/websocket-client.d.ts +5 -1
- package/lib/types/websocket-client.d.ts.map +1 -1
- package/lib/utils/__tests__/api-requester.test.js +7 -2
- package/lib/utils/api-requester.js +19 -5
- package/lib/web-rtc-client.js +72 -40
- package/lib/websocket-client.js +8 -2
- package/package.json +37 -35
- package/src/__tests__/api-client.test.ts +9 -1
- package/src/__tests__/web-rtc-client.test.ts +12 -0
- package/src/api/dird.ts +3 -2
- package/src/api-client.ts +8 -2
- package/src/checker/checks/ice-ipv4.ts +2 -1
- package/src/domain/Contact.ts +16 -8
- package/src/domain/Phone/WebRTCPhone.ts +11 -12
- package/src/domain/Session.ts +28 -0
- package/src/domain/__tests__/Contact.test.ts +213 -54
- package/src/domain/types.ts +42 -14
- package/src/lib/WazoSessionDescriptionHandler.ts +15 -11
- package/src/service/IssueReporter.ts +13 -2
- package/src/service/__tests__/IssueReporter.test.ts +14 -0
- package/src/service/getApiClient.ts +12 -0
- package/src/simple/Auth.ts +58 -6
- package/src/simple/Phone.ts +15 -6
- package/src/simple/Websocket.ts +1 -0
- package/src/simple/room/Stream.ts +3 -3
- package/src/types/global.d.ts +1 -0
- package/src/utils/Heartbeat.ts +2 -2
- package/src/utils/__tests__/api-requester.test.ts +8 -2
- package/src/utils/api-requester.ts +31 -4
- package/src/web-rtc-client.ts +91 -71
- package/src/websocket-client.ts +14 -1
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import { AbortController } from 'node-abort-controller';
|
|
1
2
|
import { Base64 } from 'js-base64';
|
|
2
3
|
import WazoApiClient from '../api-client';
|
|
3
4
|
import ServerError from '../domain/ServerError';
|
|
4
5
|
import BadResponse from '../domain/BadResponse';
|
|
5
6
|
import Session from '../domain/Session';
|
|
7
|
+
const controller = new AbortController();
|
|
6
8
|
const mockedResponse = {
|
|
7
9
|
data: {
|
|
8
10
|
token: 1,
|
|
@@ -65,7 +67,7 @@ const server = 'localhost';
|
|
|
65
67
|
const authVersion = '0.1';
|
|
66
68
|
const username = 'wazo';
|
|
67
69
|
const password = 'zowa';
|
|
68
|
-
jest.mock('node-fetch/
|
|
70
|
+
jest.mock('node-fetch/src/index', () => { });
|
|
69
71
|
const token = '1234';
|
|
70
72
|
const client = new WazoApiClient({
|
|
71
73
|
server,
|
|
@@ -98,6 +100,7 @@ describe('With correct API results', () => {
|
|
|
98
100
|
expect(global.fetch).toBeCalledWith(`https://${server}/api/auth/${authVersion}/token`, {
|
|
99
101
|
method: 'post',
|
|
100
102
|
body: JSON.stringify(data),
|
|
103
|
+
signal: controller.signal,
|
|
101
104
|
headers,
|
|
102
105
|
agent: null,
|
|
103
106
|
});
|
|
@@ -111,6 +114,7 @@ describe('With correct API results', () => {
|
|
|
111
114
|
expect(global.fetch).toBeCalledWith(`https://${server}/api/auth/${authVersion}/token/${oldToken}`, {
|
|
112
115
|
method: 'delete',
|
|
113
116
|
body: null,
|
|
117
|
+
signal: controller.signal,
|
|
114
118
|
headers: {},
|
|
115
119
|
agent: null,
|
|
116
120
|
});
|
|
@@ -131,6 +135,7 @@ describe('With unAuthorized API results', () => {
|
|
|
131
135
|
expect(global.fetch).toBeCalledWith(`https://${server}/api/auth/${authVersion}/token/${tokenToCheck}`, {
|
|
132
136
|
method: 'head',
|
|
133
137
|
body: null,
|
|
138
|
+
signal: controller.signal,
|
|
134
139
|
headers: {},
|
|
135
140
|
agent: null,
|
|
136
141
|
});
|
|
@@ -159,6 +164,7 @@ describe('With not found API results', () => {
|
|
|
159
164
|
expect(global.fetch).toBeCalledWith(`https://${server}/api/calld/1.0/users/me/voicemails`, {
|
|
160
165
|
method: 'get',
|
|
161
166
|
body: null,
|
|
167
|
+
signal: controller.signal,
|
|
162
168
|
headers: {
|
|
163
169
|
'X-Auth-Token': token,
|
|
164
170
|
'Content-Type': 'application/json',
|
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
import WebRTCClient from '../web-rtc-client';
|
|
2
2
|
jest.mock('sip.js/lib/platform/web/transport');
|
|
3
|
+
jest.mock('sip.js/lib/api/user-agent', () => ({
|
|
4
|
+
start: () => { },
|
|
5
|
+
UserAgent: class UserAgent {
|
|
6
|
+
// @ts-ignore
|
|
7
|
+
static makeURI = () => { };
|
|
8
|
+
// @ts-ignore
|
|
9
|
+
start = () => { };
|
|
10
|
+
transport = {};
|
|
11
|
+
},
|
|
12
|
+
}));
|
|
3
13
|
// @ts-expect-error
|
|
4
14
|
const client = new WebRTCClient({});
|
|
5
15
|
describe('WebRTC client', () => {
|
package/esm/api/dird.js
CHANGED
|
@@ -15,9 +15,9 @@ const getContactPayload = (contact) => ({
|
|
|
15
15
|
note: contact.note ? contact.note : '',
|
|
16
16
|
});
|
|
17
17
|
export default ((client, baseUrl) => ({
|
|
18
|
-
search: (context, term) => client.get(`${baseUrl}/directories/lookup/${context}`, {
|
|
18
|
+
search: (context, term, offset = 0, limit = null) => client.get(`${baseUrl}/directories/lookup/${context}`, {
|
|
19
19
|
term,
|
|
20
|
-
}).then(Contact.parseMany),
|
|
20
|
+
}).then((response) => Contact.parseMany(response, offset, limit)),
|
|
21
21
|
listPersonalContacts: (queryParams = null) => client.get(`${baseUrl}/personal`, queryParams).then((response) => Contact.parseManyPersonal(response.items)),
|
|
22
22
|
fetchPersonalContact: (contactUuid) => client.get(`${baseUrl}/personal/${contactUuid}`).then(Contact.parsePersonal),
|
|
23
23
|
addContact: (contact) => client.post(`${baseUrl}/personal`, getContactPayload(contact)).then(Contact.parsePersonal),
|
package/esm/api-client.js
CHANGED
|
@@ -116,6 +116,7 @@ export default class ApiClient {
|
|
|
116
116
|
this.onRefreshTokenError(error);
|
|
117
117
|
}
|
|
118
118
|
}
|
|
119
|
+
return null;
|
|
119
120
|
}
|
|
120
121
|
setToken(token) {
|
|
121
122
|
this.client.setToken(token);
|
|
@@ -126,6 +127,9 @@ export default class ApiClient {
|
|
|
126
127
|
setRefreshToken(refreshToken) {
|
|
127
128
|
this.refreshToken = refreshToken;
|
|
128
129
|
}
|
|
130
|
+
setRequestTimeout(requestTimeout) {
|
|
131
|
+
this.client.setRequestTimeout(requestTimeout);
|
|
132
|
+
}
|
|
129
133
|
setClientId(clientId) {
|
|
130
134
|
this.client.clientId = clientId;
|
|
131
135
|
}
|
|
@@ -12,7 +12,8 @@ export default {
|
|
|
12
12
|
name: 'Non IP v4 ice',
|
|
13
13
|
check: (server, session, externalAppConfig) => new Promise((resolve, reject) => {
|
|
14
14
|
if (typeof MediaStream === 'undefined') {
|
|
15
|
-
|
|
15
|
+
resolve('Skipped on node');
|
|
16
|
+
return;
|
|
16
17
|
}
|
|
17
18
|
const mobile = isMobile();
|
|
18
19
|
const offerOptions = {
|
package/esm/domain/Contact.js
CHANGED
|
@@ -55,11 +55,12 @@ export default class Contact {
|
|
|
55
55
|
}
|
|
56
56
|
return aLastName.localeCompare(bLastName);
|
|
57
57
|
}
|
|
58
|
-
static parseMany(response) {
|
|
59
|
-
if (!response || !response.results) {
|
|
58
|
+
static parseMany(response, offset = 0, limit = null) {
|
|
59
|
+
if (!response || !response.results || limit === 0) {
|
|
60
60
|
return [];
|
|
61
61
|
}
|
|
62
|
-
|
|
62
|
+
const results = limit !== null && limit > 0 ? response.results.slice(offset, limit) : offset > 0 ? response.results.slice(offset) : response.results;
|
|
63
|
+
return results.map(r => Contact.parse(r, response.column_types));
|
|
63
64
|
}
|
|
64
65
|
static manyGraphQlWithNumbersParser(numbers) {
|
|
65
66
|
return (response) => {
|
|
@@ -160,13 +161,19 @@ export default class Contact {
|
|
|
160
161
|
const postalAddress = plain.postalAddresses[0];
|
|
161
162
|
address = `${postalAddress.street} ${postalAddress.city} ${postalAddress.postCode} ${postalAddress.country}`;
|
|
162
163
|
}
|
|
164
|
+
const firstName = plain.givenName || '';
|
|
165
|
+
const lastName = plain.familyName || '';
|
|
166
|
+
const companyName = plain.company || '';
|
|
167
|
+
const isCompanyAccount = !firstName && !lastName;
|
|
168
|
+
const name = isCompanyAccount ? companyName : `${firstName} ${lastName}`;
|
|
163
169
|
return new Contact({
|
|
164
|
-
name
|
|
170
|
+
name,
|
|
165
171
|
number: plain.phoneNumbers.length ? plain.phoneNumbers[0].number : '',
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
172
|
+
// Make numbers unique
|
|
173
|
+
numbers: [...new Map(plain.phoneNumbers.map(item => [item.number, item])).values()].map((item, idx) => ({
|
|
174
|
+
label: idx === 0 ? 'primary' : 'secondary',
|
|
175
|
+
number: item.number,
|
|
176
|
+
})),
|
|
170
177
|
email: plain.emailAddresses.length ? plain.emailAddresses[0].email : '',
|
|
171
178
|
emails: plain.emailAddresses.length ? [{
|
|
172
179
|
label: 'primary',
|
|
@@ -303,9 +303,6 @@ export default class WebRTCPhone extends Emitter {
|
|
|
303
303
|
this.eventEmitter.emit(ON_CALL_CANCELED, callSession, elsewhere);
|
|
304
304
|
};
|
|
305
305
|
}
|
|
306
|
-
else if (sipSession instanceof Invitation) {
|
|
307
|
-
console.warn('sipSession._onCancel not found, please update the wazo SDK accordingly');
|
|
308
|
-
}
|
|
309
306
|
sipSession.stateChange.addListener((newState) => {
|
|
310
307
|
switch (newState) {
|
|
311
308
|
case SessionState.Establishing:
|
|
@@ -359,10 +356,6 @@ export default class WebRTCPhone extends Emitter {
|
|
|
359
356
|
if (kind === 'audio') {
|
|
360
357
|
return this.eventEmitter.emit(ON_AUDIO_STREAM, stream);
|
|
361
358
|
}
|
|
362
|
-
// not sure this does anything
|
|
363
|
-
if (kind === 'video') {
|
|
364
|
-
event.track.enabled = false;
|
|
365
|
-
}
|
|
366
359
|
return this.eventEmitter.emit(ON_VIDEO_STREAM, stream, event.track.id, event, sipSession);
|
|
367
360
|
};
|
|
368
361
|
peerConnection.onremovestream = (event) => {
|
|
@@ -411,10 +404,12 @@ export default class WebRTCPhone extends Emitter {
|
|
|
411
404
|
logger.info('WebRTC phone - stop screen sharing', {
|
|
412
405
|
restoreLocalStream,
|
|
413
406
|
});
|
|
414
|
-
let reinvited;
|
|
407
|
+
let reinvited = null;
|
|
415
408
|
try {
|
|
416
409
|
if (this.currentScreenShare.stream) {
|
|
417
|
-
this.currentScreenShare.stream.getTracks()
|
|
410
|
+
this.currentScreenShare.stream.getTracks()
|
|
411
|
+
.filter((track) => track.enabled)
|
|
412
|
+
.forEach((track) => track.stop());
|
|
418
413
|
}
|
|
419
414
|
if (restoreLocalStream) {
|
|
420
415
|
const targetCallSession = callSession || this.currentCallSession;
|
|
@@ -1098,12 +1093,15 @@ export default class WebRTCPhone extends Emitter {
|
|
|
1098
1093
|
async close(force = false) {
|
|
1099
1094
|
logger.info('WebRTC close');
|
|
1100
1095
|
try {
|
|
1101
|
-
await Promise.race([
|
|
1096
|
+
await Promise.race([
|
|
1097
|
+
this.unregister(),
|
|
1098
|
+
new Promise((resolve, reject) => setTimeout(() => reject(new Error('Unregister, timed out')), 3000)),
|
|
1099
|
+
]);
|
|
1102
1100
|
}
|
|
1103
1101
|
catch (e) {
|
|
1104
1102
|
logger.error('WebRTC close, unregister error', e);
|
|
1105
1103
|
}
|
|
1106
|
-
this.client.close(force);
|
|
1104
|
+
await this.client.close(force);
|
|
1107
1105
|
this.unbind();
|
|
1108
1106
|
this.incomingSessions = [];
|
|
1109
1107
|
this.currentSipSession = undefined;
|
package/esm/domain/Session.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/* eslint-disable no-nested-ternary */
|
|
2
2
|
import { KEYUTIL, KJUR, b64utoutf8 } from 'jsrsasign';
|
|
3
3
|
import swarmPublicKey from '../pubkey';
|
|
4
|
+
import Contact from './Contact';
|
|
4
5
|
import newFrom from '../utils/new-from';
|
|
5
6
|
import compareVersions from '../utils/compare-version';
|
|
6
7
|
const { jws } = KJUR;
|
|
@@ -8,6 +9,7 @@ const swarmKey = (KEYUTIL.getKey(swarmPublicKey));
|
|
|
8
9
|
const MINIMUM_WAZO_ENGINE_VERSION_FOR_DEFAULT_CONTEXT = '19.08';
|
|
9
10
|
export default class Session {
|
|
10
11
|
acl;
|
|
12
|
+
contact;
|
|
11
13
|
token;
|
|
12
14
|
refreshToken;
|
|
13
15
|
uuid;
|
|
@@ -95,6 +97,28 @@ export default class Session {
|
|
|
95
97
|
primaryCtiLine() {
|
|
96
98
|
return this.profile && (this.profile.sipLines || []).find(sipLine => sipLine && !sipLine.isWebRtc());
|
|
97
99
|
}
|
|
100
|
+
getContact() {
|
|
101
|
+
if (this.contact) {
|
|
102
|
+
return this.contact;
|
|
103
|
+
}
|
|
104
|
+
const { email, firstName, lastName, mobileNumber, state, status } = this.profile || {};
|
|
105
|
+
const number = this.primaryNumber() || '';
|
|
106
|
+
const numbers = [{ label: 'primary', number }];
|
|
107
|
+
if (mobileNumber) {
|
|
108
|
+
numbers.push({ label: 'mobile', number: mobileNumber });
|
|
109
|
+
}
|
|
110
|
+
return new Contact({
|
|
111
|
+
uuid: this.uuid || '',
|
|
112
|
+
email,
|
|
113
|
+
name: `${firstName} ${lastName}`,
|
|
114
|
+
number,
|
|
115
|
+
numbers,
|
|
116
|
+
state: state || '',
|
|
117
|
+
status,
|
|
118
|
+
backend: 'wazo',
|
|
119
|
+
source: 'default',
|
|
120
|
+
});
|
|
121
|
+
}
|
|
98
122
|
primaryContext() {
|
|
99
123
|
if (this.engineVersion) {
|
|
100
124
|
if (this.hasEngineVersionGte(MINIMUM_WAZO_ENGINE_VERSION_FOR_DEFAULT_CONTEXT)) {
|
|
@@ -1,4 +1,132 @@
|
|
|
1
1
|
import Contact from '../Contact';
|
|
2
|
+
const genericContactResponse = {
|
|
3
|
+
column_types: [
|
|
4
|
+
'name',
|
|
5
|
+
'number',
|
|
6
|
+
'callable',
|
|
7
|
+
'voicemail',
|
|
8
|
+
'favorite',
|
|
9
|
+
'email',
|
|
10
|
+
'personal',
|
|
11
|
+
],
|
|
12
|
+
term: 'a',
|
|
13
|
+
column_headers: [
|
|
14
|
+
'Nom',
|
|
15
|
+
'Num\u00e9ro',
|
|
16
|
+
'Mobile',
|
|
17
|
+
'Bo\u00eete vocale',
|
|
18
|
+
'Favoris',
|
|
19
|
+
'E-mail',
|
|
20
|
+
'Personnel',
|
|
21
|
+
],
|
|
22
|
+
results: [],
|
|
23
|
+
};
|
|
24
|
+
const contact1 = {
|
|
25
|
+
source: 'internal',
|
|
26
|
+
column_values: [
|
|
27
|
+
'Jonathan Lessard',
|
|
28
|
+
'8020',
|
|
29
|
+
'06800880',
|
|
30
|
+
null,
|
|
31
|
+
false,
|
|
32
|
+
'jonathan@example.com',
|
|
33
|
+
false,
|
|
34
|
+
],
|
|
35
|
+
backend: 'wazo',
|
|
36
|
+
relations: {
|
|
37
|
+
user_id: 8,
|
|
38
|
+
xivo_id: '6cd695d2-cdb9-4444-8b2d-27425ab85fa8',
|
|
39
|
+
agent_id: null,
|
|
40
|
+
endpoint_id: 8,
|
|
41
|
+
user_uuid: 'a14dd6d6-547c-434d-bd5c-e882b5b83b54',
|
|
42
|
+
source_entry_id: '8',
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
const contact2 = {
|
|
46
|
+
source: 'internal',
|
|
47
|
+
column_values: [
|
|
48
|
+
'John Doe',
|
|
49
|
+
'8021',
|
|
50
|
+
'06800881',
|
|
51
|
+
null,
|
|
52
|
+
false,
|
|
53
|
+
'john@example.com',
|
|
54
|
+
false,
|
|
55
|
+
],
|
|
56
|
+
backend: 'wazo',
|
|
57
|
+
relations: {
|
|
58
|
+
user_id: 9,
|
|
59
|
+
xivo_id: '6cd695d2-cdb9-4444-8b2d-27425ab85fa9',
|
|
60
|
+
agent_id: null,
|
|
61
|
+
endpoint_id: 9,
|
|
62
|
+
user_uuid: 'a14dd6d6-547c-434d-bd5c-e882b5b83b55',
|
|
63
|
+
source_entry_id: '9',
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
const parsedContact1 = new Contact({
|
|
67
|
+
name: 'Jonathan Lessard',
|
|
68
|
+
number: '8020',
|
|
69
|
+
numbers: [
|
|
70
|
+
{
|
|
71
|
+
label: 'primary',
|
|
72
|
+
number: '8020',
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
label: 'secondary',
|
|
76
|
+
number: '06800880',
|
|
77
|
+
},
|
|
78
|
+
],
|
|
79
|
+
favorited: false,
|
|
80
|
+
email: 'jonathan@example.com',
|
|
81
|
+
emails: [
|
|
82
|
+
{
|
|
83
|
+
label: 'primary',
|
|
84
|
+
email: 'jonathan@example.com',
|
|
85
|
+
},
|
|
86
|
+
],
|
|
87
|
+
address: '',
|
|
88
|
+
entreprise: '',
|
|
89
|
+
birthday: '',
|
|
90
|
+
note: '',
|
|
91
|
+
endpointId: 8,
|
|
92
|
+
personal: false,
|
|
93
|
+
source: 'internal',
|
|
94
|
+
sourceId: '8',
|
|
95
|
+
uuid: 'a14dd6d6-547c-434d-bd5c-e882b5b83b54',
|
|
96
|
+
backend: 'wazo',
|
|
97
|
+
});
|
|
98
|
+
const parsedContact2 = new Contact({
|
|
99
|
+
name: 'John Doe',
|
|
100
|
+
number: '8021',
|
|
101
|
+
numbers: [
|
|
102
|
+
{
|
|
103
|
+
label: 'primary',
|
|
104
|
+
number: '8021',
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
label: 'secondary',
|
|
108
|
+
number: '06800881',
|
|
109
|
+
},
|
|
110
|
+
],
|
|
111
|
+
favorited: false,
|
|
112
|
+
email: 'john@example.com',
|
|
113
|
+
emails: [
|
|
114
|
+
{
|
|
115
|
+
label: 'primary',
|
|
116
|
+
email: 'john@example.com',
|
|
117
|
+
},
|
|
118
|
+
],
|
|
119
|
+
address: '',
|
|
120
|
+
entreprise: '',
|
|
121
|
+
birthday: '',
|
|
122
|
+
note: '',
|
|
123
|
+
endpointId: 9,
|
|
124
|
+
personal: false,
|
|
125
|
+
source: 'internal',
|
|
126
|
+
sourceId: '9',
|
|
127
|
+
uuid: 'a14dd6d6-547c-434d-bd5c-e882b5b83b55',
|
|
128
|
+
backend: 'wazo',
|
|
129
|
+
});
|
|
2
130
|
describe('Contact domain', () => {
|
|
3
131
|
it('is extern when given no uuid', () => {
|
|
4
132
|
const boba = new Contact({});
|
|
@@ -131,7 +259,7 @@ describe('Contact domain', () => {
|
|
|
131
259
|
const contact = new Contact({
|
|
132
260
|
name: 'John Smith',
|
|
133
261
|
});
|
|
134
|
-
const { firstName, lastName
|
|
262
|
+
const { firstName, lastName } = contact.separateName();
|
|
135
263
|
expect(firstName).toBe('John');
|
|
136
264
|
expect(lastName).toBe('Smith');
|
|
137
265
|
});
|
|
@@ -139,7 +267,7 @@ describe('Contact domain', () => {
|
|
|
139
267
|
const contact = new Contact({
|
|
140
268
|
name: 'John Smith',
|
|
141
269
|
});
|
|
142
|
-
const { firstName, lastName
|
|
270
|
+
const { firstName, lastName } = contact.separateName();
|
|
143
271
|
expect(firstName).toBe('John');
|
|
144
272
|
expect(lastName).toBe('Smith');
|
|
145
273
|
});
|
|
@@ -165,67 +293,91 @@ describe('Contact domain', () => {
|
|
|
165
293
|
});
|
|
166
294
|
it('can parse a plain contact to domain', () => {
|
|
167
295
|
const response = {
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
column_headers: ['Nom', 'Num\u00e9ro', 'Mobile', 'Bo\u00eete vocale', 'Favoris', 'E-mail', 'Personnel'],
|
|
171
|
-
results: [{
|
|
172
|
-
source: 'internal',
|
|
173
|
-
column_values: ['Jonathan Lessard', '8020', '06800880', null, false, 'contact@nexapp.ca', false],
|
|
174
|
-
backend: 'wazo',
|
|
175
|
-
relations: {
|
|
176
|
-
user_id: 8,
|
|
177
|
-
xivo_id: '6cd695d2-cdb9-4444-8b2d-27425ab85fa8',
|
|
178
|
-
agent_id: null,
|
|
179
|
-
endpoint_id: 8,
|
|
180
|
-
user_uuid: 'a14dd6d6-547c-434d-bd5c-e882b5b83b54',
|
|
181
|
-
source_entry_id: '8',
|
|
182
|
-
},
|
|
183
|
-
}],
|
|
296
|
+
...genericContactResponse,
|
|
297
|
+
results: [contact1],
|
|
184
298
|
};
|
|
185
299
|
const contact = Contact.parse(response.results[0], response.column_types);
|
|
186
|
-
expect(contact).toEqual(
|
|
187
|
-
name: 'Jonathan Lessard',
|
|
188
|
-
number: '8020',
|
|
189
|
-
numbers: [{
|
|
190
|
-
label: 'primary',
|
|
191
|
-
number: '8020',
|
|
192
|
-
}, {
|
|
193
|
-
label: 'secondary',
|
|
194
|
-
number: '06800880',
|
|
195
|
-
}],
|
|
196
|
-
favorited: false,
|
|
197
|
-
email: 'contact@nexapp.ca',
|
|
198
|
-
emails: [{
|
|
199
|
-
label: 'primary',
|
|
200
|
-
email: 'contact@nexapp.ca',
|
|
201
|
-
}],
|
|
202
|
-
address: '',
|
|
203
|
-
entreprise: '',
|
|
204
|
-
birthday: '',
|
|
205
|
-
note: '',
|
|
206
|
-
endpointId: 8,
|
|
207
|
-
personal: false,
|
|
208
|
-
source: 'internal',
|
|
209
|
-
sourceId: '8',
|
|
210
|
-
uuid: 'a14dd6d6-547c-434d-bd5c-e882b5b83b54',
|
|
211
|
-
backend: 'wazo',
|
|
212
|
-
}));
|
|
300
|
+
expect(contact).toEqual(parsedContact1);
|
|
213
301
|
});
|
|
214
302
|
it('should merge 2 contact', () => {
|
|
215
|
-
const
|
|
303
|
+
const noBirthdayContact = new Contact({
|
|
216
304
|
uuid: 'uuid-12345',
|
|
217
305
|
lastActivity: 'yesterday',
|
|
218
306
|
// @ts-expect-error
|
|
219
307
|
birthday: null,
|
|
220
308
|
});
|
|
221
|
-
const
|
|
309
|
+
const noLastActivityContact = new Contact({
|
|
222
310
|
uuid: 'uuid-12345',
|
|
223
311
|
// @ts-expect-error
|
|
224
312
|
lastActivity: null,
|
|
225
313
|
birthday: 'tomorrow',
|
|
226
314
|
});
|
|
227
|
-
const result =
|
|
315
|
+
const result = noBirthdayContact.merge(noLastActivityContact);
|
|
228
316
|
expect(result.lastActivity).toBe('yesterday');
|
|
229
317
|
expect(result.birthday).toBe('tomorrow');
|
|
230
318
|
});
|
|
319
|
+
it('returns all contacts when parsing many contacts with no limit', () => {
|
|
320
|
+
const response = {
|
|
321
|
+
...genericContactResponse,
|
|
322
|
+
results: [contact1, contact2],
|
|
323
|
+
};
|
|
324
|
+
const contacts = Contact.parseMany(response);
|
|
325
|
+
expect(contacts).toEqual([parsedContact1, parsedContact2]);
|
|
326
|
+
});
|
|
327
|
+
it('returns all contacts when parsing many contacts with a higher limit than number of results', () => {
|
|
328
|
+
const response = {
|
|
329
|
+
...genericContactResponse,
|
|
330
|
+
results: [contact1, contact2],
|
|
331
|
+
};
|
|
332
|
+
const contacts = Contact.parseMany(response, 0, 1000);
|
|
333
|
+
expect(contacts).toEqual([parsedContact1, parsedContact2]);
|
|
334
|
+
});
|
|
335
|
+
it('returns limited contacts when parsing many contacts with a smaller limit than number of results', () => {
|
|
336
|
+
const response = {
|
|
337
|
+
...genericContactResponse,
|
|
338
|
+
results: [contact1],
|
|
339
|
+
};
|
|
340
|
+
const contacts = Contact.parseMany(response, 0, 1);
|
|
341
|
+
expect(contacts).toEqual([parsedContact1]);
|
|
342
|
+
});
|
|
343
|
+
it('returns all contacts when parsing many contacts with same limit than number of results', () => {
|
|
344
|
+
const response = {
|
|
345
|
+
...genericContactResponse,
|
|
346
|
+
results: [contact1, contact2],
|
|
347
|
+
};
|
|
348
|
+
const contacts = Contact.parseMany(response, 0, 2);
|
|
349
|
+
expect(contacts).toEqual([parsedContact1, parsedContact2]);
|
|
350
|
+
});
|
|
351
|
+
it('returns an empty array when parsing many contact with limit of 0', () => {
|
|
352
|
+
const response = {
|
|
353
|
+
...genericContactResponse,
|
|
354
|
+
results: [contact1, contact2],
|
|
355
|
+
};
|
|
356
|
+
const contacts = Contact.parseMany(response, 0, 0);
|
|
357
|
+
expect(contacts).toEqual([]);
|
|
358
|
+
});
|
|
359
|
+
it('returns all contacts when parsing many contacts with a negative limit', () => {
|
|
360
|
+
const response = {
|
|
361
|
+
...genericContactResponse,
|
|
362
|
+
results: [contact1, contact2],
|
|
363
|
+
};
|
|
364
|
+
const contacts = Contact.parseMany(response, 0, -1);
|
|
365
|
+
expect(contacts).toEqual([parsedContact1, parsedContact2]);
|
|
366
|
+
});
|
|
367
|
+
it('returns all contacts when parsing many contacts with a null limit', () => {
|
|
368
|
+
const response = {
|
|
369
|
+
...genericContactResponse,
|
|
370
|
+
results: [contact1, contact2],
|
|
371
|
+
};
|
|
372
|
+
const contacts = Contact.parseMany(response, 0, null);
|
|
373
|
+
expect(contacts).toEqual([parsedContact1, parsedContact2]);
|
|
374
|
+
});
|
|
375
|
+
it('returns all contacts after the first one when parsing many contacts with an offset of 1', () => {
|
|
376
|
+
const response = {
|
|
377
|
+
...genericContactResponse,
|
|
378
|
+
results: [contact1, contact2],
|
|
379
|
+
};
|
|
380
|
+
const contacts = Contact.parseMany(response, 1, null);
|
|
381
|
+
expect(contacts).toEqual([parsedContact2]);
|
|
382
|
+
});
|
|
231
383
|
});
|
|
@@ -170,20 +170,23 @@ class WazoSessionDescriptionHandler extends SessionDescriptionHandler {
|
|
|
170
170
|
if (this._peerConnection === undefined) {
|
|
171
171
|
return;
|
|
172
172
|
}
|
|
173
|
-
// Closing senders via getLocalStreams
|
|
173
|
+
// Closing senders via getLocalStreams, `getLocalStreams` is deprecated, we have to check if it exists.
|
|
174
174
|
// @ts-ignore
|
|
175
|
-
this.peerConnection?.getLocalStreams
|
|
176
|
-
|
|
177
|
-
|
|
175
|
+
if (this.peerConnection?.getLocalStreams) {
|
|
176
|
+
// @ts-ignore
|
|
177
|
+
this.peerConnection?.getLocalStreams().forEach(stream => {
|
|
178
|
+
stream.getTracks()
|
|
179
|
+
.filter((track) => track.enabled)
|
|
180
|
+
.forEach((track) => track.stop());
|
|
178
181
|
});
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
182
|
+
// Closing receivers via getRemoteStreams
|
|
183
|
+
// @ts-ignore
|
|
184
|
+
this.peerConnection?.getRemoteStreams().forEach(stream => {
|
|
185
|
+
stream.getTracks()
|
|
186
|
+
.filter((track) => track.enabled)
|
|
187
|
+
.forEach((track) => track.stop());
|
|
185
188
|
});
|
|
186
|
-
}
|
|
189
|
+
}
|
|
187
190
|
if (this._dataChannel) {
|
|
188
191
|
this._dataChannel.close();
|
|
189
192
|
}
|
|
@@ -12,7 +12,7 @@ const ERROR = 'error';
|
|
|
12
12
|
const CONSOLE_METHODS = [INFO, LOG, WARN, ERROR];
|
|
13
13
|
const LOG_LEVELS = [TRACE, DEBUG, INFO, LOG, WARN, ERROR];
|
|
14
14
|
const CATEGORY_PREFIX = 'logger-category=';
|
|
15
|
-
const MAX_REMOTE_RETRY =
|
|
15
|
+
const MAX_REMOTE_RETRY = 10;
|
|
16
16
|
const addLevelsTo = (instance, withMethods = false) => {
|
|
17
17
|
instance.TRACE = TRACE;
|
|
18
18
|
instance.DEBUG = DEBUG;
|
|
@@ -265,6 +265,14 @@ class IssueReporter {
|
|
|
265
265
|
this.bufferTimeout = null;
|
|
266
266
|
}
|
|
267
267
|
}
|
|
268
|
+
_computeRetryDelay(attempt, initial = 1000, maxWait = 50000) {
|
|
269
|
+
// min wait is initial(attempt = 0)
|
|
270
|
+
// later retries are randomly distributed between initial wait and exponential
|
|
271
|
+
const base = 1.5;
|
|
272
|
+
const wait = Math.min(initial * base ** attempt, maxWait);
|
|
273
|
+
const jitterWait = Math.max(initial, Math.random() * wait);
|
|
274
|
+
return jitterWait;
|
|
275
|
+
}
|
|
268
276
|
_sendDebugToGrafana(payload, retry = 0) {
|
|
269
277
|
if (!this.remoteClientConfiguration || retry >= MAX_REMOTE_RETRY) {
|
|
270
278
|
return;
|
|
@@ -284,6 +292,8 @@ class IssueReporter {
|
|
|
284
292
|
// @ts-ignore
|
|
285
293
|
e.skipSendToRemote = true;
|
|
286
294
|
this.log('error', this._makeCategory('grafana'), 'Sending log to grafana, error', e);
|
|
295
|
+
// wait at least 1 second, at most 50 seconds
|
|
296
|
+
const wait = this._computeRetryDelay(retry, 1000, 50000);
|
|
287
297
|
setTimeout(() => {
|
|
288
298
|
if (Array.isArray(payload)) {
|
|
289
299
|
payload = payload.map(message => this._writeRetryCount(message, retry + 1));
|
|
@@ -292,7 +302,7 @@ class IssueReporter {
|
|
|
292
302
|
payload = this._writeRetryCount(payload, retry + 1);
|
|
293
303
|
}
|
|
294
304
|
this._sendDebugToGrafana(payload, retry + 1);
|
|
295
|
-
},
|
|
305
|
+
}, wait);
|
|
296
306
|
});
|
|
297
307
|
}
|
|
298
308
|
_writeRetryCount(message, count) {
|
|
@@ -97,4 +97,16 @@ describe('IssueReporter', () => {
|
|
|
97
97
|
_retry: 1,
|
|
98
98
|
});
|
|
99
99
|
});
|
|
100
|
+
it('retry delay', () => {
|
|
101
|
+
expect(IssueReporter._computeRetryDelay(0, 1000, 50000)).toBe(1000);
|
|
102
|
+
expect(IssueReporter._computeRetryDelay(1, 1000, 50000)).toBeGreaterThanOrEqual(1000);
|
|
103
|
+
const samples = [];
|
|
104
|
+
for (let i = 0; i <= 10; i++) {
|
|
105
|
+
samples.push(IssueReporter._computeRetryDelay(i, 1000, 50000));
|
|
106
|
+
}
|
|
107
|
+
const min = Math.min(...samples);
|
|
108
|
+
expect(min).toBe(1000);
|
|
109
|
+
const max = Math.max(...samples);
|
|
110
|
+
expect(max).toBeLessThanOrEqual(50000);
|
|
111
|
+
});
|
|
100
112
|
});
|
|
@@ -14,6 +14,7 @@ global.wazoOnRefreshTokenError = global.wazoOnRefreshTokenError || {};
|
|
|
14
14
|
global.wazoRefreshExpiration = global.wazoRefreshExpiration || {};
|
|
15
15
|
global.wazoRefreshBackend = global.wazoRefreshBackend || {};
|
|
16
16
|
global.wazoIsMobile = global.wazoIsMobile || {};
|
|
17
|
+
global.wazoRequestApiTimeout = global.wazoRequestApiTimeout || {};
|
|
17
18
|
global.wazoFetchOptions = global.wazoFetchOptions || {};
|
|
18
19
|
export const setApiClientId = (clientId, forServer = null) => {
|
|
19
20
|
global.wazoClientId[String(forServer)] = clientId;
|
|
@@ -49,12 +50,17 @@ export const setRefreshBackend = (refreshBackend, forServer = null) => {
|
|
|
49
50
|
export const setIsMobile = (isMobile, forServer = null) => {
|
|
50
51
|
global.wazoIsMobile[String(forServer)] = isMobile;
|
|
51
52
|
};
|
|
53
|
+
export const setRequestTimeout = (requestTimeout, forServer = null) => {
|
|
54
|
+
global.wazoRequestApiTimeout[String(forServer)] = requestTimeout;
|
|
55
|
+
};
|
|
52
56
|
export const setFetchOptions = (fetchOptions, forServer = null) => {
|
|
53
57
|
global.wazoFetchOptions[String(forServer)] = fetchOptions;
|
|
54
58
|
};
|
|
59
|
+
export const getFetchOptions = (forServer = null) => (forServer ? global.wazoFetchOptions[forServer] : global.wazoFetchOptions.null) || global.wazoFetchOptions.null;
|
|
55
60
|
const fillClient = (apiClient) => {
|
|
56
61
|
const { server, token, clientId, } = apiClient.client;
|
|
57
62
|
const tenantId = global.wazoRefreshTenantId[server] || global.wazoRefreshTenantId.null || apiClient.refreshTenantId;
|
|
63
|
+
const requestTimeout = global.wazoRequestApiTimeout[server] || global.wazoRequestApiTimeout.null;
|
|
58
64
|
// try with null server when dealing with non-related server info
|
|
59
65
|
apiClient.setToken(global.wazoClientToken[server] || global.wazoClientToken.null || token);
|
|
60
66
|
apiClient.setClientId(global.wazoClientId[server] || global.wazoClientId.null || clientId);
|
|
@@ -69,6 +75,9 @@ const fillClient = (apiClient) => {
|
|
|
69
75
|
apiClient.setRefreshExpiration(global.wazoRefreshExpiration[server] || global.wazoRefreshExpiration.null || apiClient.refreshExpiration);
|
|
70
76
|
apiClient.setRefreshBackend(global.wazoRefreshBackend[server] || global.wazoRefreshBackend.null || apiClient.refreshBackend);
|
|
71
77
|
apiClient.setIsMobile(global.wazoIsMobile[server] || global.wazoIsMobile.null || apiClient.isMobile);
|
|
78
|
+
if (requestTimeout) {
|
|
79
|
+
apiClient.setRequestTimeout(requestTimeout);
|
|
80
|
+
}
|
|
72
81
|
return apiClient;
|
|
73
82
|
};
|
|
74
83
|
export default ((forServer = null) => {
|