@app-connect/core 1.7.18 → 1.7.20
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/connector/proxy/index.js +2 -1
- package/handlers/auth.js +30 -55
- package/handlers/log.js +182 -10
- package/handlers/plugin.js +27 -0
- package/handlers/user.js +31 -2
- package/index.js +115 -22
- package/lib/authSession.js +21 -12
- package/lib/callLogComposer.js +1 -1
- package/lib/debugTracer.js +20 -2
- package/lib/util.js +21 -4
- package/mcp/README.md +395 -0
- package/mcp/mcpHandler.js +318 -82
- package/mcp/tools/checkAuthStatus.js +28 -35
- package/mcp/tools/createCallLog.js +13 -9
- package/mcp/tools/createContact.js +2 -6
- package/mcp/tools/doAuth.js +27 -157
- package/mcp/tools/findContactByName.js +6 -9
- package/mcp/tools/findContactByPhone.js +2 -6
- package/mcp/tools/getGoogleFilePicker.js +5 -9
- package/mcp/tools/getHelp.js +2 -3
- package/mcp/tools/getPublicConnectors.js +55 -24
- package/mcp/tools/index.js +11 -36
- package/mcp/tools/logout.js +32 -13
- package/mcp/tools/rcGetCallLogs.js +3 -20
- package/mcp/ui/App/App.tsx +358 -0
- package/mcp/ui/App/components/AuthInfoForm.tsx +113 -0
- package/mcp/ui/App/components/AuthSuccess.tsx +22 -0
- package/mcp/ui/App/components/ConnectorList.tsx +82 -0
- package/mcp/ui/App/components/DebugPanel.tsx +43 -0
- package/mcp/ui/App/components/OAuthConnect.tsx +270 -0
- package/mcp/ui/App/lib/callTool.ts +130 -0
- package/mcp/ui/App/lib/debugLog.ts +41 -0
- package/mcp/ui/App/lib/developerPortal.ts +111 -0
- package/mcp/ui/App/main.css +6 -0
- package/mcp/ui/App/root.tsx +13 -0
- package/mcp/ui/dist/index.html +53 -0
- package/mcp/ui/index.html +13 -0
- package/mcp/ui/package-lock.json +6356 -0
- package/mcp/ui/package.json +25 -0
- package/mcp/ui/tsconfig.json +26 -0
- package/mcp/ui/vite.config.ts +16 -0
- package/models/llmSessionModel.js +14 -0
- package/models/userModel.js +3 -0
- package/package.json +2 -2
- package/releaseNotes.json +24 -0
- package/test/handlers/auth.test.js +31 -0
- package/test/handlers/plugin.test.js +287 -0
- package/test/lib/util.test.js +379 -1
- package/test/mcp/tools/createCallLog.test.js +3 -3
- package/test/mcp/tools/doAuth.test.js +40 -303
- package/test/mcp/tools/findContactByName.test.js +3 -3
- package/test/mcp/tools/findContactByPhone.test.js +3 -3
- package/test/mcp/tools/getGoogleFilePicker.test.js +7 -7
- package/test/mcp/tools/getPublicConnectors.test.js +49 -70
- package/test/mcp/tools/logout.test.js +17 -11
- package/mcp/SupportedPlatforms.md +0 -12
- package/mcp/tools/collectAuthInfo.js +0 -91
- package/mcp/tools/setConnector.js +0 -69
- package/test/mcp/tools/collectAuthInfo.test.js +0 -234
- package/test/mcp/tools/setConnector.test.js +0 -177
package/connector/proxy/index.js
CHANGED
|
@@ -51,7 +51,7 @@ function getBasicAuth({ apiKey }) {
|
|
|
51
51
|
return Buffer.from(`${apiKey}:`).toString('base64');
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
async function getUserInfo({ authHeader, hostname, additionalInfo, platform, apiKey, proxyId, proxyConfig } = {}) {
|
|
54
|
+
async function getUserInfo({ authHeader, hostname, additionalInfo, platform, apiKey, proxyId, proxyConfig, userEmail } = {}) {
|
|
55
55
|
const cfg = proxyConfig ? proxyConfig : (await loadPlatformConfig(proxyId));
|
|
56
56
|
if (!cfg || !cfg.operations?.getUserInfo) {
|
|
57
57
|
// Fallback if no getUserInfo operation defined
|
|
@@ -72,6 +72,7 @@ async function getUserInfo({ authHeader, hostname, additionalInfo, platform, api
|
|
|
72
72
|
apiKey,
|
|
73
73
|
hostname,
|
|
74
74
|
platform,
|
|
75
|
+
userEmail,
|
|
75
76
|
},
|
|
76
77
|
user: {},
|
|
77
78
|
authHeader
|
package/handlers/auth.js
CHANGED
|
@@ -7,7 +7,7 @@ const adminCore = require('./admin');
|
|
|
7
7
|
const { Connector } = require('../models/dynamo/connectorSchema');
|
|
8
8
|
const { handleDatabaseError } = require('../lib/errorHandler');
|
|
9
9
|
|
|
10
|
-
async function onOAuthCallback({ platform, hostname, tokenUrl, query, isFromMCP = false }) {
|
|
10
|
+
async function onOAuthCallback({ platform, hostname, tokenUrl, query, hashedRcExtensionId, isFromMCP = false }) {
|
|
11
11
|
const callbackUri = query.callbackUri;
|
|
12
12
|
const apiUrl = query.apiUrl;
|
|
13
13
|
const username = query.username;
|
|
@@ -54,6 +54,7 @@ async function onOAuthCallback({ platform, hostname, tokenUrl, query, isFromMCP
|
|
|
54
54
|
refreshToken,
|
|
55
55
|
tokenExpiry: isNaN(expires) ? null : expires,
|
|
56
56
|
rcAccountId: query?.rcAccountId,
|
|
57
|
+
hashedRcExtensionId,
|
|
57
58
|
proxyId
|
|
58
59
|
});
|
|
59
60
|
}
|
|
@@ -76,7 +77,7 @@ async function onOAuthCallback({ platform, hostname, tokenUrl, query, isFromMCP
|
|
|
76
77
|
}
|
|
77
78
|
}
|
|
78
79
|
|
|
79
|
-
async function onApiKeyLogin({ platform, hostname, apiKey, proxyId, additionalInfo }) {
|
|
80
|
+
async function onApiKeyLogin({ platform, hostname, apiKey, proxyId, rcAccountId, hashedRcExtensionId, additionalInfo }) {
|
|
80
81
|
const platformModule = connectorRegistry.getConnector(platform);
|
|
81
82
|
const basicAuth = platformModule.getBasicAuth({ apiKey });
|
|
82
83
|
const { successful, platformUserInfo, returnMessage } = await platformModule.getUserInfo({ authHeader: `Basic ${basicAuth}`, hostname, platform, additionalInfo, apiKey, proxyId });
|
|
@@ -88,6 +89,8 @@ async function onApiKeyLogin({ platform, hostname, apiKey, proxyId, additionalIn
|
|
|
88
89
|
platform,
|
|
89
90
|
hostname,
|
|
90
91
|
proxyId,
|
|
92
|
+
hashedRcExtensionId,
|
|
93
|
+
rcAccountId,
|
|
91
94
|
accessToken: platformUserInfo.overridingApiKey ?? apiKey
|
|
92
95
|
});
|
|
93
96
|
}
|
|
@@ -110,7 +113,7 @@ async function onApiKeyLogin({ platform, hostname, apiKey, proxyId, additionalIn
|
|
|
110
113
|
}
|
|
111
114
|
}
|
|
112
115
|
|
|
113
|
-
async function saveUserInfo({ platformUserInfo, platform, hostname, accessToken, refreshToken, tokenExpiry, rcAccountId, proxyId }) {
|
|
116
|
+
async function saveUserInfo({ platformUserInfo, platform, hostname, accessToken, refreshToken, tokenExpiry, rcAccountId, hashedRcExtensionId, proxyId }) {
|
|
114
117
|
const id = platformUserInfo.id;
|
|
115
118
|
const name = platformUserInfo.name;
|
|
116
119
|
const existingUser = await UserModel.findByPk(id);
|
|
@@ -130,6 +133,7 @@ async function saveUserInfo({ platformUserInfo, platform, hostname, accessToken,
|
|
|
130
133
|
refreshToken,
|
|
131
134
|
tokenExpiry,
|
|
132
135
|
rcAccountId,
|
|
136
|
+
hashedRcExtensionId,
|
|
133
137
|
platformAdditionalInfo: {
|
|
134
138
|
...existingUser.platformAdditionalInfo, // keep existing platformAdditionalInfo
|
|
135
139
|
...platformAdditionalInfo,
|
|
@@ -143,57 +147,20 @@ async function saveUserInfo({ platformUserInfo, platform, hostname, accessToken,
|
|
|
143
147
|
}
|
|
144
148
|
else {
|
|
145
149
|
try {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
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
|
-
}
|
|
181
|
-
}
|
|
182
|
-
else {
|
|
183
|
-
await UserModel.create({
|
|
184
|
-
id,
|
|
185
|
-
hostname,
|
|
186
|
-
timezoneName,
|
|
187
|
-
timezoneOffset,
|
|
188
|
-
platform,
|
|
189
|
-
accessToken,
|
|
190
|
-
refreshToken,
|
|
191
|
-
tokenExpiry,
|
|
192
|
-
rcAccountId,
|
|
193
|
-
platformAdditionalInfo,
|
|
194
|
-
userSettings: {}
|
|
195
|
-
});
|
|
196
|
-
}
|
|
150
|
+
await UserModel.create({
|
|
151
|
+
id,
|
|
152
|
+
hostname,
|
|
153
|
+
timezoneName,
|
|
154
|
+
timezoneOffset,
|
|
155
|
+
platform,
|
|
156
|
+
accessToken,
|
|
157
|
+
refreshToken,
|
|
158
|
+
tokenExpiry,
|
|
159
|
+
rcAccountId,
|
|
160
|
+
hashedRcExtensionId,
|
|
161
|
+
platformAdditionalInfo,
|
|
162
|
+
userSettings: {}
|
|
163
|
+
});
|
|
197
164
|
}
|
|
198
165
|
catch (error) {
|
|
199
166
|
return handleDatabaseError(error, 'Error saving user info');
|
|
@@ -206,8 +173,16 @@ async function saveUserInfo({ platformUserInfo, platform, hostname, accessToken,
|
|
|
206
173
|
}
|
|
207
174
|
|
|
208
175
|
async function getLicenseStatus({ userId, platform }) {
|
|
176
|
+
const user = await UserModel.findByPk(userId);
|
|
177
|
+
if (!user) {
|
|
178
|
+
return {
|
|
179
|
+
isLicenseValid: false,
|
|
180
|
+
licenseStatus: 'Invalid (User not found)',
|
|
181
|
+
licenseStatusDescription: ''
|
|
182
|
+
}
|
|
183
|
+
}
|
|
209
184
|
const platformModule = connectorRegistry.getConnector(platform);
|
|
210
|
-
const licenseStatus = await platformModule.getLicenseStatus({ userId, platform });
|
|
185
|
+
const licenseStatus = await platformModule.getLicenseStatus({ userId, platform, user });
|
|
211
186
|
return licenseStatus;
|
|
212
187
|
}
|
|
213
188
|
|
package/handlers/log.js
CHANGED
|
@@ -2,6 +2,7 @@ const Op = require('sequelize').Op;
|
|
|
2
2
|
const { CallLogModel } = require('../models/callLogModel');
|
|
3
3
|
const { MessageLogModel } = require('../models/messageLogModel');
|
|
4
4
|
const { UserModel } = require('../models/userModel');
|
|
5
|
+
const { CacheModel } = require('../models/cacheModel');
|
|
5
6
|
const oauth = require('../lib/oauth');
|
|
6
7
|
const { composeCallLog } = require('../lib/callLogComposer');
|
|
7
8
|
const { composeSharedSMSLog } = require('../lib/sharedSMSComposer');
|
|
@@ -11,11 +12,14 @@ const { NoteCache } = require('../models/dynamo/noteCacheSchema');
|
|
|
11
12
|
const { Connector } = require('../models/dynamo/connectorSchema');
|
|
12
13
|
const moment = require('moment');
|
|
13
14
|
const { getMediaReaderLinkByPlatformMediaLink } = require('../lib/util');
|
|
15
|
+
const axios = require('axios');
|
|
16
|
+
const { getPluginsFromUserSettings } = require('../lib/util');
|
|
14
17
|
const logger = require('../lib/logger');
|
|
15
18
|
const { handleApiError, handleDatabaseError } = require('../lib/errorHandler');
|
|
19
|
+
const { v4: uuidv4 } = require('uuid');
|
|
16
20
|
const { AccountDataModel } = require('../models/accountDataModel');
|
|
17
21
|
|
|
18
|
-
async function createCallLog({ platform, userId, incomingData, hashedAccountId, isFromSSCL }) {
|
|
22
|
+
async function createCallLog({ jwtToken, platform, userId, incomingData, hashedAccountId, isFromSSCL }) {
|
|
19
23
|
try {
|
|
20
24
|
let existingCallLog = null;
|
|
21
25
|
try {
|
|
@@ -55,6 +59,7 @@ async function createCallLog({ platform, userId, incomingData, hashedAccountId,
|
|
|
55
59
|
}
|
|
56
60
|
};
|
|
57
61
|
}
|
|
62
|
+
|
|
58
63
|
const platformModule = connectorRegistry.getConnector(platform);
|
|
59
64
|
const callLog = incomingData.logInfo;
|
|
60
65
|
const additionalSubmission = incomingData.additionalSubmission;
|
|
@@ -115,6 +120,64 @@ async function createCallLog({ platform, userId, incomingData, hashedAccountId,
|
|
|
115
120
|
name: incomingData.contactName ?? ""
|
|
116
121
|
};
|
|
117
122
|
|
|
123
|
+
|
|
124
|
+
const pluginAsyncTaskIds = [];
|
|
125
|
+
// Plugins
|
|
126
|
+
const loggingPlugins = getPluginsFromUserSettings({ userSettings: user.userSettings, logType: 'call' });
|
|
127
|
+
for (const pluginSetting of loggingPlugins) {
|
|
128
|
+
const pluginId = pluginSetting.id;
|
|
129
|
+
let pluginDataResponse = null;
|
|
130
|
+
switch (pluginSetting.value.access) {
|
|
131
|
+
case 'public':
|
|
132
|
+
pluginDataResponse = await axios.get(`https://appconnect.labs.ringcentral.com/public-api/connectors/${pluginId}/manifest?type=plugin`);
|
|
133
|
+
break;
|
|
134
|
+
case 'private':
|
|
135
|
+
case 'shared':
|
|
136
|
+
pluginDataResponse = await axios.get(`https://appconnect.labs.ringcentral.com/public-api/connectors/${pluginId}/manifest?access=internal&type=connector&accountId=${user.rcAccountId}`);
|
|
137
|
+
break;
|
|
138
|
+
default:
|
|
139
|
+
throw new Error('Invalid plugin access');
|
|
140
|
+
}
|
|
141
|
+
const pluginData = pluginDataResponse.data;
|
|
142
|
+
const pluginManifest = pluginData.platforms[pluginSetting.value.name];
|
|
143
|
+
let pluginEndpointUrl = pluginManifest.endpointUrl;
|
|
144
|
+
if (!pluginEndpointUrl) {
|
|
145
|
+
throw new Error('Plugin URL is not set');
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
// check if endpoint has query params already
|
|
149
|
+
if (pluginEndpointUrl.includes('?')) {
|
|
150
|
+
pluginEndpointUrl += `&jwtToken=${jwtToken}`;
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
pluginEndpointUrl += `?jwtToken=${jwtToken}`;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
if (pluginSetting.value.isAsync) {
|
|
157
|
+
const asyncTaskId = `${userId}-${uuidv4()}`;
|
|
158
|
+
pluginAsyncTaskIds.push(asyncTaskId);
|
|
159
|
+
await CacheModel.create({
|
|
160
|
+
id: asyncTaskId,
|
|
161
|
+
status: 'initialized',
|
|
162
|
+
userId,
|
|
163
|
+
cacheKey: `pluginTask-${pluginSetting.value.name}`,
|
|
164
|
+
expiry: moment().add(1, 'hour').toDate()
|
|
165
|
+
});
|
|
166
|
+
axios.post(pluginEndpointUrl, {
|
|
167
|
+
data: incomingData,
|
|
168
|
+
asyncTaskId
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
const processedResultResponse = await axios.post(pluginEndpointUrl, {
|
|
173
|
+
data: incomingData
|
|
174
|
+
});
|
|
175
|
+
// eslint-disable-next-line no-param-reassign
|
|
176
|
+
incomingData = processedResultResponse.data;
|
|
177
|
+
note = incomingData.note;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
118
181
|
// Compose call log details centrally
|
|
119
182
|
const logFormat = platformModule.getLogFormatType ? platformModule.getLogFormatType(platform, proxyConfig) : LOG_DETAILS_FORMAT_TYPE.PLAIN_TEXT;
|
|
120
183
|
let composedLogDetails = '';
|
|
@@ -179,8 +242,8 @@ async function createCallLog({ platform, userId, incomingData, hashedAccountId,
|
|
|
179
242
|
catch (error) {
|
|
180
243
|
return handleDatabaseError(error, 'Error creating call log');
|
|
181
244
|
}
|
|
245
|
+
return { successful: !!logId, logId, returnMessage, extraDataTracking, pluginAsyncTaskIds };
|
|
182
246
|
}
|
|
183
|
-
return { successful: !!logId, logId, returnMessage, extraDataTracking };
|
|
184
247
|
} catch (e) {
|
|
185
248
|
return handleApiError(e, platform, 'createCallLog', { userId });
|
|
186
249
|
}
|
|
@@ -285,7 +348,7 @@ async function getCallLog({ userId, sessionIds, platform, requireDetails }) {
|
|
|
285
348
|
}
|
|
286
349
|
}
|
|
287
350
|
|
|
288
|
-
async function updateCallLog({ platform, userId, incomingData, hashedAccountId, isFromSSCL }) {
|
|
351
|
+
async function updateCallLog({ jwtToken, platform, userId, incomingData, hashedAccountId, isFromSSCL }) {
|
|
289
352
|
try {
|
|
290
353
|
let existingCallLog = null;
|
|
291
354
|
try {
|
|
@@ -299,11 +362,11 @@ async function updateCallLog({ platform, userId, incomingData, hashedAccountId,
|
|
|
299
362
|
return handleDatabaseError(error, 'Error finding existing call log');
|
|
300
363
|
}
|
|
301
364
|
if (existingCallLog) {
|
|
302
|
-
const platformModule = connectorRegistry.getConnector(platform);
|
|
303
365
|
let user = await UserModel.findByPk(userId);
|
|
304
366
|
if (!user || !user.accessToken) {
|
|
305
367
|
return { successful: false, message: `Contact not found` };
|
|
306
368
|
}
|
|
369
|
+
const platformModule = connectorRegistry.getConnector(platform);
|
|
307
370
|
const proxyId = user.platformAdditionalInfo?.proxyId;
|
|
308
371
|
let proxyConfig = null;
|
|
309
372
|
if (proxyId) {
|
|
@@ -334,6 +397,61 @@ async function updateCallLog({ platform, userId, incomingData, hashedAccountId,
|
|
|
334
397
|
break;
|
|
335
398
|
}
|
|
336
399
|
|
|
400
|
+
const pluginAsyncTaskIds = [];
|
|
401
|
+
// Plugins
|
|
402
|
+
const plugins = getPluginsFromUserSettings({ userSettings: user.userSettings, logType: 'call' });
|
|
403
|
+
for (const pluginSetting of plugins) {
|
|
404
|
+
const pluginId = pluginSetting.id;
|
|
405
|
+
let pluginDataResponse = null;
|
|
406
|
+
switch (pluginSetting.value.access) {
|
|
407
|
+
case 'public':
|
|
408
|
+
pluginDataResponse = await axios.get(`${process.env.DEV_PORTAL_URL}/public-api/connectors/${pluginId}/manifest?type=plugin`);
|
|
409
|
+
break;
|
|
410
|
+
case 'private':
|
|
411
|
+
case 'shared':
|
|
412
|
+
pluginDataResponse = await axios.get(`${process.env.DEV_PORTAL_URL}/public-api/connectors/${pluginId}/manifest?access=internal&type=connector&accountId=${user.rcAccountId}`);
|
|
413
|
+
break;
|
|
414
|
+
default:
|
|
415
|
+
throw new Error('Invalid plugin access');
|
|
416
|
+
}
|
|
417
|
+
const pluginData = pluginDataResponse.data;
|
|
418
|
+
const pluginManifest = pluginData.platforms[pluginSetting.value.name];
|
|
419
|
+
let pluginEndpointUrl = pluginManifest.endpointUrl;
|
|
420
|
+
if (!pluginEndpointUrl) {
|
|
421
|
+
throw new Error('Plugin URL is not set');
|
|
422
|
+
}
|
|
423
|
+
else {
|
|
424
|
+
if (pluginEndpointUrl.includes('?')) {
|
|
425
|
+
pluginEndpointUrl += `&jwtToken=${jwtToken}`;
|
|
426
|
+
}
|
|
427
|
+
else {
|
|
428
|
+
pluginEndpointUrl += `?jwtToken=${jwtToken}`;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
if (pluginSetting.value.isAsync) {
|
|
432
|
+
const asyncTaskId = `${userId}-${uuidv4()}`;
|
|
433
|
+
pluginAsyncTaskIds.push(asyncTaskId);
|
|
434
|
+
await CacheModel.create({
|
|
435
|
+
id: asyncTaskId,
|
|
436
|
+
status: 'initialized',
|
|
437
|
+
userId,
|
|
438
|
+
cacheKey: `pluginTask-${pluginSetting.value.name}`,
|
|
439
|
+
expiry: moment().add(1, 'hour').toDate()
|
|
440
|
+
});
|
|
441
|
+
axios.post(pluginEndpointUrl, {
|
|
442
|
+
data: { logInfo: incomingData },
|
|
443
|
+
asyncTaskId
|
|
444
|
+
});
|
|
445
|
+
}
|
|
446
|
+
else {
|
|
447
|
+
const processedResultResponse = await axios.post(pluginEndpointUrl, {
|
|
448
|
+
data: incomingData
|
|
449
|
+
});
|
|
450
|
+
// eslint-disable-next-line no-param-reassign
|
|
451
|
+
incomingData = processedResultResponse.data;
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
337
455
|
// Fetch existing call log details once to avoid duplicate API calls
|
|
338
456
|
let existingCallLogDetails = null; // Compose updated call log details centrally
|
|
339
457
|
const logFormat = platformModule.getLogFormatType ? platformModule.getLogFormatType(platform, proxyConfig) : LOG_DETAILS_FORMAT_TYPE.PLAIN_TEXT;
|
|
@@ -416,12 +534,7 @@ async function updateCallLog({ platform, userId, incomingData, hashedAccountId,
|
|
|
416
534
|
isFromSSCL,
|
|
417
535
|
proxyConfig,
|
|
418
536
|
});
|
|
419
|
-
|
|
420
|
-
extraDataTracking = {};
|
|
421
|
-
}
|
|
422
|
-
extraDataTracking.withSmartNoteLog = !!incomingData.aiNote;
|
|
423
|
-
extraDataTracking.withTranscript = !!incomingData.transcript;
|
|
424
|
-
return { successful: true, logId: existingCallLog.thirdPartyLogId, updatedNote, returnMessage, extraDataTracking };
|
|
537
|
+
return { successful: true, logId: existingCallLog.thirdPartyLogId, updatedNote, returnMessage, extraDataTracking, pluginAsyncTaskIds };
|
|
425
538
|
}
|
|
426
539
|
return { successful: false };
|
|
427
540
|
} catch (e) {
|
|
@@ -518,6 +631,65 @@ async function createMessageLog({ platform, userId, incomingData }) {
|
|
|
518
631
|
const ownerName = incomingData.logInfo.owner?.name;
|
|
519
632
|
const isSharedSMS = !!ownerName;
|
|
520
633
|
|
|
634
|
+
const pluginAsyncTaskIds = [];
|
|
635
|
+
// Plugins
|
|
636
|
+
const isSMS = incomingData.logInfo.messages.some(m => m.type === 'SMS');
|
|
637
|
+
const isFax = incomingData.logInfo.messages.some(m => m.type === 'Fax');
|
|
638
|
+
const smsPlugins = isSMS ? getPluginsFromUserSettings({ userSettings: user.userSettings, logType: 'sms' }) : [];
|
|
639
|
+
const faxPlugins = isFax ? getPluginsFromUserSettings({ userSettings: user.userSettings, logType: 'fax' }) : [];
|
|
640
|
+
const plugins = [...smsPlugins, ...faxPlugins];
|
|
641
|
+
for (const pluginSetting of plugins) {
|
|
642
|
+
const pluginId = pluginSetting.id;
|
|
643
|
+
let pluginDataResponse = null;
|
|
644
|
+
switch (pluginSetting.value.access) {
|
|
645
|
+
case 'public':
|
|
646
|
+
pluginDataResponse = await axios.get(`${process.env.DEV_PORTAL_URL}/public-api/connectors/${pluginId}/manifest?type=plugin`);
|
|
647
|
+
break;
|
|
648
|
+
case 'private':
|
|
649
|
+
case 'shared':
|
|
650
|
+
pluginDataResponse = await axios.get(`${process.env.DEV_PORTAL_URL}/public-api/connectors/${pluginId}/manifest?access=internal&type=connector&accountId=${user.rcAccountId}`);
|
|
651
|
+
break;
|
|
652
|
+
default:
|
|
653
|
+
throw new Error('Invalid plugin access');
|
|
654
|
+
}
|
|
655
|
+
const pluginData = pluginDataResponse.data;
|
|
656
|
+
const pluginManifest = pluginData.platforms[pluginSetting.value.name];
|
|
657
|
+
let pluginEndpointUrl = pluginManifest.endpointUrl;
|
|
658
|
+
if (!pluginEndpointUrl) {
|
|
659
|
+
throw new Error('Plugin URL is not set');
|
|
660
|
+
}
|
|
661
|
+
else {
|
|
662
|
+
if (pluginEndpointUrl.includes('?')) {
|
|
663
|
+
pluginEndpointUrl += `&jwtToken=${jwtToken}`;
|
|
664
|
+
}
|
|
665
|
+
else {
|
|
666
|
+
pluginEndpointUrl += `?jwtToken=${jwtToken}`;
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
if (pluginSetting.value.isAsync) {
|
|
670
|
+
const asyncTaskId = `${userId}-${uuidv4()}`;
|
|
671
|
+
pluginAsyncTaskIds.push(asyncTaskId);
|
|
672
|
+
await CacheModel.create({
|
|
673
|
+
id: asyncTaskId,
|
|
674
|
+
status: 'initialized',
|
|
675
|
+
userId,
|
|
676
|
+
cacheKey: `pluginTask-${pluginSetting.value.name}`,
|
|
677
|
+
expiry: moment().add(1, 'hour').toDate()
|
|
678
|
+
});
|
|
679
|
+
axios.post(pluginEndpointUrl, {
|
|
680
|
+
data: { logInfo: incomingData },
|
|
681
|
+
asyncTaskId
|
|
682
|
+
});
|
|
683
|
+
}
|
|
684
|
+
else {
|
|
685
|
+
const processedResultResponse = await axios.post(pluginEndpointUrl, {
|
|
686
|
+
data: incomingData
|
|
687
|
+
});
|
|
688
|
+
// eslint-disable-next-line no-param-reassign
|
|
689
|
+
incomingData = processedResultResponse.data;
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
|
|
521
693
|
let messageIds = [];
|
|
522
694
|
const correspondents = [];
|
|
523
695
|
if (isGroupSMS) {
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
const { CacheModel } = require('../models/cacheModel');
|
|
2
|
+
const { Op } = require('sequelize');
|
|
3
|
+
|
|
4
|
+
async function getPluginAsyncTasks({ asyncTaskIds }) {
|
|
5
|
+
const caches = await CacheModel.findAll({
|
|
6
|
+
where: {
|
|
7
|
+
id: {
|
|
8
|
+
[Op.in]: asyncTaskIds
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
});
|
|
12
|
+
const result = caches.map(cache => ({
|
|
13
|
+
cacheKey: cache.cacheKey,
|
|
14
|
+
status: cache.status
|
|
15
|
+
}));
|
|
16
|
+
const toRemoveCaches = caches.filter(cache => cache.status === 'completed' || cache.status === 'failed');
|
|
17
|
+
await CacheModel.destroy({
|
|
18
|
+
where: {
|
|
19
|
+
id: {
|
|
20
|
+
[Op.in]: toRemoveCaches.map(cache => cache.id)
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
return result;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
exports.getPluginAsyncTasks = getPluginAsyncTasks;
|
package/handlers/user.js
CHANGED
|
@@ -49,7 +49,10 @@ async function getUserSettings({ user, rcAccessToken, rcAccountId }) {
|
|
|
49
49
|
const keys = Object.keys(userSettingsByAdmin.userSettings).concat(Object.keys(userSettings));
|
|
50
50
|
// distinct keys
|
|
51
51
|
for (const key of new Set(keys)) {
|
|
52
|
-
//
|
|
52
|
+
// marked as removed
|
|
53
|
+
if (userSettingsByAdmin.userSettings[key]?.isRemoved) {
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
53
56
|
if ((userSettingsByAdmin.userSettings[key] === undefined || userSettingsByAdmin.userSettings[key].customizable) && userSettings[key] !== undefined) {
|
|
54
57
|
result[key] = {
|
|
55
58
|
customizable: true,
|
|
@@ -57,6 +60,27 @@ async function getUserSettings({ user, rcAccessToken, rcAccountId }) {
|
|
|
57
60
|
defaultValue: userSettings[key].defaultValue,
|
|
58
61
|
options: userSettings[key].options
|
|
59
62
|
};
|
|
63
|
+
// Special case: plugins
|
|
64
|
+
if (key.startsWith('plugin_')) {
|
|
65
|
+
const config = Object.keys(result[key].value.config)?.length === 0 ? null : result[key].value.config;
|
|
66
|
+
if (config) {
|
|
67
|
+
const configFromadminSettings = userSettingsByAdmin.userSettings[key].value.config ?? {};
|
|
68
|
+
for (const k in config) {
|
|
69
|
+
// use admin setting to replace, if not customizable
|
|
70
|
+
if (configFromadminSettings[k] && !configFromadminSettings[k].customizable || !config[k].value && configFromadminSettings[k].value) {
|
|
71
|
+
config[k] = configFromadminSettings[k];
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
config[k].customizable = configFromadminSettings[k].customizable;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
result[key].value.config = config;
|
|
78
|
+
}
|
|
79
|
+
//Case: no config at all, use admin setting directly
|
|
80
|
+
else {
|
|
81
|
+
result[key].value.config = userSettingsByAdmin.userSettings[key].value.config;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
60
84
|
}
|
|
61
85
|
// from admin settings
|
|
62
86
|
else {
|
|
@@ -68,7 +92,7 @@ async function getUserSettings({ user, rcAccessToken, rcAccountId }) {
|
|
|
68
92
|
return result;
|
|
69
93
|
}
|
|
70
94
|
|
|
71
|
-
async function updateUserSettings({ user, userSettings, platformName }) {
|
|
95
|
+
async function updateUserSettings({ user, userSettings, settingKeysToRemove, platformName }) {
|
|
72
96
|
const keys = Object.keys(userSettings || {});
|
|
73
97
|
let updatedSettings = {
|
|
74
98
|
...(user.userSettings || {})
|
|
@@ -76,6 +100,11 @@ async function updateUserSettings({ user, userSettings, platformName }) {
|
|
|
76
100
|
for (const k of keys) {
|
|
77
101
|
updatedSettings[k] = userSettings[k];
|
|
78
102
|
}
|
|
103
|
+
for (const k of settingKeysToRemove) {
|
|
104
|
+
if (updatedSettings[k]) {
|
|
105
|
+
delete updatedSettings[k];
|
|
106
|
+
}
|
|
107
|
+
}
|
|
79
108
|
const platformModule = connectorRegistry.getConnector(platformName);
|
|
80
109
|
if (platformModule.onUpdateUserSettings) {
|
|
81
110
|
const { successful, returnMessage } = await platformModule.onUpdateUserSettings({ user, userSettings, updatedSettings });
|