@webex/calling 0.0.1-next.6 → 0.0.1-next.8

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 CHANGED
@@ -1,52 +1,74 @@
1
- # @webex/calling
1
+ ### Table of Contents
2
+ - [Getting Started](#getting-started)
3
+ - [Developing](#developing)
4
+ - [Building](#building)
5
+ - [Testing](#testing)
6
+ - [Samples](#samples)
7
+ - [Consuming SDK](#consuming-sdk)
8
+ - [NPM](#npm)
9
+ - [CDN](#cdn)
10
+ - [Kitchen Sink App](#kitchen-sink-app)
11
+ ---
2
12
 
13
+ ## Getting Started
3
14
  With the Webex Calling SDK, you can effortlessly integrate fundamental audio calling capabilities into your solutions, enhancing the way your users connect.
4
15
 
5
- > [Introduction to the Webex Web Calling SDK](https://github.com/webex/webex-js-sdk/wiki/Introducing-the-Webex-Web-Calling-SDK)
6
- > [Quickstart guide](https://github.com/webex/webex-js-sdk/wiki/Quickstart-Guide-(Calling))
7
- > API guide - TBD
8
-
16
+ - [Introduction to the Webex Web Calling SDK](https://github.com/webex/webex-js-sdk/wiki/Introducing-the-Webex-Web-Calling-SDK)
17
+ - [Quickstart guide](https://github.com/webex/webex-js-sdk/wiki/Quickstart-Guide-(Calling)).
9
18
 
10
19
  ## Developing
11
20
 
12
- ```shell
21
+ ```bash
13
22
  git clone https://github.com/\<your-fork\>/webex-js-sdk.git
14
23
  cd web-js-sdk/
15
24
  yarn install
16
25
  ```
17
26
 
18
- ### Building
27
+ ## Building
19
28
 
20
29
  If your project needs some additional steps for the developer to build the
21
30
  project after some code changes, state them here:
22
31
 
23
- ```shell
32
+ ```bash
24
33
  yarn workspaces foreach --parallel --verbose run build:src
25
34
 
26
35
  yarn build:local
27
36
  ```
28
37
 
29
- ### Testing
38
+ ## Testing
30
39
 
31
- ```shell
40
+ ```bash
32
41
  yarn workspace @webex/calling run test
33
42
  ```
34
43
 
35
- ### Testing on samples
36
- ```shell
37
- yarn run samples:serve
44
+ ## Samples
45
+ ```bash
46
+ yarn run samples:serve
38
47
  ```
39
48
 
40
- ### Consuming the SDK
41
-
42
- The Calling package can be incorporated into an existing project by updating the package.json. Add the line pasted below to get access to the calling-sdk package located in the artifactory.
49
+ ## Consuming SDK
50
+ To consume the latest stable version of the Calling SDK one can use NPM or CDN.
51
+ # NPM
52
+ ```javascript
53
+ npm install @webex/calling
54
+ ```
55
+ (or)
43
56
 
57
+ ```javascript
58
+ yarn add @webex/calling
59
+ ```
44
60
 
45
- Use the following commands to update the package.json and package.lock/yarn.lock with the latest version of the calling-sdk package.
46
- ```shell
47
- npm install @webex/web-calling-sdk
61
+ ```javascript
62
+ import Calling from '@webex/calling'
48
63
  ```
49
- (or)
50
- ```shell
51
- yarn add @webex/web-calling-sdk
52
- ```
64
+ # CDN
65
+ ```javascript
66
+ <script src="../calling.min.js"></script>
67
+ ```
68
+
69
+ ### Kitchen Sink App
70
+ To test Calling SDK API, use this Kitchen Sink app: https://webex.github.io/webex-js-sdk/samples/calling/
71
+
72
+
73
+
74
+
@@ -4,7 +4,7 @@ import log from '../Logger';
4
4
  import { serviceErrorCodeHandler } from '../common/Utils';
5
5
  import { CALL_HISTORY_FILE, FROM_DATE, HISTORY, LIMIT, NUMBER_OF_DAYS } from './constants';
6
6
  import { STATUS_CODE, SUCCESS_MESSAGE, USER_SESSIONS } from '../common/constants';
7
- import { EVENT_KEYS, MOBIUS_EVENT_KEYS, } from '../Events/types';
7
+ import { COMMON_EVENT_KEYS, MOBIUS_EVENT_KEYS, } from '../Events/types';
8
8
  import { Eventing } from '../Events/impl';
9
9
  export class CallHistory extends Eventing {
10
10
  sdkConnector;
@@ -70,13 +70,10 @@ export class CallHistory extends Eventing {
70
70
  return errorStatus;
71
71
  }
72
72
  }
73
- getSDKConnector() {
74
- return this.sdkConnector;
75
- }
76
73
  registerSessionsListener() {
77
74
  this.sdkConnector.registerListener(MOBIUS_EVENT_KEYS.CALL_SESSION_EVENT_INCLUSIVE, async (event) => {
78
75
  if (event && event.data.userSessions.userSessions) {
79
- this.emit(EVENT_KEYS.CALL_HISTORY_USER_SESSION_INFO, event);
76
+ this.emit(COMMON_EVENT_KEYS.CALL_HISTORY_USER_SESSION_INFO, event);
80
77
  }
81
78
  });
82
79
  }
@@ -1,16 +1,14 @@
1
1
  import * as Media from '@webex/internal-media-core';
2
2
  import { Mutex } from 'async-mutex';
3
3
  import { filterMobiusUris, handleCallingClientErrors, validateServiceData } from '../common/Utils';
4
- import { ERROR_TYPE } from '../Errors/types';
5
4
  import { LOGGER } from '../Logger/types';
6
5
  import SDKConnector from '../SDKConnector';
7
6
  import { Eventing } from '../Events/impl';
8
- import { EVENT_KEYS, MOBIUS_EVENT_KEYS, SessionType, } from '../Events/types';
9
- import { MobiusStatus, CallDirection, ServiceIndicator, ALLOWED_SERVICES, HTTP_METHODS, } from '../common/types';
7
+ import { MOBIUS_EVENT_KEYS, SessionType, CALLING_CLIENT_EVENT_KEYS, } from '../Events/types';
8
+ import { ServiceIndicator, ALLOWED_SERVICES, HTTP_METHODS, } from '../common/types';
10
9
  import log from '../Logger';
11
10
  import { getCallManager } from './calling/callManager';
12
- import { CALLING_CLIENT_FILE, VALID_PHONE, CALLS_CLEARED_HANDLER_UTIL, CALLING_USER_AGENT, CISCO_DEVICE_URL, DISCOVERY_URL, GET_MOBIUS_SERVERS_UTIL, IP_ENDPOINT, SPARK_USER_AGENT, URL_ENDPOINT, NETWORK_FLAP_TIMEOUT, } from './constants';
13
- import { CallingClientError } from '../Errors';
11
+ import { CALLING_CLIENT_FILE, CALLS_CLEARED_HANDLER_UTIL, CALLING_USER_AGENT, CISCO_DEVICE_URL, DISCOVERY_URL, GET_MOBIUS_SERVERS_UTIL, IP_ENDPOINT, SPARK_USER_AGENT, URL_ENDPOINT, NETWORK_FLAP_TIMEOUT, } from './constants';
14
12
  import Line from './line';
15
13
  import { LINE_EVENTS, LineStatus } from './line/types';
16
14
  import { METRIC_EVENT, REG_ACTION, METRIC_TYPE } from '../Metrics/types';
@@ -47,7 +45,6 @@ export class CallingClient extends Eventing {
47
45
  this.backupMobiusUris = [];
48
46
  this.registerSessionsListener();
49
47
  log.setLogger(logLevel, CALLING_CLIENT_FILE);
50
- this.incomingCallListener();
51
48
  this.registerCallsClearedListener();
52
49
  }
53
50
  async init() {
@@ -55,16 +52,6 @@ export class CallingClient extends Eventing {
55
52
  await this.createLine();
56
53
  this.detectNetworkChange();
57
54
  }
58
- incomingCallListener() {
59
- const logContext = {
60
- file: CALLING_CLIENT_FILE,
61
- method: this.incomingCallListener.name,
62
- };
63
- log.log('Listening for incoming calls... ', logContext);
64
- this.callManager.on(EVENT_KEYS.INCOMING_CALL, (callObj) => {
65
- this.emit(EVENT_KEYS.INCOMING_CALL, callObj);
66
- });
67
- }
68
55
  async detectNetworkChange() {
69
56
  let retry = false;
70
57
  const line = Object.values(this.lineDict)[0];
@@ -113,7 +100,7 @@ export class CallingClient extends Eventing {
113
100
  catch (err) {
114
101
  handleCallingClientErrors(err, (clientError) => {
115
102
  this.metricManager.submitRegistrationMetric(METRIC_EVENT.REGISTRATION_ERROR, REG_ACTION.REGISTER, METRIC_TYPE.BEHAVIORAL, clientError);
116
- this.emit(EVENT_KEYS.ERROR, clientError);
103
+ this.emit(CALLING_CLIENT_EVENT_KEYS.ERROR, clientError);
117
104
  }, { method: GET_MOBIUS_SERVERS_UTIL, file: CALLING_CLIENT_FILE });
118
105
  regionInfo.clientRegion = '';
119
106
  regionInfo.countryCode = '';
@@ -163,7 +150,7 @@ export class CallingClient extends Eventing {
163
150
  catch (err) {
164
151
  handleCallingClientErrors(err, (clientError) => {
165
152
  this.metricManager.submitRegistrationMetric(METRIC_EVENT.REGISTRATION_ERROR, REG_ACTION.REGISTER, METRIC_TYPE.BEHAVIORAL, clientError);
166
- this.emit(EVENT_KEYS.ERROR, clientError);
153
+ this.emit(CALLING_CLIENT_EVENT_KEYS.ERROR, clientError);
167
154
  }, { method: GET_MOBIUS_SERVERS_UTIL, file: CALLING_CLIENT_FILE });
168
155
  useDefault = true;
169
156
  }
@@ -184,7 +171,7 @@ export class CallingClient extends Eventing {
184
171
  method: this.registerCallsClearedListener.name,
185
172
  };
186
173
  log.log('Registering listener for all calls cleared event', logContext);
187
- this.callManager.on(EVENT_KEYS.ALL_CALLS_CLEARED, this.callsClearedHandler);
174
+ this.callManager.on(CALLING_CLIENT_EVENT_KEYS.ALL_CALLS_CLEARED, this.callsClearedHandler);
188
175
  }
189
176
  callsClearedHandler = async () => {
190
177
  const { registration } = Object.values(this.lineDict)[0];
@@ -203,35 +190,6 @@ export class CallingClient extends Eventing {
203
190
  getLoggingLevel() {
204
191
  return log.getLogLevel();
205
192
  }
206
- getCall = (correlationId) => {
207
- return this.callManager.getCall(correlationId);
208
- };
209
- makeCall = (dest) => {
210
- let call;
211
- const { registration } = Object.values(this.lineDict)[0];
212
- if (dest) {
213
- const match = dest.address.match(VALID_PHONE);
214
- if (match && match[0].length === dest.address.length) {
215
- const sanitizedNumber = dest.address
216
- .replace(/[^[*+]\d#]/gi, '')
217
- .replace(/\s+/gi, '')
218
- .replace(/-/gi, '');
219
- const formattedDest = {
220
- type: dest.type,
221
- address: `tel:${sanitizedNumber}`,
222
- };
223
- call = this.callManager.createCall(formattedDest, CallDirection.OUTBOUND, registration.getDeviceInfo().device?.deviceId);
224
- log.log(`New call created, callId: ${call.getCallId()}`, {});
225
- }
226
- else {
227
- log.warn('Invalid phone number detected', {});
228
- const err = new CallingClientError('An invalid phone number was detected. Check the number and try again.', {}, ERROR_TYPE.CALL_ERROR, MobiusStatus.ACTIVE);
229
- this.emit(EVENT_KEYS.ERROR, err);
230
- }
231
- return call;
232
- }
233
- return undefined;
234
- };
235
193
  getSDKConnector() {
236
194
  return this.sdkConnector;
237
195
  }
@@ -249,7 +207,7 @@ export class CallingClient extends Eventing {
249
207
  sessionArr.splice(i, 1);
250
208
  }
251
209
  }
252
- this.emit(EVENT_KEYS.USER_SESSION_INFO, event);
210
+ this.emit(CALLING_CLIENT_EVENT_KEYS.USER_SESSION_INFO, event);
253
211
  }
254
212
  });
255
213
  }
@@ -260,6 +218,28 @@ export class CallingClient extends Eventing {
260
218
  getLines() {
261
219
  return this.lineDict;
262
220
  }
221
+ getActiveCalls() {
222
+ const activeCalls = {};
223
+ const calls = this.callManager.getActiveCalls();
224
+ Object.keys(calls).forEach((correlationId) => {
225
+ const call = calls[correlationId];
226
+ if (!activeCalls[call.lineId]) {
227
+ activeCalls[call.lineId] = [];
228
+ }
229
+ activeCalls[call.lineId].push(call);
230
+ });
231
+ return activeCalls;
232
+ }
233
+ getConnectedCall() {
234
+ let connectCall;
235
+ const calls = this.callManager.getActiveCalls();
236
+ Object.keys(calls).forEach((correlationId) => {
237
+ if (calls[correlationId].isConnected() && !calls[correlationId].isHeld()) {
238
+ connectCall = calls[correlationId];
239
+ }
240
+ });
241
+ return connectCall;
242
+ }
263
243
  }
264
244
  export const createClient = async (webex, config) => {
265
245
  const callingClientInstance = new CallingClient(webex, config);
@@ -8,7 +8,7 @@ import { createCallError } from '../../Errors/catalog/CallError';
8
8
  import { CALL_ENDPOINT_RESOURCE, CALL_FILE, CALL_HOLD_SERVICE, CALL_STATUS_RESOURCE, CALL_TRANSFER_SERVICE, CALLING_USER_AGENT, CALLS_ENDPOINT_RESOURCE, CISCO_DEVICE_URL, DEFAULT_LOCAL_CALL_ID, DEFAULT_SESSION_TIMER, DEVICES_ENDPOINT_RESOURCE, HOLD_ENDPOINT, INITIAL_SEQ_NUMBER, MEDIA_ENDPOINT_RESOURCE, RESUME_ENDPOINT, SPARK_USER_AGENT, SUPPLEMENTARY_SERVICES_TIMEOUT, TRANSFER_ENDPOINT, } from '../constants';
9
9
  import SDKConnector from '../../SDKConnector';
10
10
  import { Eventing } from '../../Events/impl';
11
- import { EVENT_KEYS, MEDIA_CONNECTION_EVENT_KEYS, MOBIUS_MIDCALL_STATE, SUPPLEMENTARY_SERVICES, } from '../../Events/types';
11
+ import { CALL_EVENT_KEYS, MEDIA_CONNECTION_EVENT_KEYS, MOBIUS_MIDCALL_STATE, SUPPLEMENTARY_SERVICES, } from '../../Events/types';
12
12
  import { DisconnectCause, DisconnectCode, MidCallEventType, MobiusCallState, RoapScenario, TransferType, } from './types';
13
13
  import log from '../../Logger';
14
14
  import { createCallerId } from './CallerId';
@@ -23,6 +23,7 @@ export class Call extends Eventing {
23
23
  callId;
24
24
  correlationId;
25
25
  deviceId;
26
+ lineId;
26
27
  disconnectReason;
27
28
  callStateMachine;
28
29
  mediaStateMachine;
@@ -54,13 +55,14 @@ export class Call extends Eventing {
54
55
  isHeld() {
55
56
  return this.held;
56
57
  }
57
- constructor(activeUrl, webex, destination, direction, deviceId, deleteCb, indicator) {
58
+ constructor(activeUrl, webex, destination, direction, deviceId, lineId, deleteCb, indicator) {
58
59
  super();
59
60
  this.destination = destination;
60
61
  this.direction = direction;
61
62
  this.sdkConnector = SDKConnector;
62
63
  this.deviceId = deviceId;
63
64
  this.serviceIndicator = indicator;
65
+ this.lineId = lineId;
64
66
  if (!this.sdkConnector.getWebex()) {
65
67
  SDKConnector.setWebex(webex);
66
68
  }
@@ -88,7 +90,7 @@ export class Call extends Eventing {
88
90
  correlationId: this.correlationId,
89
91
  callerId: this.callerInfo,
90
92
  };
91
- this.emit(EVENT_KEYS.CALLER_ID, emitObj);
93
+ this.emit(CALL_EVENT_KEYS.CALLER_ID, emitObj);
92
94
  });
93
95
  this.remoteRoapMessage = null;
94
96
  this.disconnectReason = { code: DisconnectCode.NORMAL, cause: DisconnectCause.NORMAL };
@@ -581,7 +583,7 @@ export class Call extends Eventing {
581
583
  });
582
584
  const errData = e;
583
585
  handleCallErrors((error) => {
584
- this.emit(EVENT_KEYS.CALL_ERROR, error);
586
+ this.emit(CALL_EVENT_KEYS.CALL_ERROR, error);
585
587
  this.submitCallErrorMetric(error);
586
588
  this.sendCallStateMachineEvt({ type: 'E_UNKNOWN', data: errData });
587
589
  }, ERROR_LAYER.CALL_CONTROL, (interval) => undefined, this.getCorrelationId(), errData, this.handleOutgoingCallSetup.name, CALL_FILE);
@@ -606,7 +608,7 @@ export class Call extends Eventing {
606
608
  method: this.handleCallHold.name,
607
609
  });
608
610
  const callError = createCallError('An error occurred while placing the call on hold. Wait a moment and try again.', errorContext, ERROR_TYPE.TIMEOUT, this.getCorrelationId(), ERROR_LAYER.CALL_CONTROL);
609
- this.emit(EVENT_KEYS.HOLD_ERROR, callError);
611
+ this.emit(CALL_EVENT_KEYS.HOLD_ERROR, callError);
610
612
  this.submitCallErrorMetric(callError);
611
613
  }, SUPPLEMENTARY_SERVICES_TIMEOUT);
612
614
  }
@@ -618,7 +620,7 @@ export class Call extends Eventing {
618
620
  });
619
621
  const errData = e;
620
622
  handleCallErrors((error) => {
621
- this.emit(EVENT_KEYS.HOLD_ERROR, error);
623
+ this.emit(CALL_EVENT_KEYS.HOLD_ERROR, error);
622
624
  this.submitCallErrorMetric(error);
623
625
  this.sendCallStateMachineEvt({ type: 'E_CALL_ESTABLISHED', data: errData });
624
626
  }, ERROR_LAYER.CALL_CONTROL, (interval) => undefined, this.getCorrelationId(), errData, this.handleOutgoingCallSetup.name, CALL_FILE);
@@ -643,7 +645,7 @@ export class Call extends Eventing {
643
645
  method: this.handleCallResume.name,
644
646
  });
645
647
  const callError = createCallError('An error occurred while resuming the call. Wait a moment and try again.', errorContext, ERROR_TYPE.TIMEOUT, this.getCorrelationId(), ERROR_LAYER.CALL_CONTROL);
646
- this.emit(EVENT_KEYS.RESUME_ERROR, callError);
648
+ this.emit(CALL_EVENT_KEYS.RESUME_ERROR, callError);
647
649
  this.submitCallErrorMetric(callError);
648
650
  }, SUPPLEMENTARY_SERVICES_TIMEOUT);
649
651
  }
@@ -655,7 +657,7 @@ export class Call extends Eventing {
655
657
  });
656
658
  const errData = e;
657
659
  handleCallErrors((error) => {
658
- this.emit(EVENT_KEYS.RESUME_ERROR, error);
660
+ this.emit(CALL_EVENT_KEYS.RESUME_ERROR, error);
659
661
  this.submitCallErrorMetric(error);
660
662
  this.sendCallStateMachineEvt({ type: 'E_CALL_ESTABLISHED', data: errData });
661
663
  }, ERROR_LAYER.CALL_CONTROL, (interval) => undefined, this.getCorrelationId(), errData, this.handleOutgoingCallSetup.name, CALL_FILE);
@@ -687,7 +689,7 @@ export class Call extends Eventing {
687
689
  });
688
690
  this.startCallerIdResolution(data.callerId);
689
691
  }
690
- this.emit(EVENT_KEYS.PROGRESS, this.correlationId);
692
+ this.emit(CALL_EVENT_KEYS.PROGRESS, this.correlationId);
691
693
  }
692
694
  handleIncomingRoapOfferRequest(context, event) {
693
695
  log.info(`handleIncomingRoapOfferRequest: ${this.getCorrelationId()} `, {
@@ -739,7 +741,7 @@ export class Call extends Eventing {
739
741
  });
740
742
  const errData = err;
741
743
  handleCallErrors((error) => {
742
- this.emit(EVENT_KEYS.CALL_ERROR, error);
744
+ this.emit(CALL_EVENT_KEYS.CALL_ERROR, error);
743
745
  this.submitCallErrorMetric(error);
744
746
  this.sendCallStateMachineEvt({ type: 'E_UNKNOWN', data: errData });
745
747
  }, ERROR_LAYER.CALL_CONTROL, (interval) => undefined, this.getCorrelationId(), errData, this.handleOutgoingCallAlerting.name, CALL_FILE);
@@ -750,7 +752,7 @@ export class Call extends Eventing {
750
752
  file: CALL_FILE,
751
753
  method: this.handleIncomingCallConnect.name,
752
754
  });
753
- this.emit(EVENT_KEYS.CONNECT, this.correlationId);
755
+ this.emit(CALL_EVENT_KEYS.CONNECT, this.correlationId);
754
756
  if (this.earlyMedia || this.mediaNegotiationCompleted) {
755
757
  this.mediaNegotiationCompleted = false;
756
758
  this.sendCallStateMachineEvt({ type: 'E_CALL_ESTABLISHED' });
@@ -783,7 +785,7 @@ export class Call extends Eventing {
783
785
  });
784
786
  const errData = err;
785
787
  handleCallErrors((error) => {
786
- this.emit(EVENT_KEYS.CALL_ERROR, error);
788
+ this.emit(CALL_EVENT_KEYS.CALL_ERROR, error);
787
789
  this.submitCallErrorMetric(error);
788
790
  this.sendCallStateMachineEvt({ type: 'E_UNKNOWN', data: errData });
789
791
  }, ERROR_LAYER.CALL_CONTROL, (interval) => undefined, this.getCorrelationId(), errData, this.handleOutgoingCallConnect.name, CALL_FILE);
@@ -818,7 +820,7 @@ export class Call extends Eventing {
818
820
  }
819
821
  this.sendMediaStateMachineEvt({ type: 'E_ROAP_TEARDOWN' });
820
822
  this.sendCallStateMachineEvt({ type: 'E_CALL_CLEARED' });
821
- this.emit(EVENT_KEYS.DISCONNECT, this.correlationId);
823
+ this.emit(CALL_EVENT_KEYS.DISCONNECT, this.correlationId);
822
824
  }
823
825
  async handleOutgoingCallDisconnect(event) {
824
826
  this.setDisconnectReason();
@@ -851,7 +853,7 @@ export class Call extends Eventing {
851
853
  file: CALL_FILE,
852
854
  method: this.handleCallEstablished.name,
853
855
  });
854
- this.emit(EVENT_KEYS.ESTABLISHED, this.correlationId);
856
+ this.emit(CALL_EVENT_KEYS.ESTABLISHED, this.correlationId);
855
857
  this.earlyMedia = false;
856
858
  this.connected = true;
857
859
  if (this.sessionTimer) {
@@ -875,7 +877,7 @@ export class Call extends Eventing {
875
877
  clearInterval(this.sessionTimer);
876
878
  }
877
879
  handleCallErrors((callError) => {
878
- this.emit(EVENT_KEYS.CALL_ERROR, callError);
880
+ this.emit(CALL_EVENT_KEYS.CALL_ERROR, callError);
879
881
  this.submitCallErrorMetric(callError);
880
882
  }, ERROR_LAYER.CALL_CONTROL, (interval) => {
881
883
  setTimeout(() => {
@@ -930,7 +932,7 @@ export class Call extends Eventing {
930
932
  return (error) => {
931
933
  switch (this.callStateMachine.state.value) {
932
934
  case 'S_CALL_HOLD':
933
- this.emit(EVENT_KEYS.HOLD_ERROR, error);
935
+ this.emit(CALL_EVENT_KEYS.HOLD_ERROR, error);
934
936
  if (this.supplementaryServicesTimer) {
935
937
  clearTimeout(this.supplementaryServicesTimer);
936
938
  this.supplementaryServicesTimer = undefined;
@@ -939,12 +941,12 @@ export class Call extends Eventing {
939
941
  this.sendCallStateMachineEvt({ type: 'E_CALL_ESTABLISHED', data: errData });
940
942
  return;
941
943
  case 'S_CALL_RESUME':
942
- this.emit(EVENT_KEYS.RESUME_ERROR, error);
944
+ this.emit(CALL_EVENT_KEYS.RESUME_ERROR, error);
943
945
  this.submitCallErrorMetric(error);
944
946
  this.sendCallStateMachineEvt({ type: 'E_CALL_ESTABLISHED', data: errData });
945
947
  return;
946
948
  default:
947
- this.emit(EVENT_KEYS.CALL_ERROR, error);
949
+ this.emit(CALL_EVENT_KEYS.CALL_ERROR, error);
948
950
  this.submitCallErrorMetric(error);
949
951
  if (!this.connected) {
950
952
  this.sendMediaStateMachineEvt({ type: 'E_ROAP_ERROR', data: errData });
@@ -1043,7 +1045,7 @@ export class Call extends Eventing {
1043
1045
  });
1044
1046
  const errData = err;
1045
1047
  handleCallErrors((error) => {
1046
- this.emit(EVENT_KEYS.CALL_ERROR, error);
1048
+ this.emit(CALL_EVENT_KEYS.CALL_ERROR, error);
1047
1049
  this.submitCallErrorMetric(error);
1048
1050
  }, ERROR_LAYER.MEDIA, (interval) => undefined, this.getCorrelationId(), errData, this.handleRoapError.name, CALL_FILE);
1049
1051
  }
@@ -1382,7 +1384,7 @@ export class Call extends Eventing {
1382
1384
  });
1383
1385
  const errData = e;
1384
1386
  handleCallErrors((error) => {
1385
- this.emit(EVENT_KEYS.TRANSFER_ERROR, error);
1387
+ this.emit(CALL_EVENT_KEYS.TRANSFER_ERROR, error);
1386
1388
  this.submitCallErrorMetric(error, TRANSFER_ACTION.BLIND);
1387
1389
  }, ERROR_LAYER.CALL_CONTROL, (interval) => undefined, this.getCorrelationId(), errData, this.completeTransfer.name, CALL_FILE);
1388
1390
  }
@@ -1407,7 +1409,7 @@ export class Call extends Eventing {
1407
1409
  });
1408
1410
  const errData = e;
1409
1411
  handleCallErrors((error) => {
1410
- this.emit(EVENT_KEYS.TRANSFER_ERROR, error);
1412
+ this.emit(CALL_EVENT_KEYS.TRANSFER_ERROR, error);
1411
1413
  this.submitCallErrorMetric(error, TRANSFER_ACTION.CONSULT);
1412
1414
  }, ERROR_LAYER.CALL_CONTROL, (interval) => undefined, this.getCorrelationId(), errData, this.completeTransfer.name, CALL_FILE);
1413
1415
  }
@@ -1500,7 +1502,7 @@ export class Call extends Eventing {
1500
1502
  mediaTrackListener() {
1501
1503
  this.mediaConnection.on(Event.REMOTE_TRACK_ADDED, (e) => {
1502
1504
  if (e.type === MEDIA_CONNECTION_EVENT_KEYS.MEDIA_TYPE_AUDIO) {
1503
- this.emit(EVENT_KEYS.REMOTE_MEDIA, e.track);
1505
+ this.emit(CALL_EVENT_KEYS.REMOTE_MEDIA, e.track);
1504
1506
  }
1505
1507
  });
1506
1508
  }
@@ -1558,7 +1560,7 @@ export class Call extends Eventing {
1558
1560
  file: CALL_FILE,
1559
1561
  method: 'handleMidCallEvent',
1560
1562
  });
1561
- this.emit(EVENT_KEYS.HELD, this.correlationId);
1563
+ this.emit(CALL_EVENT_KEYS.HELD, this.correlationId);
1562
1564
  this.held = true;
1563
1565
  if (this.supplementaryServicesTimer) {
1564
1566
  clearTimeout(this.supplementaryServicesTimer);
@@ -1571,7 +1573,7 @@ export class Call extends Eventing {
1571
1573
  file: CALL_FILE,
1572
1574
  method: 'handleMidCallEvent',
1573
1575
  });
1574
- this.emit(EVENT_KEYS.RESUMED, this.correlationId);
1576
+ this.emit(CALL_EVENT_KEYS.RESUMED, this.correlationId);
1575
1577
  this.held = false;
1576
1578
  if (this.supplementaryServicesTimer) {
1577
1579
  clearTimeout(this.supplementaryServicesTimer);
@@ -1646,4 +1648,4 @@ export class Call extends Eventing {
1646
1648
  return this.getCallStats();
1647
1649
  }
1648
1650
  }
1649
- export const createCall = (activeUrl, webex, dest, dir, deviceId, deleteCb, indicator) => new Call(activeUrl, webex, dest, dir, deviceId, deleteCb, indicator);
1651
+ export const createCall = (activeUrl, webex, dest, dir, deviceId, lineId, deleteCb, indicator) => new Call(activeUrl, webex, dest, dir, deviceId, lineId, deleteCb, indicator);
@@ -1,5 +1,5 @@
1
1
  import { CALL_MANAGER_FILE } from '../constants';
2
- import { EVENT_KEYS } from '../../Events/types';
2
+ import { CALLING_CLIENT_EVENT_KEYS, LINE_EVENT_KEYS } from '../../Events/types';
3
3
  import { Eventing } from '../../Events/impl';
4
4
  import SDKConnector from '../../SDKConnector';
5
5
  import { CallDirection } from '../../common/types';
@@ -13,6 +13,7 @@ export class CallManager extends Eventing {
13
13
  callCollection;
14
14
  activeMobiusUrl;
15
15
  serviceIndicator;
16
+ lineDict;
16
17
  constructor(webex, indicator) {
17
18
  super();
18
19
  this.sdkConnector = SDKConnector;
@@ -20,19 +21,20 @@ export class CallManager extends Eventing {
20
21
  if (!this.sdkConnector.getWebex()) {
21
22
  SDKConnector.setWebex(webex);
22
23
  }
24
+ this.lineDict = {};
23
25
  this.webex = this.sdkConnector.getWebex();
24
26
  this.callCollection = {};
25
27
  this.activeMobiusUrl = '';
26
28
  this.listenForWsEvents();
27
29
  }
28
- createCall = (destination, direction, deviceId) => {
30
+ createCall = (destination, direction, deviceId, lineId) => {
29
31
  log.log('Creating call object', {});
30
- const newCall = createCall(this.activeMobiusUrl, this.webex, destination, direction, deviceId, (correlationId) => {
32
+ const newCall = createCall(this.activeMobiusUrl, this.webex, destination, direction, deviceId, lineId, (correlationId) => {
31
33
  delete this.callCollection[correlationId];
32
34
  const activeCalls = Object.keys(this.getActiveCalls()).length;
33
35
  log.info(`DELETE:: Deleted corelationId: ${newCall.getCorrelationId()} from CallManager, Number of call records :- ${activeCalls}`, {});
34
36
  if (activeCalls === 0) {
35
- this.emit(EVENT_KEYS.ALL_CALLS_CLEARED);
37
+ this.emit(CALLING_CLIENT_EVENT_KEYS.ALL_CALLS_CLEARED);
36
38
  }
37
39
  }, this.serviceIndicator);
38
40
  this.callCollection[newCall.getCorrelationId()] = newCall;
@@ -75,7 +77,8 @@ export class CallManager extends Eventing {
75
77
  const newId = Object.keys(this.callCollection).find((id) => this.callCollection[id].getCallId() === callId);
76
78
  let newCall;
77
79
  if (!newId) {
78
- newCall = this.createCall({}, CallDirection.INBOUND, mobiusEvent.data.deviceId);
80
+ const lineId = this.getLineId(mobiusEvent.data.deviceId);
81
+ newCall = this.createCall({}, CallDirection.INBOUND, mobiusEvent.data.deviceId, lineId);
79
82
  log.log(`New incoming call created with correlationId from Call Setup message: ${newCall.getCorrelationId()}`, {
80
83
  file: CALL_MANAGER_FILE,
81
84
  method: 'dequeueWsEvents',
@@ -103,7 +106,7 @@ export class CallManager extends Eventing {
103
106
  });
104
107
  newCall.startCallerIdResolution(mobiusEvent.data.callerId);
105
108
  }
106
- this.emit(EVENT_KEYS.INCOMING_CALL, newCall);
109
+ this.emit(LINE_EVENT_KEYS.INCOMING_CALL, newCall);
107
110
  newCall.sendCallStateMachineEvt({ type: 'E_RECV_CALL_SETUP', data: mobiusEvent.data });
108
111
  break;
109
112
  }
@@ -135,7 +138,8 @@ export class CallManager extends Eventing {
135
138
  activeCall = this.getCall(newId);
136
139
  }
137
140
  else {
138
- activeCall = this.createCall({}, CallDirection.INBOUND, mobiusEvent.data.deviceId);
141
+ const lineId = this.getLineId(mobiusEvent.data.deviceId);
142
+ activeCall = this.createCall({}, CallDirection.INBOUND, mobiusEvent.data.deviceId, lineId);
139
143
  log.log(`New incoming call created with correlationId from ROAP Message: ${activeCall.getCorrelationId()}`, {
140
144
  file: CALL_MANAGER_FILE,
141
145
  method: 'dequeueWsEvents',
@@ -265,6 +269,12 @@ export class CallManager extends Eventing {
265
269
  getActiveCalls = () => {
266
270
  return this.callCollection;
267
271
  };
272
+ updateLine(deviceId, line) {
273
+ this.lineDict[deviceId] = line;
274
+ }
275
+ getLineId(deviceId) {
276
+ return this.lineDict[deviceId].lineId;
277
+ }
268
278
  }
269
279
  export const getCallManager = (webex, indicator) => {
270
280
  if (!callManager) {
@@ -1,12 +1,16 @@
1
1
  import { v4 as uuid } from 'uuid';
2
- import { MobiusStatus, ServiceIndicator } from '../../common/types';
3
- import { LINE_EVENTS } from './types';
4
- import { LINE_FILE } from '../constants';
2
+ import { CallDirection, MobiusStatus, ServiceIndicator, } from '../../common/types';
3
+ import { LINE_EVENTS, LineStatus } from './types';
4
+ import { LINE_FILE, VALID_PHONE } from '../constants';
5
5
  import log from '../../Logger';
6
6
  import { createRegistration } from '../registration';
7
7
  import { Eventing } from '../../Events/impl';
8
+ import { LineError } from '../../Errors/catalog/LineError';
8
9
  import { validateServiceData } from '../../common';
9
10
  import SDKConnector from '../../SDKConnector';
11
+ import { LINE_EVENT_KEYS } from '../../Events/types';
12
+ import { getCallManager } from '../calling/callManager';
13
+ import { ERROR_TYPE } from '../../Errors/types';
10
14
  export default class Line extends Eventing {
11
15
  #webex;
12
16
  #mutex;
@@ -29,6 +33,7 @@ export default class Line extends Eventing {
29
33
  rehomingIntervalMax;
30
34
  voicePortalNumber;
31
35
  voicePortalExtension;
36
+ callManager;
32
37
  #primaryMobiusUris;
33
38
  #backupMobiusUris;
34
39
  constructor(userId, clientDeviceUri, status, mutex, primaryMobiusUris, backupMobiusUris, logLevel, serviceDataConfig, phoneNumber, extension, voicemail) {
@@ -52,6 +57,8 @@ export default class Line extends Eventing {
52
57
  this.registration = createRegistration(this.#webex, serviceData, this.#mutex, this.lineEmitter, logLevel);
53
58
  this.registration.setStatus(MobiusStatus.DEFAULT);
54
59
  log.setLogger(logLevel, LINE_FILE);
60
+ this.callManager = getCallManager(this.#webex, serviceData.indicator);
61
+ this.incomingCallListener();
55
62
  }
56
63
  async register() {
57
64
  await this.#mutex.runExclusive(async () => {
@@ -60,6 +67,9 @@ export default class Line extends Eventing {
60
67
  this.registration.setMobiusServers(this.#primaryMobiusUris, this.#backupMobiusUris);
61
68
  await this.registration.triggerRegistration();
62
69
  });
70
+ if (this.mobiusDeviceId) {
71
+ this.callManager.updateLine(this.mobiusDeviceId, this);
72
+ }
63
73
  }
64
74
  async deregister() {
65
75
  this.registration.deregister();
@@ -107,4 +117,42 @@ export default class Line extends Eventing {
107
117
  }
108
118
  getRegistrationStatus = () => this.registration.getStatus();
109
119
  getDeviceId = () => this.registration.getDeviceInfo().device?.deviceId;
120
+ makeCall = (dest) => {
121
+ let call;
122
+ if (dest) {
123
+ const match = dest.address.match(VALID_PHONE);
124
+ if (match && match[0].length === dest.address.length) {
125
+ const sanitizedNumber = dest.address
126
+ .replace(/[^[*+]\d#]/gi, '')
127
+ .replace(/\s+/gi, '')
128
+ .replace(/-/gi, '');
129
+ const formattedDest = {
130
+ type: dest.type,
131
+ address: `tel:${sanitizedNumber}`,
132
+ };
133
+ call = this.callManager.createCall(formattedDest, CallDirection.OUTBOUND, this.registration.getDeviceInfo().device?.deviceId, this.lineId);
134
+ log.log(`New call created, callId: ${call.getCallId()}`, {});
135
+ }
136
+ else {
137
+ log.warn('Invalid phone number detected', {});
138
+ const err = new LineError('An invalid phone number was detected. Check the number and try again.', {}, ERROR_TYPE.CALL_ERROR, LineStatus.ACTIVE);
139
+ this.emit(LINE_EVENTS.ERROR, err);
140
+ }
141
+ return call;
142
+ }
143
+ return undefined;
144
+ };
145
+ incomingCallListener() {
146
+ const logContext = {
147
+ file: LINE_FILE,
148
+ method: this.incomingCallListener.name,
149
+ };
150
+ log.log('Listening for incoming calls... ', logContext);
151
+ this.callManager.on(LINE_EVENT_KEYS.INCOMING_CALL, (callObj) => {
152
+ this.emit(LINE_EVENTS.INCOMING_CALL, callObj);
153
+ });
154
+ }
155
+ getCall = (correlationId) => {
156
+ return this.callManager.getCall(correlationId);
157
+ };
110
158
  }
@@ -11,4 +11,5 @@ export var LINE_EVENTS;
11
11
  LINE_EVENTS["RECONNECTING"] = "reconnecting";
12
12
  LINE_EVENTS["REGISTERED"] = "registered";
13
13
  LINE_EVENTS["UNREGISTERED"] = "unregistered";
14
+ LINE_EVENTS["INCOMING_CALL"] = "line:incoming_call";
14
15
  })(LINE_EVENTS || (LINE_EVENTS = {}));
@@ -24,9 +24,6 @@ export class ContactsClient {
24
24
  this.defaultGroupId = '';
25
25
  log.setLogger(logger.level, CONTACTS_FILE);
26
26
  }
27
- getSDKConnector() {
28
- return this.sdkConnector;
29
- }
30
27
  async decryptContactDetail(encryptionKeyUrl, contactDetails) {
31
28
  const decryptedContactDetail = [...contactDetails];
32
29
  const decryptedValues = await Promise.all(decryptedContactDetail.map((detail) => this.webex.internal.encryption.decryptText(encryptionKeyUrl, detail.value)));
@@ -1,26 +1,35 @@
1
- export var EVENT_KEYS;
2
- (function (EVENT_KEYS) {
3
- EVENT_KEYS["ALERTING"] = "call:alerting";
4
- EVENT_KEYS["CALL_ERROR"] = "call:error";
5
- EVENT_KEYS["CALLER_ID"] = "call:caller_id";
6
- EVENT_KEYS["CONNECT"] = "call:connect";
7
- EVENT_KEYS["DISCONNECT"] = "call:disconnect";
8
- EVENT_KEYS["ERROR"] = "callingClient:error";
9
- EVENT_KEYS["ESTABLISHED"] = "call:established";
10
- EVENT_KEYS["HELD"] = "call:held";
11
- EVENT_KEYS["HOLD_ERROR"] = "call:hold_error";
12
- EVENT_KEYS["INCOMING_CALL"] = "callingClient:incoming_call";
13
- EVENT_KEYS["OUTGOING_CALL"] = "callingClient:outgoing_call";
14
- EVENT_KEYS["PROGRESS"] = "call:progress";
15
- EVENT_KEYS["REMOTE_MEDIA"] = "call:remote_media";
16
- EVENT_KEYS["RESUME_ERROR"] = "call:resume_error";
17
- EVENT_KEYS["RESUMED"] = "call:resumed";
18
- EVENT_KEYS["TRANSFER_ERROR"] = "call:transfer_error";
19
- EVENT_KEYS["USER_SESSION_INFO"] = "callingClient:user_recent_sessions";
20
- EVENT_KEYS["CB_VOICEMESSAGE_CONTENT_GET"] = "call_back_voicemail_content_get";
21
- EVENT_KEYS["CALL_HISTORY_USER_SESSION_INFO"] = "callHistory:user_recent_sessions";
22
- EVENT_KEYS["ALL_CALLS_CLEARED"] = "callingClient:all_calls_cleared";
23
- })(EVENT_KEYS || (EVENT_KEYS = {}));
1
+ export var COMMON_EVENT_KEYS;
2
+ (function (COMMON_EVENT_KEYS) {
3
+ COMMON_EVENT_KEYS["CB_VOICEMESSAGE_CONTENT_GET"] = "call_back_voicemail_content_get";
4
+ COMMON_EVENT_KEYS["CALL_HISTORY_USER_SESSION_INFO"] = "callHistory:user_recent_sessions";
5
+ })(COMMON_EVENT_KEYS || (COMMON_EVENT_KEYS = {}));
6
+ export var LINE_EVENT_KEYS;
7
+ (function (LINE_EVENT_KEYS) {
8
+ LINE_EVENT_KEYS["INCOMING_CALL"] = "incoming_call";
9
+ })(LINE_EVENT_KEYS || (LINE_EVENT_KEYS = {}));
10
+ export var CALLING_CLIENT_EVENT_KEYS;
11
+ (function (CALLING_CLIENT_EVENT_KEYS) {
12
+ CALLING_CLIENT_EVENT_KEYS["ERROR"] = "callingClient:error";
13
+ CALLING_CLIENT_EVENT_KEYS["OUTGOING_CALL"] = "callingClient:outgoing_call";
14
+ CALLING_CLIENT_EVENT_KEYS["USER_SESSION_INFO"] = "callingClient:user_recent_sessions";
15
+ CALLING_CLIENT_EVENT_KEYS["ALL_CALLS_CLEARED"] = "callingClient:all_calls_cleared";
16
+ })(CALLING_CLIENT_EVENT_KEYS || (CALLING_CLIENT_EVENT_KEYS = {}));
17
+ export var CALL_EVENT_KEYS;
18
+ (function (CALL_EVENT_KEYS) {
19
+ CALL_EVENT_KEYS["ALERTING"] = "alerting";
20
+ CALL_EVENT_KEYS["CALL_ERROR"] = "call_error";
21
+ CALL_EVENT_KEYS["CALLER_ID"] = "caller_id";
22
+ CALL_EVENT_KEYS["CONNECT"] = "connect";
23
+ CALL_EVENT_KEYS["DISCONNECT"] = "disconnect";
24
+ CALL_EVENT_KEYS["ESTABLISHED"] = "established";
25
+ CALL_EVENT_KEYS["HELD"] = "held";
26
+ CALL_EVENT_KEYS["HOLD_ERROR"] = "hold_error";
27
+ CALL_EVENT_KEYS["PROGRESS"] = "progress";
28
+ CALL_EVENT_KEYS["REMOTE_MEDIA"] = "remote_media";
29
+ CALL_EVENT_KEYS["RESUME_ERROR"] = "resume_error";
30
+ CALL_EVENT_KEYS["RESUMED"] = "resumed";
31
+ CALL_EVENT_KEYS["TRANSFER_ERROR"] = "transfer_error";
32
+ })(CALL_EVENT_KEYS || (CALL_EVENT_KEYS = {}));
24
33
  export var SUPPLEMENTARY_SERVICES;
25
34
  (function (SUPPLEMENTARY_SERVICES) {
26
35
  SUPPLEMENTARY_SERVICES["HOLD"] = "hold";
@@ -0,0 +1,6 @@
1
+ import { CallHistory } from './CallHistory/CallHistory';
2
+ import { CallSettings } from './CallSettings/CallSettings';
3
+ import { CallingClient } from './CallingClient/CallingClient';
4
+ import { ContactsClient } from './Contacts/ContactsClient';
5
+ import { Voicemail } from './Voicemail/Voicemail';
6
+ export { CallHistory, CallSettings, CallingClient, ContactsClient, Voicemail };
package/package.json CHANGED
@@ -31,7 +31,7 @@
31
31
  "test:style": "eslint 'src/**/*.ts'",
32
32
  "fix:lint": "eslint 'src/**/*.ts' --fix",
33
33
  "fix:prettier": "prettier \"src/**/*.ts\" --write",
34
- "build:docs": "typedoc --out docs/",
34
+ "build:docs": "typedoc --out ../../docs/calling",
35
35
  "docs": "typedoc --emit none",
36
36
  "deploy:npm": "npm publish"
37
37
  },
@@ -97,7 +97,7 @@
97
97
  "sinon": "12.0.1",
98
98
  "ts-jest": "27.1.4",
99
99
  "typed-emitter": "2.1.0",
100
- "typedoc": "0.22.13",
100
+ "typedoc": "0.23.26",
101
101
  "typescript": "4.9.5"
102
102
  },
103
103
  "commitlint": {
@@ -129,5 +129,5 @@
129
129
  "staticpath": "docs",
130
130
  "noprompt": true
131
131
  },
132
- "version": "0.0.1-next.6"
132
+ "version": "0.0.1-next.8"
133
133
  }