@app-connect/core 1.7.8 → 1.7.11

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.
Files changed (69) hide show
  1. package/connector/developerPortal.js +43 -0
  2. package/connector/proxy/index.js +10 -3
  3. package/connector/registry.js +8 -6
  4. package/handlers/admin.js +44 -21
  5. package/handlers/auth.js +97 -69
  6. package/handlers/calldown.js +10 -4
  7. package/handlers/contact.js +45 -112
  8. package/handlers/disposition.js +4 -142
  9. package/handlers/log.js +174 -259
  10. package/handlers/user.js +19 -6
  11. package/index.js +310 -122
  12. package/lib/analytics.js +3 -1
  13. package/lib/authSession.js +68 -0
  14. package/lib/callLogComposer.js +498 -420
  15. package/lib/errorHandler.js +206 -0
  16. package/lib/jwt.js +2 -0
  17. package/lib/logger.js +190 -0
  18. package/lib/oauth.js +21 -12
  19. package/lib/ringcentral.js +2 -10
  20. package/lib/sharedSMSComposer.js +471 -0
  21. package/mcp/SupportedPlatforms.md +12 -0
  22. package/mcp/lib/validator.js +91 -0
  23. package/mcp/mcpHandler.js +166 -0
  24. package/mcp/tools/checkAuthStatus.js +90 -0
  25. package/mcp/tools/collectAuthInfo.js +86 -0
  26. package/mcp/tools/createCallLog.js +299 -0
  27. package/mcp/tools/createMessageLog.js +283 -0
  28. package/mcp/tools/doAuth.js +185 -0
  29. package/mcp/tools/findContactByName.js +87 -0
  30. package/mcp/tools/findContactByPhone.js +96 -0
  31. package/mcp/tools/getCallLog.js +98 -0
  32. package/mcp/tools/getHelp.js +39 -0
  33. package/mcp/tools/getPublicConnectors.js +46 -0
  34. package/mcp/tools/index.js +58 -0
  35. package/mcp/tools/logout.js +63 -0
  36. package/mcp/tools/rcGetCallLogs.js +73 -0
  37. package/mcp/tools/setConnector.js +64 -0
  38. package/mcp/tools/updateCallLog.js +122 -0
  39. package/models/accountDataModel.js +34 -0
  40. package/models/cacheModel.js +3 -0
  41. package/package.json +6 -4
  42. package/releaseNotes.json +36 -0
  43. package/test/connector/registry.test.js +145 -0
  44. package/test/handlers/admin.test.js +583 -0
  45. package/test/handlers/auth.test.js +355 -0
  46. package/test/handlers/contact.test.js +852 -0
  47. package/test/handlers/log.test.js +872 -0
  48. package/test/lib/callLogComposer.test.js +1231 -0
  49. package/test/lib/debugTracer.test.js +328 -0
  50. package/test/lib/logger.test.js +206 -0
  51. package/test/lib/oauth.test.js +359 -0
  52. package/test/lib/ringcentral.test.js +473 -0
  53. package/test/lib/sharedSMSComposer.test.js +1084 -0
  54. package/test/lib/util.test.js +282 -0
  55. package/test/mcp/tools/collectAuthInfo.test.js +192 -0
  56. package/test/mcp/tools/createCallLog.test.js +412 -0
  57. package/test/mcp/tools/createMessageLog.test.js +580 -0
  58. package/test/mcp/tools/doAuth.test.js +363 -0
  59. package/test/mcp/tools/findContactByName.test.js +263 -0
  60. package/test/mcp/tools/findContactByPhone.test.js +284 -0
  61. package/test/mcp/tools/getCallLog.test.js +286 -0
  62. package/test/mcp/tools/getPublicConnectors.test.js +128 -0
  63. package/test/mcp/tools/logout.test.js +169 -0
  64. package/test/mcp/tools/setConnector.test.js +177 -0
  65. package/test/mcp/tools/updateCallLog.test.js +346 -0
  66. package/test/models/accountDataModel.test.js +98 -0
  67. package/test/models/dynamo/connectorSchema.test.js +189 -0
  68. package/test/models/models.test.js +539 -0
  69. package/test/setup.js +176 -176
@@ -0,0 +1,43 @@
1
+ const axios = require('axios');
2
+ const { logger } = require('../lib/logger');
3
+
4
+
5
+ async function getPublicConnectorList() {
6
+ try {
7
+ const response = await axios.get('https://appconnect.labs.ringcentral.com/public-api/connectors');
8
+ return response.data;
9
+ } catch (error) {
10
+ logger.error('Error getting public connector list:', error);
11
+ return null;
12
+ }
13
+ }
14
+
15
+ async function getPrivateConnectorList() {
16
+ try {
17
+ const response = await axios.get(`https://appconnect.labs.ringcentral.com/public-api/connectors/internal?accountId=${process.env.RC_ACCOUNT_ID}`);
18
+ return response.data;
19
+ } catch (error) {
20
+ logger.error('Error getting private connector list:', error);
21
+ return null;
22
+ }
23
+ }
24
+
25
+ async function getConnectorManifest({connectorId, isPrivate = false}) {
26
+ try {
27
+ let response = null;
28
+ if(isPrivate) {
29
+ response = await axios.get(`https://appconnect.labs.ringcentral.com/public-api/connectors/${connectorId}/manifest?type=internal&accountId=${process.env.RC_ACCOUNT_ID}`);
30
+ }
31
+ else {
32
+ response = await axios.get(`https://appconnect.labs.ringcentral.com/public-api/connectors/${connectorId}/manifest`);
33
+ }
34
+ return response.data;
35
+ } catch (error) {
36
+ logger.error('Error getting connector manifest:', error);
37
+ return null;
38
+ }
39
+ }
40
+
41
+ exports.getPublicConnectorList = getPublicConnectorList;
42
+ exports.getPrivateConnectorList = getPrivateConnectorList;
43
+ exports.getConnectorManifest = getConnectorManifest;
@@ -10,6 +10,8 @@ const {
10
10
  } = require('./engine');
11
11
  const { Connector } = require('../../models/dynamo/connectorSchema');
12
12
  const { UserModel } = require('../../models/userModel');
13
+ const logger = require('../../lib/logger');
14
+ const { handleDatabaseError } = require('../../lib/errorHandler');
13
15
 
14
16
  async function loadPlatformConfig(proxyId) {
15
17
  if (!proxyId) {
@@ -19,7 +21,7 @@ async function loadPlatformConfig(proxyId) {
19
21
  const proxyConfig = await Connector.getProxyConfig(proxyId);
20
22
  return proxyConfig;
21
23
  } catch (error) {
22
- console.error('Error getting proxy config: ', proxyId);
24
+ logger.error('Error getting proxy config: ', { proxyId, stack: error.stack });
23
25
  return null;
24
26
  }
25
27
  }
@@ -32,7 +34,7 @@ async function getAuthType({ proxyId, proxyConfig } = {}) {
32
34
  return cfg.auth.type || 'apiKey';
33
35
  }
34
36
 
35
- async function getOauthInfo({ proxyId, proxyConfig, tokenUrl, hostname } = {}) {
37
+ async function getOauthInfo({ proxyId, proxyConfig, tokenUrl } = {}) {
36
38
  const cfg = proxyConfig ? proxyConfig : (await loadPlatformConfig(proxyId));
37
39
  if (!cfg) {
38
40
  return {};
@@ -146,7 +148,12 @@ async function unAuthorize({ user }) {
146
148
  }
147
149
  user.accessToken = '';
148
150
  user.refreshToken = '';
149
- await user.save();
151
+ try {
152
+ await user.save();
153
+ }
154
+ catch (error) {
155
+ return handleDatabaseError(error, 'Error saving user');
156
+ }
150
157
  return {
151
158
  successful: true,
152
159
  returnMessage: {
@@ -1,4 +1,5 @@
1
1
  // core/src/connector/registry.js
2
+ const logger = require('../lib/logger');
2
3
  class ConnectorRegistry {
3
4
  constructor() {
4
5
  this.connectors = new Map();
@@ -29,7 +30,7 @@ class ConnectorRegistry {
29
30
  const platformInterfaceMap = this.platformInterfaces.get(platformName);
30
31
  platformInterfaceMap.set(interfaceName, interfaceFunction);
31
32
 
32
- console.log(`Registered interface function: ${platformName}.${interfaceName}`);
33
+ logger.info(`Registered interface function: ${platformName}.${interfaceName}`);
33
34
  }
34
35
 
35
36
  /**
@@ -61,7 +62,7 @@ class ConnectorRegistry {
61
62
  const platformInterfaceMap = this.platformInterfaces.get(platformName);
62
63
  if (platformInterfaceMap && platformInterfaceMap.has(interfaceName)) {
63
64
  platformInterfaceMap.delete(interfaceName);
64
- console.log(`Unregistered interface function: ${platformName}.${interfaceName}`);
65
+ logger.info(`Unregistered interface function: ${platformName}.${interfaceName}`);
65
66
  }
66
67
  }
67
68
 
@@ -80,7 +81,7 @@ class ConnectorRegistry {
80
81
  this.manifests.set(platform, manifest);
81
82
  }
82
83
 
83
- console.log(`Registered connector: ${platform}`);
84
+ logger.info(`Registered connector: ${platform}`);
84
85
  }
85
86
 
86
87
  /**
@@ -109,7 +110,7 @@ class ConnectorRegistry {
109
110
  composedConnector[interfaceName] = interfaceFunction;
110
111
  }
111
112
 
112
- console.log(`Returning interface-only connector for platform: ${platform}`);
113
+ logger.info(`Returning interface-only connector for platform: ${platform}`);
113
114
  return composedConnector;
114
115
  }
115
116
 
@@ -203,14 +204,14 @@ class ConnectorRegistry {
203
204
  this.connectors.delete(platform);
204
205
  this.manifests.delete(platform);
205
206
  this.platformInterfaces.delete(platform);
206
- console.log(`Unregistered connector: ${platform}`);
207
+ logger.info(`Unregistered connector: ${platform}`);
207
208
  }
208
209
 
209
210
  setReleaseNotes(releaseNotes) {
210
211
  this.releaseNotes = releaseNotes;
211
212
  }
212
213
 
213
- getReleaseNotes(platform) {
214
+ getReleaseNotes() {
214
215
  return this.releaseNotes;
215
216
  }
216
217
 
@@ -237,6 +238,7 @@ class ConnectorRegistry {
237
238
  try {
238
239
  capabilities.authType = await originalConnector.getAuthType();
239
240
  } catch (error) {
241
+ logger.error('Error getting auth type', { stack: error.stack });
240
242
  capabilities.authType = 'unknown';
241
243
  }
242
244
  }
package/handlers/admin.js CHANGED
@@ -4,6 +4,8 @@ const connectorRegistry = require('../connector/registry');
4
4
  const oauth = require('../lib/oauth');
5
5
  const { RingCentral } = require('../lib/ringcentral');
6
6
  const { Connector } = require('../models/dynamo/connectorSchema');
7
+ const logger = require('../lib/logger');
8
+ const { handleDatabaseError } = require('../lib/errorHandler');
7
9
 
8
10
  const CALL_AGGREGATION_GROUPS = ["Company", "CompanyNumbers", "Users", "Queues", "IVRs", "IVAs", "SharedLines", "UserGroups", "Sites", "Departments"]
9
11
 
@@ -136,7 +138,7 @@ async function getAdminReport({ rcAccountId, timezone, timeFrom, timeTo, groupBy
136
138
  groupKeys: CALL_AGGREGATION_GROUPS
137
139
  };
138
140
  } catch (error) {
139
- console.error(error);
141
+ logger.error('Error getting admin report', { error });
140
142
  return {
141
143
  callLogStats: {}
142
144
  };
@@ -206,13 +208,19 @@ async function getUserReport({ rcAccountId, rcExtensionId, timezone, timeFrom, t
206
208
  };
207
209
  return reportStats;
208
210
  } catch (error) {
209
- console.error(error);
211
+ logger.error('Error getting user report', { error });
210
212
  return null;
211
213
  }
212
214
  }
213
215
 
214
216
  async function getUserMapping({ user, hashedRcAccountId, rcExtensionList }) {
215
- const adminConfig = await getAdminSettings({ hashedRcAccountId });
217
+ let adminConfig = null;
218
+ try {
219
+ adminConfig = await getAdminSettings({ hashedRcAccountId });
220
+ }
221
+ catch (error) {
222
+ return handleDatabaseError(error, 'Error getting user mapping');
223
+ }
216
224
  const platformModule = connectorRegistry.getConnector(user.platform);
217
225
  if (platformModule.getUserList) {
218
226
  const proxyId = user.platformAdditionalInfo?.proxyId;
@@ -318,12 +326,17 @@ async function getUserMapping({ user, hashedRcAccountId, rcExtensionList }) {
318
326
  });
319
327
  }
320
328
  }
321
- await upsertAdminSettings({
322
- hashedRcAccountId,
323
- adminSettings: {
324
- userMappings: initialUserMappings
325
- }
326
- });
329
+ try {
330
+ await upsertAdminSettings({
331
+ hashedRcAccountId,
332
+ adminSettings: {
333
+ userMappings: initialUserMappings
334
+ }
335
+ });
336
+ }
337
+ catch (error) {
338
+ return handleDatabaseError(error, 'Error initializing user mapping');
339
+ }
327
340
  }
328
341
  // Incremental update
329
342
  if (newUserMappings.length > 0) {
@@ -333,20 +346,30 @@ async function getUserMapping({ user, hashedRcAccountId, rcExtensionList }) {
333
346
  ...u,
334
347
  rcExtensionId: [u.rcExtensionId]
335
348
  }));
336
- await upsertAdminSettings({
337
- hashedRcAccountId,
338
- adminSettings: {
339
- userMappings: [...adminConfig.userMappings, ...newUserMappings]
340
- }
341
- });
349
+ try {
350
+ await upsertAdminSettings({
351
+ hashedRcAccountId,
352
+ adminSettings: {
353
+ userMappings: [...adminConfig.userMappings, ...newUserMappings]
354
+ }
355
+ });
356
+ }
357
+ catch (error) {
358
+ return handleDatabaseError(error, 'Error updating user mapping');
359
+ }
342
360
  }
343
361
  else {
344
- await upsertAdminSettings({
345
- hashedRcAccountId,
346
- adminSettings: {
347
- userMappings: [...newUserMappings]
348
- }
349
- });
362
+ try {
363
+ await upsertAdminSettings({
364
+ hashedRcAccountId,
365
+ adminSettings: {
366
+ userMappings: [...newUserMappings]
367
+ }
368
+ });
369
+ }
370
+ catch (error) {
371
+ return handleDatabaseError(error, 'Error updating user mapping');
372
+ }
350
373
  }
351
374
  }
352
375
  return userMappingResult;
package/handlers/auth.js CHANGED
@@ -5,15 +5,20 @@ const Op = require('sequelize').Op;
5
5
  const { RingCentral } = require('../lib/ringcentral');
6
6
  const adminCore = require('./admin');
7
7
  const { Connector } = require('../models/dynamo/connectorSchema');
8
+ const { handleDatabaseError } = require('../lib/errorHandler');
8
9
 
9
- async function onOAuthCallback({ platform, hostname, tokenUrl, callbackUri, apiUrl, username, query, proxyId }) {
10
+ async function onOAuthCallback({ platform, hostname, tokenUrl, query, isFromMCP = false }) {
11
+ const callbackUri = query.callbackUri;
12
+ const apiUrl = query.apiUrl;
13
+ const username = query.username;
14
+ const proxyId = query.proxyId;
15
+ const userEmail = query.userEmail;
10
16
  const platformModule = connectorRegistry.getConnector(platform);
11
17
  let proxyConfig = null;
12
18
  if (proxyId) {
13
19
  proxyConfig = await Connector.getProxyConfig(proxyId);
14
20
  }
15
- const oauthInfo = await platformModule.getOauthInfo({ tokenUrl, hostname, rcAccountId: query.rcAccountId, proxyId, proxyConfig });
16
-
21
+ const oauthInfo = await platformModule.getOauthInfo({ tokenUrl, hostname, rcAccountId: query.rcAccountId, proxyId, proxyConfig, userEmail, isFromMCP });
17
22
  if (oauthInfo.failMessage) {
18
23
  return {
19
24
  userInfo: null,
@@ -27,27 +32,34 @@ async function onOAuthCallback({ platform, hostname, tokenUrl, callbackUri, apiU
27
32
  // Some platforms require different oauth queries, this won't affect normal OAuth process unless CRM module implements getOverridingOAuthOption() method
28
33
  let overridingOAuthOption = null;
29
34
  if (platformModule.getOverridingOAuthOption != null) {
30
- overridingOAuthOption = platformModule.getOverridingOAuthOption({ code: callbackUri.split('code=')[1] });
35
+ const code = new URL(callbackUri).searchParams.get('code');
36
+ overridingOAuthOption = platformModule.getOverridingOAuthOption({ code });
31
37
  }
32
38
  const oauthApp = oauth.getOAuthApp(oauthInfo);
33
39
  const { accessToken, refreshToken, expires } = await oauthApp.code.getToken(callbackUri, overridingOAuthOption);
34
40
  const authHeader = `Bearer ${accessToken}`;
35
- const { successful, platformUserInfo, returnMessage } = await platformModule.getUserInfo({ authHeader, tokenUrl, apiUrl, hostname, platform, username, callbackUri, query, proxyId, proxyConfig });
41
+ const { successful, platformUserInfo, returnMessage } = await platformModule.getUserInfo({ authHeader, tokenUrl, apiUrl, hostname, platform, username, callbackUri, query, proxyId, proxyConfig, userEmail });
36
42
 
37
43
  if (successful) {
38
- let userInfo = await saveUserInfo({
39
- platformUserInfo,
40
- platform,
41
- tokenUrl,
42
- apiUrl,
43
- username,
44
- hostname: platformUserInfo?.overridingHostname ? platformUserInfo.overridingHostname : hostname,
45
- accessToken,
46
- refreshToken,
47
- tokenExpiry: expires,
48
- rcAccountId: query.rcAccountId,
49
- proxyId
50
- });
44
+ let userInfo = null;
45
+ try {
46
+ userInfo = await saveUserInfo({
47
+ platformUserInfo,
48
+ platform,
49
+ tokenUrl,
50
+ apiUrl,
51
+ username,
52
+ hostname: platformUserInfo?.overridingHostname ? platformUserInfo.overridingHostname : hostname,
53
+ accessToken,
54
+ refreshToken,
55
+ tokenExpiry: expires,
56
+ rcAccountId: query?.rcAccountId,
57
+ proxyId
58
+ });
59
+ }
60
+ catch (error) {
61
+ return handleDatabaseError(error, 'Error saving user info');
62
+ }
51
63
  if (platformModule.postSaveUserInfo) {
52
64
  userInfo = await platformModule.postSaveUserInfo({ userInfo, oauthApp });
53
65
  }
@@ -69,13 +81,19 @@ async function onApiKeyLogin({ platform, hostname, apiKey, proxyId, additionalIn
69
81
  const basicAuth = platformModule.getBasicAuth({ apiKey });
70
82
  const { successful, platformUserInfo, returnMessage } = await platformModule.getUserInfo({ authHeader: `Basic ${basicAuth}`, hostname, platform, additionalInfo, apiKey, proxyId });
71
83
  if (successful) {
72
- let userInfo = await saveUserInfo({
73
- platformUserInfo,
74
- platform,
75
- hostname,
76
- proxyId,
77
- accessToken: platformUserInfo.overridingApiKey ?? apiKey
78
- });
84
+ let userInfo = null;
85
+ try {
86
+ userInfo = await saveUserInfo({
87
+ platformUserInfo,
88
+ platform,
89
+ hostname,
90
+ proxyId,
91
+ accessToken: platformUserInfo.overridingApiKey ?? apiKey
92
+ });
93
+ }
94
+ catch (error) {
95
+ return handleDatabaseError(error, 'Error saving user info');
96
+ }
79
97
  if (platformModule.postSaveUserInfo) {
80
98
  userInfo = await platformModule.postSaveUserInfo({ userInfo });
81
99
  }
@@ -101,43 +119,65 @@ async function saveUserInfo({ platformUserInfo, platform, hostname, accessToken,
101
119
  const platformAdditionalInfo = platformUserInfo.platformAdditionalInfo || {};
102
120
  platformAdditionalInfo.proxyId = proxyId;
103
121
  if (existingUser) {
104
- await existingUser.update(
105
- {
106
- platform,
107
- hostname,
108
- timezoneName,
109
- timezoneOffset,
110
- accessToken,
111
- refreshToken,
112
- tokenExpiry,
113
- rcAccountId,
114
- platformAdditionalInfo: {
115
- ...existingUser.platformAdditionalInfo, // keep existing platformAdditionalInfo
116
- ...platformAdditionalInfo,
117
- }
118
- }
119
- );
120
- }
121
- else {
122
- // TEMP: replace user with old ID
123
- if (id.endsWith(`-${platform}`)) {
124
- const oldID = id.split('-');
125
- const userWithOldID = await UserModel.findByPk(oldID[0]);
126
- if (userWithOldID) {
127
- await UserModel.create({
128
- id,
122
+ try {
123
+ await existingUser.update(
124
+ {
125
+ platform,
129
126
  hostname,
130
127
  timezoneName,
131
128
  timezoneOffset,
132
- platform,
133
129
  accessToken,
134
130
  refreshToken,
135
131
  tokenExpiry,
136
132
  rcAccountId,
137
- platformAdditionalInfo,
138
- userSettings: userWithOldID.userSettings
139
- });
140
- await userWithOldID.destroy();
133
+ platformAdditionalInfo: {
134
+ ...existingUser.platformAdditionalInfo, // keep existing platformAdditionalInfo
135
+ ...platformAdditionalInfo,
136
+ }
137
+ }
138
+ );
139
+ }
140
+ catch (error) {
141
+ return handleDatabaseError(error, 'Error saving user info');
142
+ }
143
+ }
144
+ else {
145
+ try {
146
+ // TEMP: replace user with old ID
147
+ if (id.endsWith(`-${platform}`)) {
148
+ const oldID = id.split('-');
149
+ const userWithOldID = await UserModel.findByPk(oldID[0]);
150
+ if (userWithOldID) {
151
+ await UserModel.create({
152
+ id,
153
+ hostname,
154
+ timezoneName,
155
+ timezoneOffset,
156
+ platform,
157
+ accessToken,
158
+ refreshToken,
159
+ tokenExpiry,
160
+ rcAccountId,
161
+ platformAdditionalInfo,
162
+ userSettings: userWithOldID.userSettings
163
+ });
164
+ await userWithOldID.destroy();
165
+ }
166
+ else {
167
+ await UserModel.create({
168
+ id,
169
+ hostname,
170
+ timezoneName,
171
+ timezoneOffset,
172
+ platform,
173
+ accessToken,
174
+ refreshToken,
175
+ tokenExpiry,
176
+ rcAccountId,
177
+ platformAdditionalInfo,
178
+ userSettings: {}
179
+ });
180
+ }
141
181
  }
142
182
  else {
143
183
  await UserModel.create({
@@ -155,20 +195,8 @@ async function saveUserInfo({ platformUserInfo, platform, hostname, accessToken,
155
195
  });
156
196
  }
157
197
  }
158
- else {
159
- await UserModel.create({
160
- id,
161
- hostname,
162
- timezoneName,
163
- timezoneOffset,
164
- platform,
165
- accessToken,
166
- refreshToken,
167
- tokenExpiry,
168
- rcAccountId,
169
- platformAdditionalInfo,
170
- userSettings: {}
171
- });
198
+ catch (error) {
199
+ return handleDatabaseError(error, 'Error saving user info');
172
200
  }
173
201
  }
174
202
  return {
@@ -2,8 +2,9 @@ const { UserModel } = require('../models/userModel');
2
2
  const { CallDownListModel } = require('../models/callDownListModel');
3
3
  const { Op } = require('sequelize');
4
4
  const jwt = require('../lib/jwt');
5
+ const { handleDatabaseError } = require('../lib/errorHandler');
5
6
 
6
- async function schedule({ jwtToken, rcAccessToken, body }) {
7
+ async function schedule({ jwtToken, body }) {
7
8
  const unAuthData = jwt.decodeJwt(jwtToken);
8
9
  if (!unAuthData?.id) throw new Error('Unauthorized');
9
10
  const user = await UserModel.findByPk(unAuthData.id);
@@ -48,9 +49,14 @@ async function markCalled({ jwtToken, id, lastCallAt }) {
48
49
  const unAuthData = jwt.decodeJwt(jwtToken);
49
50
  if (!unAuthData?.id) throw new Error('Unauthorized');
50
51
  const when = lastCallAt ? new Date(lastCallAt) : new Date();
51
- const [affected] = await CallDownListModel.update({ status: 'called', lastCallAt: when }, { where: { id, userId: unAuthData.id } });
52
- if (!affected) throw new Error('Not found');
53
- return { successful: true };
52
+ try {
53
+ const [affected] = await CallDownListModel.update({ status: 'called', lastCallAt: when }, { where: { id, userId: unAuthData.id } });
54
+ if (!affected) throw new Error('Not found');
55
+ return { successful: true };
56
+ }
57
+ catch (error) {
58
+ return handleDatabaseError(error, 'Error marking call as called', { id, userId: unAuthData.id });
59
+ }
54
60
  }
55
61
 
56
62
  async function update({ jwtToken, id, updateData }) {