@app-connect/core 1.7.5 → 1.7.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/handlers/admin.js CHANGED
@@ -5,6 +5,8 @@ const oauth = require('../lib/oauth');
5
5
  const { RingCentral } = require('../lib/ringcentral');
6
6
  const { Connector } = require('../models/dynamo/connectorSchema');
7
7
 
8
+ const CALL_AGGREGATION_GROUPS = ["Company", "CompanyNumbers", "Users", "Queues", "IVRs", "IVAs", "SharedLines", "UserGroups", "Sites", "Departments"]
9
+
8
10
  async function validateAdminRole({ rcAccessToken }) {
9
11
  const rcExtensionResponse = await axios.get(
10
12
  'https://platform.ringcentral.com/restapi/v1.0/account/~/extension/~',
@@ -72,7 +74,7 @@ async function updateServerLoggingSettings({ user, additionalFieldValues }) {
72
74
  return {};
73
75
  }
74
76
 
75
- async function getAdminReport({ rcAccountId, timezone, timeFrom, timeTo }) {
77
+ async function getAdminReport({ rcAccountId, timezone, timeFrom, timeTo, groupBy }) {
76
78
  try {
77
79
  if (!process.env.RINGCENTRAL_SERVER || !process.env.RINGCENTRAL_CLIENT_ID || !process.env.RINGCENTRAL_CLIENT_SECRET) {
78
80
  return {
@@ -99,29 +101,39 @@ async function getAdminReport({ rcAccountId, timezone, timeFrom, timeTo }) {
99
101
  token: { access_token: adminConfig.adminAccessToken, token_type: 'Bearer' },
100
102
  timezone,
101
103
  timeFrom,
102
- timeTo
104
+ timeTo,
105
+ groupBy: groupBy == 'undefined' ? CALL_AGGREGATION_GROUPS[0] : groupBy
103
106
  });
104
- var dataCounter = callsAggregationData.data.records[0].counters;
105
- var inboundCallCount = dataCounter.callsByDirection.values.inbound;
106
- var outboundCallCount = dataCounter.callsByDirection.values.outbound;
107
- var answeredCallCount = dataCounter.callsByResponse.values.answered;
108
- // keep 2 decimal places
109
- var answeredCallPercentage = inboundCallCount === 0 ? '0%' : `${((answeredCallCount / inboundCallCount) * 100).toFixed(2)}%`;
110
-
111
- var dataTimer = callsAggregationData.data.records[0].timers;
112
- // keep 2 decimal places
113
- var totalTalkTime = (dataTimer.allCalls.values / 60).toFixed(2);
114
- // keep 2 decimal places
115
- var averageTalkTime = (totalTalkTime / (inboundCallCount + outboundCallCount)).toFixed(2);
116
- return {
117
- callLogStats: {
107
+ var callLogStats = [];
108
+ var itemKeys = [];
109
+ for (const record of callsAggregationData.data.records) {
110
+ if(!record?.info?.name){
111
+ continue;
112
+ }
113
+ itemKeys.push(record.info.name);
114
+ var dataCounter = record.counters;
115
+ var inboundCallCount = dataCounter.callsByDirection.values.inbound;
116
+ var outboundCallCount = dataCounter.callsByDirection.values.outbound;
117
+ var answeredCallCount = dataCounter.callsByResponse.values.answered;
118
+ // keep 2 decimal places
119
+ var answeredCallPercentage = inboundCallCount === 0 ? '0%' : `${((answeredCallCount / inboundCallCount) * 100).toFixed(2)}%`;
120
+ var totalTalkTime = Number(record.timers.allCalls.values) === 0 ? 0 : Number(record.timers.allCalls.values).toFixed(2);
121
+ var averageTalkTime = Number(totalTalkTime) === 0 ? 0 : (Number(totalTalkTime) / (inboundCallCount + outboundCallCount)).toFixed(2);
122
+ callLogStats.push({
123
+ name: record.info.name,
118
124
  inboundCallCount,
119
125
  outboundCallCount,
120
126
  answeredCallCount,
121
127
  answeredCallPercentage,
122
128
  totalTalkTime,
123
129
  averageTalkTime
124
- }
130
+ });
131
+ }
132
+ return {
133
+ callLogStats,
134
+ itemKeys,
135
+ groupedBy: callsAggregationData.data.groupedBy,
136
+ groupKeys: CALL_AGGREGATION_GROUPS
125
137
  };
126
138
  } catch (error) {
127
139
  console.error(error);
@@ -53,8 +53,41 @@ async function markCalled({ jwtToken, id, lastCallAt }) {
53
53
  return { successful: true };
54
54
  }
55
55
 
56
+ async function update({ jwtToken, id, updateData }) {
57
+ const unAuthData = jwt.decodeJwt(jwtToken);
58
+ if (!unAuthData?.id) throw new Error('Unauthorized');
59
+
60
+ // Prepare the update object with only valid fields
61
+ const allowedFields = ['contactId', 'contactType', 'contactName', 'phoneNumber', 'status', 'scheduledAt', 'lastCallAt', 'note'];
62
+ const updateObject = {};
63
+
64
+ // Filter and prepare update data
65
+ Object.keys(updateData).forEach(key => {
66
+ if (allowedFields.includes(key)) {
67
+ let value = updateData[key];
68
+
69
+ // Handle date fields
70
+ if ((key === 'scheduledAt' || key === 'lastCallAt') && value) {
71
+ value = new Date(value);
72
+ }
73
+
74
+ updateObject[key] = value;
75
+ }
76
+ });
77
+
78
+ // If no valid fields to update, throw error
79
+ if (Object.keys(updateObject).length === 0) {
80
+ throw new Error('No valid fields to update');
81
+ }
82
+
83
+ const [affected] = await CallDownListModel.update(updateObject, { where: { id, userId: unAuthData.id } });
84
+ if (!affected) throw new Error('Not found');
85
+ return { successful: true };
86
+ }
87
+
56
88
  exports.schedule = schedule;
57
89
  exports.list = list;
58
90
  exports.remove = remove;
59
91
  exports.markCalled = markCalled;
92
+ exports.update = update;
60
93
 
@@ -3,8 +3,11 @@ const { UserModel } = require('../models/userModel');
3
3
  const errorMessage = require('../lib/generalErrorMessage');
4
4
  const connectorRegistry = require('../connector/registry');
5
5
  const { Connector } = require('../models/dynamo/connectorSchema');
6
+ const { DebugTracer } = require('../lib/debugTracer');
6
7
 
7
- async function findContact({ platform, userId, phoneNumber, overridingFormat, isExtension }) {
8
+ async function findContact({ platform, userId, phoneNumber, overridingFormat, isExtension, tracer }) {
9
+ tracer?.trace('handler.findContact:entered', { platform, userId, phoneNumber });
10
+
8
11
  try {
9
12
  let user = await UserModel.findOne({
10
13
  where: {
@@ -12,7 +15,10 @@ async function findContact({ platform, userId, phoneNumber, overridingFormat, is
12
15
  platform
13
16
  }
14
17
  });
18
+ tracer?.trace('handler.findContact:userFound', { user });
19
+
15
20
  if (!user || !user.accessToken) {
21
+ tracer?.trace('handler.findContact:noUser', { userId });
16
22
  return {
17
23
  successful: false,
18
24
  returnMessage: {
@@ -26,26 +32,36 @@ async function findContact({ platform, userId, phoneNumber, overridingFormat, is
26
32
  let proxyConfig = null;
27
33
  if (proxyId) {
28
34
  proxyConfig = await Connector.getProxyConfig(proxyId);
35
+ tracer?.trace('handler.findContact:proxyConfig', { proxyConfig });
29
36
  }
30
37
  const platformModule = connectorRegistry.getConnector(platform);
31
38
  const authType = await platformModule.getAuthType({ proxyId, proxyConfig });
39
+ tracer?.trace('handler.findContact:authType', { authType });
40
+
32
41
  let authHeader = '';
33
42
  switch (authType) {
34
43
  case 'oauth':
35
44
  const oauthApp = oauth.getOAuthApp((await platformModule.getOauthInfo({ tokenUrl: user?.platformAdditionalInfo?.tokenUrl, hostname: user?.hostname, proxyId, proxyConfig })));
36
45
  user = await oauth.checkAndRefreshAccessToken(oauthApp, user);
37
46
  authHeader = `Bearer ${user.accessToken}`;
47
+ tracer?.trace('handler.findContact:oauthAuth', { authHeader });
38
48
  break;
39
49
  case 'apiKey':
40
50
  const basicAuth = platformModule.getBasicAuth({ apiKey: user.accessToken });
41
51
  authHeader = `Basic ${basicAuth}`;
52
+ tracer?.trace('handler.findContact:apiKeyAuth', {});
42
53
  break;
43
54
  }
44
- const { successful, matchedContactInfo, returnMessage, extraDataTracking } = await platformModule.findContact({ user, authHeader, phoneNumber, overridingFormat, isExtension, proxyConfig });
55
+
56
+ const { successful, matchedContactInfo, returnMessage, extraDataTracking } = await platformModule.findContact({ user, authHeader, phoneNumber, overridingFormat, isExtension, proxyConfig, tracer });
57
+ tracer?.trace('handler.findContact:platformFindResult', { successful, matchedContactInfo });
58
+
45
59
  if (matchedContactInfo != null && matchedContactInfo?.filter(c => !c.isNewContact)?.length > 0) {
60
+ tracer?.trace('handler.findContact:contactsFound', { count: matchedContactInfo.length });
46
61
  return { successful, returnMessage, contact: matchedContactInfo, extraDataTracking };
47
62
  }
48
63
  else {
64
+ tracer?.trace('handler.findContact:noContactsMatched', { matchedContactInfo });
49
65
  if (returnMessage) {
50
66
  return {
51
67
  successful,
@@ -78,6 +94,8 @@ async function findContact({ platform, userId, phoneNumber, overridingFormat, is
78
94
  }
79
95
  } catch (e) {
80
96
  console.error(`platform: ${platform} \n${e.stack} \n${JSON.stringify(e.response?.data)}`);
97
+ tracer?.traceError('handler.findContact:error', e, { platform, statusCode: e.response?.status });
98
+
81
99
  if (e.response?.status === 429) {
82
100
  return {
83
101
  successful: false,