@wazo/sdk 0.39.0-beta.8 → 0.39.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/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/chatd.js +17 -3
- 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/chatd.d.ts +1 -1
- package/esm/types/api/chatd.d.ts.map +1 -1
- 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 +69 -40
- 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/chatd.js +23 -7
- 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/chatd.d.ts +1 -1
- package/lib/types/api/chatd.d.ts.map +1 -1
- 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 +67 -39
- 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/chatd.ts +22 -4
- 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 +84 -70
- package/src/websocket-client.ts +14 -1
|
@@ -1,9 +1,13 @@
|
|
|
1
|
+
import { AbortController } from 'node-abort-controller';
|
|
1
2
|
import { Base64 } from 'js-base64';
|
|
3
|
+
|
|
2
4
|
import WazoApiClient from '../api-client';
|
|
3
5
|
import ServerError from '../domain/ServerError';
|
|
4
6
|
import BadResponse from '../domain/BadResponse';
|
|
5
7
|
import Session from '../domain/Session';
|
|
6
8
|
|
|
9
|
+
const controller = new AbortController();
|
|
10
|
+
|
|
7
11
|
const mockedResponse = {
|
|
8
12
|
data: {
|
|
9
13
|
token: 1,
|
|
@@ -75,7 +79,7 @@ const server = 'localhost';
|
|
|
75
79
|
const authVersion = '0.1';
|
|
76
80
|
const username = 'wazo';
|
|
77
81
|
const password = 'zowa';
|
|
78
|
-
jest.mock('node-fetch/
|
|
82
|
+
jest.mock('node-fetch/src/index', () => {});
|
|
79
83
|
const token = '1234';
|
|
80
84
|
|
|
81
85
|
const client = new WazoApiClient({
|
|
@@ -112,6 +116,7 @@ describe('With correct API results', () => {
|
|
|
112
116
|
expect(global.fetch).toBeCalledWith(`https://${server}/api/auth/${authVersion}/token`, {
|
|
113
117
|
method: 'post',
|
|
114
118
|
body: JSON.stringify(data),
|
|
119
|
+
signal: controller.signal,
|
|
115
120
|
headers,
|
|
116
121
|
agent: null,
|
|
117
122
|
});
|
|
@@ -126,6 +131,7 @@ describe('With correct API results', () => {
|
|
|
126
131
|
expect(global.fetch).toBeCalledWith(`https://${server}/api/auth/${authVersion}/token/${oldToken}`, {
|
|
127
132
|
method: 'delete',
|
|
128
133
|
body: null,
|
|
134
|
+
signal: controller.signal,
|
|
129
135
|
headers: {},
|
|
130
136
|
agent: null,
|
|
131
137
|
});
|
|
@@ -148,6 +154,7 @@ describe('With unAuthorized API results', () => {
|
|
|
148
154
|
expect(global.fetch).toBeCalledWith(`https://${server}/api/auth/${authVersion}/token/${tokenToCheck}`, {
|
|
149
155
|
method: 'head',
|
|
150
156
|
body: null,
|
|
157
|
+
signal: controller.signal,
|
|
151
158
|
headers: {},
|
|
152
159
|
agent: null,
|
|
153
160
|
});
|
|
@@ -179,6 +186,7 @@ describe('With not found API results', () => {
|
|
|
179
186
|
expect(global.fetch).toBeCalledWith(`https://${server}/api/calld/1.0/users/me/voicemails`, {
|
|
180
187
|
method: 'get',
|
|
181
188
|
body: null,
|
|
189
|
+
signal: controller.signal,
|
|
182
190
|
headers: {
|
|
183
191
|
'X-Auth-Token': token,
|
|
184
192
|
'Content-Type': 'application/json',
|
|
@@ -1,6 +1,18 @@
|
|
|
1
1
|
import WebRTCClient from '../web-rtc-client';
|
|
2
2
|
|
|
3
3
|
jest.mock('sip.js/lib/platform/web/transport');
|
|
4
|
+
jest.mock('sip.js/lib/api/user-agent', () => ({
|
|
5
|
+
start: () => {},
|
|
6
|
+
UserAgent: class UserAgent {
|
|
7
|
+
// @ts-ignore
|
|
8
|
+
static makeURI = () => {};
|
|
9
|
+
|
|
10
|
+
// @ts-ignore
|
|
11
|
+
start = () => {};
|
|
12
|
+
|
|
13
|
+
transport = {};
|
|
14
|
+
},
|
|
15
|
+
}));
|
|
4
16
|
|
|
5
17
|
// @ts-expect-error
|
|
6
18
|
const client = new WebRTCClient({});
|
package/src/api/chatd.ts
CHANGED
|
@@ -38,7 +38,7 @@ export interface ChatD {
|
|
|
38
38
|
getState: (contactUuid: UUID) => Promise<string>;
|
|
39
39
|
getContactStatusInfo: (contactUuid: UUID) => Promise<PresenceResponse>;
|
|
40
40
|
getLineState: (contactUuid: UUID) => Promise<string>;
|
|
41
|
-
getMultipleLineState: (contactUuids: Array<UUID> | null | undefined) => Promise<
|
|
41
|
+
getMultipleLineState: (contactUuids: Array<UUID> | null | undefined) => Promise<Array<PresenceResponse>>;
|
|
42
42
|
getUserRooms: () => Promise<Array<ChatRoom>>;
|
|
43
43
|
createRoom: (name: string, users: Array<ChatUser>) => Promise<ChatRoom>;
|
|
44
44
|
getRoomMessages: (roomUuid: string, params?: GetMessagesOptions) => Promise<Array<ChatMessage>>;
|
|
@@ -46,6 +46,9 @@ export interface ChatD {
|
|
|
46
46
|
getMessages: (options: GetMessagesOptions) => Promise<ChatMessage>;
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
+
// split contact status retrieval to avoid `414 Request-URI Too Large`.
|
|
50
|
+
const MAX_PRESENCE_FETCHED = 30;
|
|
51
|
+
|
|
49
52
|
export default ((client: ApiRequester, baseUrl: string): ChatD => ({
|
|
50
53
|
updateState: (contactUuid: UUID, state: string): Promise<boolean> => client.put(`${baseUrl}/users/${contactUuid}/presences`, {
|
|
51
54
|
state,
|
|
@@ -60,11 +63,26 @@ export default ((client: ApiRequester, baseUrl: string): ChatD => ({
|
|
|
60
63
|
getState: async (contactUuid: UUID): Promise<string> => client.get(`${baseUrl}/users/${contactUuid}/presences`).then((response: PresenceResponse) => response.state),
|
|
61
64
|
getContactStatusInfo: async (contactUuid: UUID): Promise<PresenceResponse> => client.get(`${baseUrl}/users/${contactUuid}/presences`).then((response: PresenceResponse) => response),
|
|
62
65
|
getLineState: async (contactUuid: UUID): Promise<string> => client.get(`${baseUrl}/users/${contactUuid}/presences`).then((response: PresenceResponse) => Profile.getLinesState(response.lines)),
|
|
63
|
-
|
|
66
|
+
async getMultipleLineState(contactUuids: Array<UUID> | null | undefined): Promise<Array<PresenceResponse>> {
|
|
64
67
|
const body: any = {};
|
|
68
|
+
const uuids: Array<UUID> = contactUuids || [];
|
|
69
|
+
|
|
70
|
+
if (uuids.length > MAX_PRESENCE_FETCHED) {
|
|
71
|
+
const requests = uuids.reduce((acc, _, i) => {
|
|
72
|
+
if (i % MAX_PRESENCE_FETCHED === 0) {
|
|
73
|
+
// @ts-ignore
|
|
74
|
+
acc.push(this.getMultipleLineState(uuids.slice(i, i + MAX_PRESENCE_FETCHED)));
|
|
75
|
+
}
|
|
76
|
+
return acc;
|
|
77
|
+
}, []);
|
|
78
|
+
|
|
79
|
+
const splittedStatuses = await Promise.all(requests);
|
|
80
|
+
|
|
81
|
+
return splittedStatuses.flat();
|
|
82
|
+
}
|
|
65
83
|
|
|
66
|
-
if (
|
|
67
|
-
body.user_uuid =
|
|
84
|
+
if (uuids.length) {
|
|
85
|
+
body.user_uuid = uuids.join(',');
|
|
68
86
|
}
|
|
69
87
|
|
|
70
88
|
return client.get(`${baseUrl}/users/presences`, body).then((response: PresenceListResponse) => response.items);
|
package/src/api/dird.ts
CHANGED
|
@@ -5,6 +5,7 @@ import Contact from '../domain/Contact';
|
|
|
5
5
|
import type { NewContact } from '../domain/Contact';
|
|
6
6
|
import type { DirectorySource, DirectorySources } from '../domain/DirectorySource';
|
|
7
7
|
import type { Sources } from '../index';
|
|
8
|
+
import { ContactsResponse } from '../index';
|
|
8
9
|
|
|
9
10
|
const getContactPayload = (contact: NewContact | Contact) => ({
|
|
10
11
|
email: contact.email,
|
|
@@ -51,9 +52,9 @@ type ContactSearchQueryParams = {
|
|
|
51
52
|
uuid?: string;
|
|
52
53
|
} | null;
|
|
53
54
|
export default ((client: ApiRequester, baseUrl: string): DirD => ({
|
|
54
|
-
search: (context: string, term: string): Promise<Array<Contact>> => client.get(`${baseUrl}/directories/lookup/${context}`, {
|
|
55
|
+
search: (context: string, term: string, offset = 0, limit: (number | null) = null): Promise<Array<Contact>> => client.get(`${baseUrl}/directories/lookup/${context}`, {
|
|
55
56
|
term,
|
|
56
|
-
}).then(Contact.parseMany),
|
|
57
|
+
}).then((response: ContactsResponse) => Contact.parseMany(response, offset, limit)),
|
|
57
58
|
listPersonalContacts: (queryParams: ContactSearchQueryParams = null): Promise<Array<Contact>> => client.get(`${baseUrl}/personal`, queryParams).then((response: any) => Contact.parseManyPersonal(response.items)),
|
|
58
59
|
fetchPersonalContact: (contactUuid: string): Promise<Contact> => client.get(`${baseUrl}/personal/${contactUuid}`).then(Contact.parsePersonal),
|
|
59
60
|
addContact: (contact: NewContact): Promise<Contact> => client.post(`${baseUrl}/personal`, getContactPayload(contact)).then(Contact.parsePersonal),
|
package/src/api-client.ts
CHANGED
|
@@ -125,12 +125,12 @@ export default class ApiClient {
|
|
|
125
125
|
this.initializeEndpoints();
|
|
126
126
|
}
|
|
127
127
|
|
|
128
|
-
async forceRefreshToken() {
|
|
128
|
+
async forceRefreshToken(): Promise<string | null> {
|
|
129
129
|
logger.info('forcing refresh token, calling callback');
|
|
130
130
|
return this.refreshTokenCallback();
|
|
131
131
|
}
|
|
132
132
|
|
|
133
|
-
async refreshTokenCallback() {
|
|
133
|
+
async refreshTokenCallback(): Promise<string | null> {
|
|
134
134
|
logger.info('refresh token callback called', {
|
|
135
135
|
refreshToken: this.refreshToken,
|
|
136
136
|
refreshBackend: this.refreshBackend,
|
|
@@ -167,6 +167,8 @@ export default class ApiClient {
|
|
|
167
167
|
this.onRefreshTokenError(error);
|
|
168
168
|
}
|
|
169
169
|
}
|
|
170
|
+
|
|
171
|
+
return null;
|
|
170
172
|
}
|
|
171
173
|
|
|
172
174
|
setToken(token: string) {
|
|
@@ -181,6 +183,10 @@ export default class ApiClient {
|
|
|
181
183
|
this.refreshToken = refreshToken;
|
|
182
184
|
}
|
|
183
185
|
|
|
186
|
+
setRequestTimeout(requestTimeout: number) {
|
|
187
|
+
this.client.setRequestTimeout(requestTimeout);
|
|
188
|
+
}
|
|
189
|
+
|
|
184
190
|
setClientId(clientId: string | null | undefined) {
|
|
185
191
|
this.client.clientId = clientId;
|
|
186
192
|
}
|
|
@@ -16,7 +16,8 @@ export default {
|
|
|
16
16
|
name: 'Non IP v4 ice',
|
|
17
17
|
check: (server: string, session: any, externalAppConfig: Record<string, any>) => new Promise((resolve, reject) => {
|
|
18
18
|
if (typeof MediaStream === 'undefined') {
|
|
19
|
-
|
|
19
|
+
resolve('Skipped on node');
|
|
20
|
+
return;
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
const mobile = isMobile();
|
package/src/domain/Contact.ts
CHANGED
|
@@ -326,12 +326,13 @@ export default class Contact {
|
|
|
326
326
|
return aLastName.localeCompare(bLastName);
|
|
327
327
|
}
|
|
328
328
|
|
|
329
|
-
static parseMany(response: ContactsResponse): Array<Contact> {
|
|
330
|
-
if (!response || !response.results) {
|
|
329
|
+
static parseMany(response: ContactsResponse, offset = 0, limit: (number | null) = null): Array<Contact> {
|
|
330
|
+
if (!response || !response.results || limit === 0) {
|
|
331
331
|
return [];
|
|
332
332
|
}
|
|
333
333
|
|
|
334
|
-
|
|
334
|
+
const results = limit !== null && limit > 0 ? response.results.slice(offset, limit) : offset > 0 ? response.results.slice(offset) : response.results;
|
|
335
|
+
return results.map(r => Contact.parse(r, response.column_types));
|
|
335
336
|
}
|
|
336
337
|
|
|
337
338
|
static manyGraphQlWithNumbersParser(numbers: string[]): (...args: Array<any>) => any {
|
|
@@ -444,13 +445,20 @@ export default class Contact {
|
|
|
444
445
|
address = `${postalAddress.street} ${postalAddress.city} ${postalAddress.postCode} ${postalAddress.country}`;
|
|
445
446
|
}
|
|
446
447
|
|
|
448
|
+
const firstName = plain.givenName || '';
|
|
449
|
+
const lastName = plain.familyName || '';
|
|
450
|
+
const companyName = plain.company || '';
|
|
451
|
+
const isCompanyAccount = !firstName && !lastName;
|
|
452
|
+
const name = isCompanyAccount ? companyName : `${firstName} ${lastName}`;
|
|
453
|
+
|
|
447
454
|
return new Contact({
|
|
448
|
-
name
|
|
455
|
+
name,
|
|
449
456
|
number: plain.phoneNumbers.length ? plain.phoneNumbers[0].number : '',
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
457
|
+
// Make numbers unique
|
|
458
|
+
numbers: [...new Map(plain.phoneNumbers.map(item => [item.number, item])).values()].map((item, idx) => ({
|
|
459
|
+
label: idx === 0 ? 'primary' : 'secondary',
|
|
460
|
+
number: item.number,
|
|
461
|
+
})),
|
|
454
462
|
email: plain.emailAddresses.length ? plain.emailAddresses[0].email : '',
|
|
455
463
|
emails: plain.emailAddresses.length ? [{
|
|
456
464
|
label: 'primary',
|
|
@@ -382,8 +382,6 @@ export default class WebRTCPhone extends Emitter implements Phone {
|
|
|
382
382
|
delete this.callSessions[sipSessionId];
|
|
383
383
|
this.eventEmitter.emit(ON_CALL_CANCELED, callSession, elsewhere);
|
|
384
384
|
};
|
|
385
|
-
} else if (sipSession instanceof Invitation) {
|
|
386
|
-
console.warn('sipSession._onCancel not found, please update the wazo SDK accordingly');
|
|
387
385
|
}
|
|
388
386
|
|
|
389
387
|
sipSession.stateChange.addListener((newState: SessionState) => {
|
|
@@ -453,11 +451,6 @@ export default class WebRTCPhone extends Emitter implements Phone {
|
|
|
453
451
|
return this.eventEmitter.emit(ON_AUDIO_STREAM, stream);
|
|
454
452
|
}
|
|
455
453
|
|
|
456
|
-
// not sure this does anything
|
|
457
|
-
if (kind === 'video') {
|
|
458
|
-
event.track.enabled = false;
|
|
459
|
-
}
|
|
460
|
-
|
|
461
454
|
return this.eventEmitter.emit(ON_VIDEO_STREAM, stream, event.track.id, event, sipSession);
|
|
462
455
|
};
|
|
463
456
|
|
|
@@ -509,7 +502,7 @@ export default class WebRTCPhone extends Emitter implements Phone {
|
|
|
509
502
|
return screenShareStream;
|
|
510
503
|
}
|
|
511
504
|
|
|
512
|
-
async stopScreenSharing(restoreLocalStream = true, callSession?: CallSession | null | undefined): Promise<OutgoingInviteRequest | void> {
|
|
505
|
+
async stopScreenSharing(restoreLocalStream = true, callSession?: CallSession | null | undefined): Promise<OutgoingInviteRequest | void | null> {
|
|
513
506
|
if (!this.currentScreenShare) {
|
|
514
507
|
return;
|
|
515
508
|
}
|
|
@@ -517,11 +510,13 @@ export default class WebRTCPhone extends Emitter implements Phone {
|
|
|
517
510
|
logger.info('WebRTC phone - stop screen sharing', {
|
|
518
511
|
restoreLocalStream,
|
|
519
512
|
});
|
|
520
|
-
let reinvited: OutgoingInviteRequest | void;
|
|
513
|
+
let reinvited: OutgoingInviteRequest | null | void = null;
|
|
521
514
|
|
|
522
515
|
try {
|
|
523
516
|
if (this.currentScreenShare.stream) {
|
|
524
|
-
this.currentScreenShare.stream.getTracks()
|
|
517
|
+
this.currentScreenShare.stream.getTracks()
|
|
518
|
+
.filter((track: MediaStreamTrack) => track.enabled)
|
|
519
|
+
.forEach((track: MediaStreamTrack) => track.stop());
|
|
525
520
|
}
|
|
526
521
|
|
|
527
522
|
if (restoreLocalStream) {
|
|
@@ -552,6 +547,7 @@ export default class WebRTCPhone extends Emitter implements Phone {
|
|
|
552
547
|
screensharing: false,
|
|
553
548
|
}) : null);
|
|
554
549
|
this.currentScreenShare = null;
|
|
550
|
+
|
|
555
551
|
return reinvited;
|
|
556
552
|
}
|
|
557
553
|
|
|
@@ -1369,12 +1365,15 @@ export default class WebRTCPhone extends Emitter implements Phone {
|
|
|
1369
1365
|
logger.info('WebRTC close');
|
|
1370
1366
|
|
|
1371
1367
|
try {
|
|
1372
|
-
await Promise.race([
|
|
1368
|
+
await Promise.race([
|
|
1369
|
+
this.unregister(),
|
|
1370
|
+
new Promise((resolve, reject) => setTimeout(() => reject(new Error('Unregister, timed out')), 3000)),
|
|
1371
|
+
]);
|
|
1373
1372
|
} catch (e: any) {
|
|
1374
1373
|
logger.error('WebRTC close, unregister error', e);
|
|
1375
1374
|
}
|
|
1376
1375
|
|
|
1377
|
-
this.client.close(force);
|
|
1376
|
+
await this.client.close(force);
|
|
1378
1377
|
this.unbind();
|
|
1379
1378
|
this.incomingSessions = [];
|
|
1380
1379
|
this.currentSipSession = undefined;
|
package/src/domain/Session.ts
CHANGED
|
@@ -72,6 +72,8 @@ type SessionArguments = {
|
|
|
72
72
|
export default class Session {
|
|
73
73
|
acl: string[];
|
|
74
74
|
|
|
75
|
+
contact?: Contact;
|
|
76
|
+
|
|
75
77
|
token: string;
|
|
76
78
|
|
|
77
79
|
refreshToken: string | null | undefined;
|
|
@@ -198,6 +200,32 @@ export default class Session {
|
|
|
198
200
|
return this.profile && (this.profile.sipLines || []).find(sipLine => sipLine && !sipLine.isWebRtc());
|
|
199
201
|
}
|
|
200
202
|
|
|
203
|
+
getContact(): Contact {
|
|
204
|
+
if (this.contact) {
|
|
205
|
+
return this.contact;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const { email, firstName, lastName, mobileNumber, state, status } = this.profile || {};
|
|
209
|
+
const number = this.primaryNumber() || '';
|
|
210
|
+
const numbers = [{ label: 'primary', number }];
|
|
211
|
+
|
|
212
|
+
if (mobileNumber) {
|
|
213
|
+
numbers.push({ label: 'mobile', number: mobileNumber });
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return new Contact({
|
|
217
|
+
uuid: this.uuid || '',
|
|
218
|
+
email,
|
|
219
|
+
name: `${firstName} ${lastName}`,
|
|
220
|
+
number,
|
|
221
|
+
numbers,
|
|
222
|
+
state: state || '',
|
|
223
|
+
status,
|
|
224
|
+
backend: 'wazo',
|
|
225
|
+
source: 'default',
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
|
|
201
229
|
primaryContext(): string {
|
|
202
230
|
if (this.engineVersion) {
|
|
203
231
|
if (this.hasEngineVersionGte(MINIMUM_WAZO_ENGINE_VERSION_FOR_DEFAULT_CONTEXT)) {
|
|
@@ -1,5 +1,138 @@
|
|
|
1
1
|
import Contact from '../Contact';
|
|
2
2
|
|
|
3
|
+
const genericContactResponse = {
|
|
4
|
+
column_types: [
|
|
5
|
+
'name',
|
|
6
|
+
'number',
|
|
7
|
+
'callable',
|
|
8
|
+
'voicemail',
|
|
9
|
+
'favorite',
|
|
10
|
+
'email',
|
|
11
|
+
'personal',
|
|
12
|
+
],
|
|
13
|
+
term: 'a',
|
|
14
|
+
column_headers: [
|
|
15
|
+
'Nom',
|
|
16
|
+
'Num\u00e9ro',
|
|
17
|
+
'Mobile',
|
|
18
|
+
'Bo\u00eete vocale',
|
|
19
|
+
'Favoris',
|
|
20
|
+
'E-mail',
|
|
21
|
+
'Personnel',
|
|
22
|
+
],
|
|
23
|
+
results: [],
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const contact1 = {
|
|
27
|
+
source: 'internal',
|
|
28
|
+
column_values: [
|
|
29
|
+
'Jonathan Lessard',
|
|
30
|
+
'8020',
|
|
31
|
+
'06800880',
|
|
32
|
+
null,
|
|
33
|
+
false,
|
|
34
|
+
'jonathan@example.com',
|
|
35
|
+
false,
|
|
36
|
+
],
|
|
37
|
+
backend: 'wazo',
|
|
38
|
+
relations: {
|
|
39
|
+
user_id: 8,
|
|
40
|
+
xivo_id: '6cd695d2-cdb9-4444-8b2d-27425ab85fa8',
|
|
41
|
+
agent_id: null,
|
|
42
|
+
endpoint_id: 8,
|
|
43
|
+
user_uuid: 'a14dd6d6-547c-434d-bd5c-e882b5b83b54',
|
|
44
|
+
source_entry_id: '8',
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const contact2 = {
|
|
49
|
+
source: 'internal',
|
|
50
|
+
column_values: [
|
|
51
|
+
'John Doe',
|
|
52
|
+
'8021',
|
|
53
|
+
'06800881',
|
|
54
|
+
null,
|
|
55
|
+
false,
|
|
56
|
+
'john@example.com',
|
|
57
|
+
false,
|
|
58
|
+
],
|
|
59
|
+
backend: 'wazo',
|
|
60
|
+
relations: {
|
|
61
|
+
user_id: 9,
|
|
62
|
+
xivo_id: '6cd695d2-cdb9-4444-8b2d-27425ab85fa9',
|
|
63
|
+
agent_id: null,
|
|
64
|
+
endpoint_id: 9,
|
|
65
|
+
user_uuid: 'a14dd6d6-547c-434d-bd5c-e882b5b83b55',
|
|
66
|
+
source_entry_id: '9',
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const parsedContact1 = new Contact({
|
|
71
|
+
name: 'Jonathan Lessard',
|
|
72
|
+
number: '8020',
|
|
73
|
+
numbers: [
|
|
74
|
+
{
|
|
75
|
+
label: 'primary',
|
|
76
|
+
number: '8020',
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
label: 'secondary',
|
|
80
|
+
number: '06800880',
|
|
81
|
+
},
|
|
82
|
+
],
|
|
83
|
+
favorited: false,
|
|
84
|
+
email: 'jonathan@example.com',
|
|
85
|
+
emails: [
|
|
86
|
+
{
|
|
87
|
+
label: 'primary',
|
|
88
|
+
email: 'jonathan@example.com',
|
|
89
|
+
},
|
|
90
|
+
],
|
|
91
|
+
address: '',
|
|
92
|
+
entreprise: '',
|
|
93
|
+
birthday: '',
|
|
94
|
+
note: '',
|
|
95
|
+
endpointId: 8,
|
|
96
|
+
personal: false,
|
|
97
|
+
source: 'internal',
|
|
98
|
+
sourceId: '8',
|
|
99
|
+
uuid: 'a14dd6d6-547c-434d-bd5c-e882b5b83b54',
|
|
100
|
+
backend: 'wazo',
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
const parsedContact2 = new Contact({
|
|
104
|
+
name: 'John Doe',
|
|
105
|
+
number: '8021',
|
|
106
|
+
numbers: [
|
|
107
|
+
{
|
|
108
|
+
label: 'primary',
|
|
109
|
+
number: '8021',
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
label: 'secondary',
|
|
113
|
+
number: '06800881',
|
|
114
|
+
},
|
|
115
|
+
],
|
|
116
|
+
favorited: false,
|
|
117
|
+
email: 'john@example.com',
|
|
118
|
+
emails: [
|
|
119
|
+
{
|
|
120
|
+
label: 'primary',
|
|
121
|
+
email: 'john@example.com',
|
|
122
|
+
},
|
|
123
|
+
],
|
|
124
|
+
address: '',
|
|
125
|
+
entreprise: '',
|
|
126
|
+
birthday: '',
|
|
127
|
+
note: '',
|
|
128
|
+
endpointId: 9,
|
|
129
|
+
personal: false,
|
|
130
|
+
source: 'internal',
|
|
131
|
+
sourceId: '9',
|
|
132
|
+
uuid: 'a14dd6d6-547c-434d-bd5c-e882b5b83b55',
|
|
133
|
+
backend: 'wazo',
|
|
134
|
+
});
|
|
135
|
+
|
|
3
136
|
describe('Contact domain', () => {
|
|
4
137
|
it('is extern when given no uuid', () => {
|
|
5
138
|
const boba = new Contact({});
|
|
@@ -132,10 +265,7 @@ describe('Contact domain', () => {
|
|
|
132
265
|
const contact = new Contact({
|
|
133
266
|
name: 'John Smith',
|
|
134
267
|
});
|
|
135
|
-
const {
|
|
136
|
-
firstName,
|
|
137
|
-
lastName,
|
|
138
|
-
} = contact.separateName();
|
|
268
|
+
const { firstName, lastName } = contact.separateName();
|
|
139
269
|
expect(firstName).toBe('John');
|
|
140
270
|
expect(lastName).toBe('Smith');
|
|
141
271
|
});
|
|
@@ -143,10 +273,7 @@ describe('Contact domain', () => {
|
|
|
143
273
|
const contact = new Contact({
|
|
144
274
|
name: 'John Smith',
|
|
145
275
|
});
|
|
146
|
-
const {
|
|
147
|
-
firstName,
|
|
148
|
-
lastName,
|
|
149
|
-
} = contact.separateName();
|
|
276
|
+
const { firstName, lastName } = contact.separateName();
|
|
150
277
|
expect(firstName).toBe('John');
|
|
151
278
|
expect(lastName).toBe('Smith');
|
|
152
279
|
});
|
|
@@ -172,67 +299,99 @@ describe('Contact domain', () => {
|
|
|
172
299
|
});
|
|
173
300
|
it('can parse a plain contact to domain', () => {
|
|
174
301
|
const response = {
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
column_headers: ['Nom', 'Num\u00e9ro', 'Mobile', 'Bo\u00eete vocale', 'Favoris', 'E-mail', 'Personnel'],
|
|
178
|
-
results: [{
|
|
179
|
-
source: 'internal',
|
|
180
|
-
column_values: ['Jonathan Lessard', '8020', '06800880', null, false, 'contact@nexapp.ca', false],
|
|
181
|
-
backend: 'wazo',
|
|
182
|
-
relations: {
|
|
183
|
-
user_id: 8,
|
|
184
|
-
xivo_id: '6cd695d2-cdb9-4444-8b2d-27425ab85fa8',
|
|
185
|
-
agent_id: null,
|
|
186
|
-
endpoint_id: 8,
|
|
187
|
-
user_uuid: 'a14dd6d6-547c-434d-bd5c-e882b5b83b54',
|
|
188
|
-
source_entry_id: '8',
|
|
189
|
-
},
|
|
190
|
-
}],
|
|
302
|
+
...genericContactResponse,
|
|
303
|
+
results: [contact1],
|
|
191
304
|
};
|
|
192
305
|
const contact = Contact.parse(response.results[0], response.column_types);
|
|
193
|
-
expect(contact).toEqual(
|
|
194
|
-
name: 'Jonathan Lessard',
|
|
195
|
-
number: '8020',
|
|
196
|
-
numbers: [{
|
|
197
|
-
label: 'primary',
|
|
198
|
-
number: '8020',
|
|
199
|
-
}, {
|
|
200
|
-
label: 'secondary',
|
|
201
|
-
number: '06800880',
|
|
202
|
-
}],
|
|
203
|
-
favorited: false,
|
|
204
|
-
email: 'contact@nexapp.ca',
|
|
205
|
-
emails: [{
|
|
206
|
-
label: 'primary',
|
|
207
|
-
email: 'contact@nexapp.ca',
|
|
208
|
-
}],
|
|
209
|
-
address: '',
|
|
210
|
-
entreprise: '',
|
|
211
|
-
birthday: '',
|
|
212
|
-
note: '',
|
|
213
|
-
endpointId: 8,
|
|
214
|
-
personal: false,
|
|
215
|
-
source: 'internal',
|
|
216
|
-
sourceId: '8',
|
|
217
|
-
uuid: 'a14dd6d6-547c-434d-bd5c-e882b5b83b54',
|
|
218
|
-
backend: 'wazo',
|
|
219
|
-
}));
|
|
306
|
+
expect(contact).toEqual(parsedContact1);
|
|
220
307
|
});
|
|
221
308
|
it('should merge 2 contact', () => {
|
|
222
|
-
const
|
|
309
|
+
const noBirthdayContact = new Contact({
|
|
223
310
|
uuid: 'uuid-12345',
|
|
224
311
|
lastActivity: 'yesterday',
|
|
225
312
|
// @ts-expect-error
|
|
226
313
|
birthday: null,
|
|
227
314
|
});
|
|
228
|
-
const
|
|
315
|
+
const noLastActivityContact = new Contact({
|
|
229
316
|
uuid: 'uuid-12345',
|
|
230
317
|
// @ts-expect-error
|
|
231
318
|
lastActivity: null,
|
|
232
319
|
birthday: 'tomorrow',
|
|
233
320
|
});
|
|
234
|
-
const result =
|
|
321
|
+
const result = noBirthdayContact.merge(noLastActivityContact);
|
|
235
322
|
expect(result.lastActivity).toBe('yesterday');
|
|
236
323
|
expect(result.birthday).toBe('tomorrow');
|
|
237
324
|
});
|
|
325
|
+
it('returns all contacts when parsing many contacts with no limit', () => {
|
|
326
|
+
const response = {
|
|
327
|
+
...genericContactResponse,
|
|
328
|
+
results: [contact1, contact2],
|
|
329
|
+
};
|
|
330
|
+
|
|
331
|
+
const contacts = Contact.parseMany(response);
|
|
332
|
+
expect(contacts).toEqual([parsedContact1, parsedContact2]);
|
|
333
|
+
});
|
|
334
|
+
it('returns all contacts when parsing many contacts with a higher limit than number of results', () => {
|
|
335
|
+
const response = {
|
|
336
|
+
...genericContactResponse,
|
|
337
|
+
results: [contact1, contact2],
|
|
338
|
+
};
|
|
339
|
+
|
|
340
|
+
const contacts = Contact.parseMany(response, 0, 1000);
|
|
341
|
+
expect(contacts).toEqual([parsedContact1, parsedContact2]);
|
|
342
|
+
});
|
|
343
|
+
it('returns limited contacts when parsing many contacts with a smaller limit than number of results', () => {
|
|
344
|
+
const response = {
|
|
345
|
+
...genericContactResponse,
|
|
346
|
+
results: [contact1],
|
|
347
|
+
};
|
|
348
|
+
|
|
349
|
+
const contacts = Contact.parseMany(response, 0, 1);
|
|
350
|
+
expect(contacts).toEqual([parsedContact1]);
|
|
351
|
+
});
|
|
352
|
+
it('returns all contacts when parsing many contacts with same limit than number of results', () => {
|
|
353
|
+
const response = {
|
|
354
|
+
...genericContactResponse,
|
|
355
|
+
results: [contact1, contact2],
|
|
356
|
+
};
|
|
357
|
+
|
|
358
|
+
const contacts = Contact.parseMany(response, 0, 2);
|
|
359
|
+
expect(contacts).toEqual([parsedContact1, parsedContact2]);
|
|
360
|
+
});
|
|
361
|
+
it('returns an empty array when parsing many contact with limit of 0', () => {
|
|
362
|
+
const response = {
|
|
363
|
+
...genericContactResponse,
|
|
364
|
+
results: [contact1, contact2],
|
|
365
|
+
};
|
|
366
|
+
|
|
367
|
+
const contacts = Contact.parseMany(response, 0, 0);
|
|
368
|
+
expect(contacts).toEqual([]);
|
|
369
|
+
});
|
|
370
|
+
it('returns all contacts when parsing many contacts with a negative limit', () => {
|
|
371
|
+
const response = {
|
|
372
|
+
...genericContactResponse,
|
|
373
|
+
results: [contact1, contact2],
|
|
374
|
+
};
|
|
375
|
+
|
|
376
|
+
const contacts = Contact.parseMany(response, 0, -1);
|
|
377
|
+
expect(contacts).toEqual([parsedContact1, parsedContact2]);
|
|
378
|
+
});
|
|
379
|
+
it('returns all contacts when parsing many contacts with a null limit', () => {
|
|
380
|
+
const response = {
|
|
381
|
+
...genericContactResponse,
|
|
382
|
+
results: [contact1, contact2],
|
|
383
|
+
};
|
|
384
|
+
|
|
385
|
+
const contacts = Contact.parseMany(response, 0, null);
|
|
386
|
+
expect(contacts).toEqual([parsedContact1, parsedContact2]);
|
|
387
|
+
});
|
|
388
|
+
it('returns all contacts after the first one when parsing many contacts with an offset of 1', () => {
|
|
389
|
+
const response = {
|
|
390
|
+
...genericContactResponse,
|
|
391
|
+
results: [contact1, contact2],
|
|
392
|
+
};
|
|
393
|
+
|
|
394
|
+
const contacts = Contact.parseMany(response, 1, null);
|
|
395
|
+
expect(contacts).toEqual([parsedContact2]);
|
|
396
|
+
});
|
|
238
397
|
});
|