@app-connect/core 1.7.21 → 1.7.23

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 (50) hide show
  1. package/README.md +8 -1
  2. package/connector/developerPortal.js +4 -4
  3. package/docs/README.md +50 -0
  4. package/docs/architecture.md +93 -0
  5. package/docs/connectors.md +117 -0
  6. package/docs/handlers.md +125 -0
  7. package/docs/libraries.md +101 -0
  8. package/docs/models.md +144 -0
  9. package/docs/routes.md +115 -0
  10. package/docs/tests.md +73 -0
  11. package/handlers/admin.js +22 -2
  12. package/handlers/auth.js +57 -10
  13. package/handlers/log.js +217 -109
  14. package/handlers/managedAuth.js +446 -0
  15. package/handlers/plugin.js +183 -1
  16. package/handlers/user.js +1 -1
  17. package/index.js +410 -35
  18. package/lib/callLogComposer.js +36 -36
  19. package/lib/jwt.js +1 -1
  20. package/lib/util.js +0 -18
  21. package/mcp/tools/createCallLog.js +5 -1
  22. package/mcp/tools/createContact.js +5 -1
  23. package/mcp/tools/createMessageLog.js +5 -1
  24. package/mcp/tools/findContactByName.js +5 -1
  25. package/mcp/tools/findContactByPhone.js +6 -2
  26. package/mcp/tools/getCallLog.js +5 -1
  27. package/mcp/tools/rcGetCallLogs.js +6 -2
  28. package/mcp/tools/updateCallLog.js +5 -1
  29. package/mcp/ui/App/lib/developerPortal.ts +1 -1
  30. package/package.json +72 -72
  31. package/releaseNotes.json +16 -0
  32. package/test/handlers/admin.test.js +33 -0
  33. package/test/handlers/auth.test.js +402 -6
  34. package/test/handlers/log.test.js +60 -0
  35. package/test/handlers/managedAuth.test.js +458 -0
  36. package/test/handlers/plugin.test.js +93 -0
  37. package/test/index.test.js +105 -0
  38. package/test/lib/callLogComposer.test.js +21 -21
  39. package/test/lib/jwt.test.js +15 -0
  40. package/test/lib/util.test.js +1 -332
  41. package/test/mcp/tools/createCallLog.test.js +11 -0
  42. package/test/mcp/tools/createContact.test.js +58 -0
  43. package/test/mcp/tools/createMessageLog.test.js +15 -0
  44. package/test/mcp/tools/findContactByName.test.js +12 -0
  45. package/test/mcp/tools/findContactByPhone.test.js +12 -0
  46. package/test/mcp/tools/getCallLog.test.js +12 -0
  47. package/test/mcp/tools/rcGetCallLogs.test.js +56 -0
  48. package/test/mcp/tools/updateCallLog.test.js +14 -0
  49. package/test/routes/managedAuthRoutes.test.js +129 -0
  50. package/test/setup.js +2 -0
@@ -702,27 +702,27 @@ function upsertRingSenseTranscript({ body, transcript, logFormat }) {
702
702
  switch (logFormat) {
703
703
  case LOG_DETAILS_FORMAT_TYPE.HTML:
704
704
  const formattedTranscript = clearedTranscript.replace(/(?:\r\n|\r|\n)/g, '<br>');
705
- transcriptRegex = /<div><b>RingSense transcript<\/b><br>(.+?)<\/div>/;
705
+ transcriptRegex = /<div><b>ACE transcript<\/b><br>(.+?)<\/div>/;
706
706
  if (transcriptRegex.test(result)) {
707
- result = result.replace(transcriptRegex, `<div><b>RingSense transcript</b><br>${formattedTranscript}</div>`);
707
+ result = result.replace(transcriptRegex, `<div><b>ACE transcript</b><br>${formattedTranscript}</div>`);
708
708
  } else {
709
- result += `<div><b>RingSense transcript</b><br>${formattedTranscript}</div>`;
709
+ result += `<div><b>ACE transcript</b><br>${formattedTranscript}</div>`;
710
710
  }
711
711
  break;
712
712
  case LOG_DETAILS_FORMAT_TYPE.MARKDOWN:
713
- transcriptRegex = /### RingSense transcript\n([\s\S]*?)(?=\n### |\n$|$)/;
713
+ transcriptRegex = /### ACE transcript\n([\s\S]*?)(?=\n### |\n$|$)/;
714
714
  if (transcriptRegex.test(result)) {
715
- result = result.replace(transcriptRegex, `### RingSense transcript\n${clearedTranscript}\n`);
715
+ result = result.replace(transcriptRegex, `### ACE transcript\n${clearedTranscript}\n`);
716
716
  } else {
717
- result += `### RingSense transcript\n${clearedTranscript}\n`;
717
+ result += `### ACE transcript\n${clearedTranscript}\n`;
718
718
  }
719
719
  break;
720
720
  case LOG_DETAILS_FORMAT_TYPE.PLAIN_TEXT:
721
- transcriptRegex = /- RingSense transcript:([\s\S]*?)--- END/;
721
+ transcriptRegex = /- ACE transcript:([\s\S]*?)--- END/;
722
722
  if (transcriptRegex.test(result)) {
723
- result = result.replace(transcriptRegex, `- RingSense transcript:\n${clearedTranscript}\n--- END`);
723
+ result = result.replace(transcriptRegex, `- ACE transcript:\n${clearedTranscript}\n--- END`);
724
724
  } else {
725
- result += `\n- RingSense transcript:\n${clearedTranscript}\n--- END\n`;
725
+ result += `\n- ACE transcript:\n${clearedTranscript}\n--- END\n`;
726
726
  }
727
727
  break;
728
728
  }
@@ -739,28 +739,28 @@ function upsertRingSenseSummary({ body, summary, logFormat }) {
739
739
 
740
740
  switch (logFormat) {
741
741
  case LOG_DETAILS_FORMAT_TYPE.HTML:
742
- summaryRegex = /<div><b>RingSense summary<\/b><br>(.+?)<\/div>/;
742
+ summaryRegex = /<div><b>ACE summary<\/b><br>(.+?)<\/div>/;
743
743
  const formattedSummary = clearedSummary.replace(/(?:\r\n|\r|\n)/g, '<br>');
744
744
  if (summaryRegex.test(result)) {
745
- result = result.replace(summaryRegex, `<div><b>RingSense summary</b><br>${formattedSummary}</div>`);
745
+ result = result.replace(summaryRegex, `<div><b>ACE summary</b><br>${formattedSummary}</div>`);
746
746
  } else {
747
- result += `<div><b>RingSense summary</b><br>${formattedSummary}</div>`;
747
+ result += `<div><b>ACE summary</b><br>${formattedSummary}</div>`;
748
748
  }
749
749
  break;
750
750
  case LOG_DETAILS_FORMAT_TYPE.MARKDOWN:
751
- summaryRegex = /### RingSense summary\n([\s\S]*?)(?=\n### |\n$|$)/;
751
+ summaryRegex = /### ACE summary\n([\s\S]*?)(?=\n### |\n$|$)/;
752
752
  if (summaryRegex.test(result)) {
753
- result = result.replace(summaryRegex, `### RingSense summary\n${summary}\n`);
753
+ result = result.replace(summaryRegex, `### ACE summary\n${summary}\n`);
754
754
  } else {
755
- result += `### RingSense summary\n${summary}\n`;
755
+ result += `### ACE summary\n${summary}\n`;
756
756
  }
757
757
  break;
758
758
  case LOG_DETAILS_FORMAT_TYPE.PLAIN_TEXT:
759
- summaryRegex = /- RingSense summary:([\s\S]*?)--- END/;
759
+ summaryRegex = /- ACE summary:([\s\S]*?)--- END/;
760
760
  if (summaryRegex.test(result)) {
761
- result = result.replace(summaryRegex, `- RingSense summary:\n${summary}\n--- END`);
761
+ result = result.replace(summaryRegex, `- ACE summary:\n${summary}\n--- END`);
762
762
  } else {
763
- result += `\n- RingSense summary:\n${summary}\n--- END\n`;
763
+ result += `\n- ACE summary:\n${summary}\n--- END\n`;
764
764
  }
765
765
  break;
766
766
  }
@@ -811,28 +811,28 @@ function upsertRingSenseBulletedSummary({ body, summary, logFormat }) {
811
811
 
812
812
  switch (logFormat) {
813
813
  case LOG_DETAILS_FORMAT_TYPE.HTML:
814
- summaryRegex = /<div><b>RingSense bulleted summary<\/b><br>(.+?)<\/div>/;
814
+ summaryRegex = /<div><b>ACE bulleted summary<\/b><br>(.+?)<\/div>/;
815
815
  const formattedSummary = clearedSummary.replace(/(?:\r\n|\r|\n)/g, '<br>');
816
816
  if (summaryRegex.test(result)) {
817
- result = result.replace(summaryRegex, `<div><b>RingSense bulleted summary</b><br>${formattedSummary}</div>`);
817
+ result = result.replace(summaryRegex, `<div><b>ACE bulleted summary</b><br>${formattedSummary}</div>`);
818
818
  } else {
819
- result += `<div><b>RingSense bulleted summary</b><br>${formattedSummary}</div>`;
819
+ result += `<div><b>ACE bulleted summary</b><br>${formattedSummary}</div>`;
820
820
  }
821
821
  break;
822
822
  case LOG_DETAILS_FORMAT_TYPE.MARKDOWN:
823
- summaryRegex = /### RingSense bulleted summary\n([\s\S]*?)(?=\n### |\n$|$)/;
823
+ summaryRegex = /### ACE bulleted summary\n([\s\S]*?)(?=\n### |\n$|$)/;
824
824
  if (summaryRegex.test(result)) {
825
- result = result.replace(summaryRegex, `### RingSense bulleted summary\n${summary}\n`);
825
+ result = result.replace(summaryRegex, `### ACE bulleted summary\n${summary}\n`);
826
826
  } else {
827
- result += `### RingSense bulleted summary\n${summary}\n`;
827
+ result += `### ACE bulleted summary\n${summary}\n`;
828
828
  }
829
829
  break;
830
830
  case LOG_DETAILS_FORMAT_TYPE.PLAIN_TEXT:
831
- summaryRegex = /- RingSense bulleted summary:\s*([^<\n]+)(?=\n|$)/i;
831
+ summaryRegex = /- ACE bulleted summary:\s*([^<\n]+)(?=\n|$)/i;
832
832
  if (summaryRegex.test(result)) {
833
- result = result.replace(summaryRegex, `- RingSense bulleted summary:\n${summary}\n--- END`);
833
+ result = result.replace(summaryRegex, `- ACE bulleted summary:\n${summary}\n--- END`);
834
834
  } else {
835
- result += `\n- RingSense bulleted summary:\n${summary}\n--- END\n`;
835
+ result += `\n- ACE bulleted summary:\n${summary}\n--- END\n`;
836
836
  }
837
837
  break;
838
838
  }
@@ -847,27 +847,27 @@ function upsertRingSenseLink({ body, link, logFormat }) {
847
847
 
848
848
  switch (logFormat) {
849
849
  case LOG_DETAILS_FORMAT_TYPE.HTML:
850
- linkRegex = /(?:<li>)?<b>RingSense recording link<\/b>:\s*(?:<a[^>]*>[^<]*<\/a>|[^<]+)(?:<\/li>|(?=<|$))/i;
850
+ linkRegex = /(?:<li>)?<b>ACE recording link<\/b>:\s*(?:<a[^>]*>[^<]*<\/a>|[^<]+)(?:<\/li>|(?=<|$))/i;
851
851
  if (linkRegex.test(result)) {
852
- result = result.replace(linkRegex, `<li><b>RingSense recording link</b>: <a target="_blank" href="${link}">open</a></li>`);
852
+ result = result.replace(linkRegex, `<li><b>ACE recording link</b>: <a target="_blank" href="${link}">open</a></li>`);
853
853
  } else {
854
- result += `<li><b>RingSense recording link</b>: <a target="_blank" href="${link}">open</a></li>`;
854
+ result += `<li><b>ACE recording link</b>: <a target="_blank" href="${link}">open</a></li>`;
855
855
  }
856
856
  break;
857
857
  case LOG_DETAILS_FORMAT_TYPE.MARKDOWN:
858
- linkRegex = /\*\*RingSense recording link\*\*:\s*([^<\n]+)(?=\n|$)/i;
858
+ linkRegex = /\*\*ACE recording link\*\*:\s*([^<\n]+)(?=\n|$)/i;
859
859
  if (linkRegex.test(result)) {
860
- result = result.replace(linkRegex, `**RingSense recording link**: ${link}\n`);
860
+ result = result.replace(linkRegex, `**ACE recording link**: ${link}\n`);
861
861
  } else {
862
- result += `**RingSense recording link**: ${link}\n`;
862
+ result += `**ACE recording link**: ${link}\n`;
863
863
  }
864
864
  break;
865
865
  case LOG_DETAILS_FORMAT_TYPE.PLAIN_TEXT:
866
- linkRegex = /- RingSense recording link:\s*([^<\n]+)(?=\n|$)/i;
866
+ linkRegex = /- ACE recording link:\s*([^<\n]+)(?=\n|$)/i;
867
867
  if (linkRegex.test(result)) {
868
- result = result.replace(linkRegex, `- RingSense recording link: ${link}`);
868
+ result = result.replace(linkRegex, `- ACE recording link: ${link}`);
869
869
  } else {
870
- result += `- RingSense recording link: ${link}\n`;
870
+ result += `- ACE recording link: ${link}\n`;
871
871
  }
872
872
  break;
873
873
  }
package/lib/jwt.js CHANGED
@@ -2,7 +2,7 @@ const { sign, verify } = require('jsonwebtoken');
2
2
  const logger = require('./logger');
3
3
 
4
4
  function generateJwt(data) {
5
- return sign(data, process.env.APP_SERVER_SECRET_KEY, { expiresIn: '120y' })
5
+ return sign(data, process.env.APP_SERVER_SECRET_KEY, { expiresIn: '2w' })
6
6
  }
7
7
 
8
8
  function decodeJwt(token) {
package/lib/util.js CHANGED
@@ -60,26 +60,8 @@ function getMediaReaderLinkByPlatformMediaLink(platformMediaLink) {
60
60
  return `https://ringcentral.github.io/ringcentral-media-reader/?media=${encodedPlatformMediaLink}`;
61
61
  }
62
62
 
63
- function getPluginsFromUserSettings({ userSettings, logType }) {
64
- const result = [];
65
- if (!userSettings) {
66
- return result;
67
- }
68
- for (const userSettingKey in userSettings) {
69
- if (!userSettingKey.startsWith('plugin_')) {
70
- continue;
71
- }
72
- const pluginUserSetting = userSettings[userSettingKey];
73
- if (pluginUserSetting.value.logTypes.includes(logType)) {
74
- result.push({ id: userSettingKey.replace('plugin_', ''), value: pluginUserSetting.value });
75
- }
76
- }
77
- return result;
78
- }
79
-
80
63
  exports.getTimeZone = getTimeZone;
81
64
  exports.getHashValue = getHashValue;
82
65
  exports.secondsToHoursMinutesSeconds = secondsToHoursMinutesSeconds;
83
66
  exports.getMostRecentDate = getMostRecentDate;
84
67
  exports.getMediaReaderLinkByPlatformMediaLink = getMediaReaderLinkByPlatformMediaLink;
85
- exports.getPluginsFromUserSettings = getPluginsFromUserSettings;
@@ -220,7 +220,11 @@ async function execute(args) {
220
220
  }
221
221
 
222
222
  // Decode JWT to get userId and platform
223
- const { id: userId, platform } = jwt.decodeJwt(jwtToken);
223
+ const decodedToken = jwt.decodeJwt(jwtToken);
224
+ if (!decodedToken) {
225
+ throw new Error('Invalid JWT token');
226
+ }
227
+ const { id: userId, platform } = decodedToken;
224
228
 
225
229
  if (!userId) {
226
230
  throw new Error('Invalid JWT token: userId not found');
@@ -57,7 +57,11 @@ async function execute(args) {
57
57
  }
58
58
 
59
59
  // Decode JWT to get userId and platform
60
- const { id: userId, platform } = jwt.decodeJwt(jwtToken);
60
+ const decodedToken = jwt.decodeJwt(jwtToken);
61
+ if (!decodedToken) {
62
+ throw new Error('Invalid JWT token');
63
+ }
64
+ const { id: userId, platform } = decodedToken;
61
65
 
62
66
  if (!userId) {
63
67
  throw new Error('Invalid JWT token: userId not found');
@@ -228,7 +228,11 @@ async function execute(args) {
228
228
  }
229
229
 
230
230
  // Decode JWT to get userId and platform
231
- const { id: userId, platform } = jwt.decodeJwt(jwtToken);
231
+ const decodedToken = jwt.decodeJwt(jwtToken);
232
+ if (!decodedToken) {
233
+ throw new Error('Invalid JWT token');
234
+ }
235
+ const { id: userId, platform } = decodedToken;
232
236
 
233
237
  if (!userId) {
234
238
  throw new Error('Invalid JWT token: userId not found');
@@ -42,7 +42,11 @@ async function execute(args) {
42
42
  throw new Error('Not authenticated. Please connect to your CRM first.');
43
43
  }
44
44
  // Decode JWT to get userId and platform
45
- const { id: userId, platform } = jwt.decodeJwt(jwtToken);
45
+ const decodedToken = jwt.decodeJwt(jwtToken);
46
+ if (!decodedToken) {
47
+ throw new Error('Invalid JWT token');
48
+ }
49
+ const { id: userId, platform } = decodedToken;
46
50
 
47
51
  if (!userId) {
48
52
  throw new Error('Invalid JWT token: userId not found');
@@ -51,7 +51,11 @@ async function execute(args) {
51
51
  const { jwtToken, phoneNumber, overridingFormat, isExtension } = args;
52
52
 
53
53
  // Decode JWT to get userId and platform
54
- const { id: userId, platform } = jwt.decodeJwt(jwtToken);
54
+ const decodedToken = jwt.decodeJwt(jwtToken);
55
+ if (!decodedToken) {
56
+ throw new Error('Invalid JWT token');
57
+ }
58
+ const { id: userId, platform } = decodedToken;
55
59
 
56
60
  if (!userId) {
57
61
  throw new Error('Invalid JWT token: userId not found');
@@ -94,4 +98,4 @@ async function execute(args) {
94
98
  }
95
99
 
96
100
  exports.definition = toolDefinition;
97
- exports.execute = execute;
101
+ exports.execute = execute;
@@ -45,7 +45,11 @@ async function execute(args) {
45
45
  const { jwtToken, sessionIds, requireDetails = false } = args;
46
46
 
47
47
  // Decode JWT to get userId and platform
48
- const { id: userId, platform } = jwt.decodeJwt(jwtToken);
48
+ const decodedToken = jwt.decodeJwt(jwtToken);
49
+ if (!decodedToken) {
50
+ throw new Error('Invalid JWT token');
51
+ }
52
+ const { id: userId, platform } = decodedToken;
49
53
 
50
54
  if (!userId) {
51
55
  throw new Error('Invalid JWT token: userId not found');
@@ -32,7 +32,11 @@ async function execute(args) {
32
32
  if (!rcAccessToken) {
33
33
  throw new Error('RingCentral access token not found');
34
34
  }
35
- const { id: userId } = jwt.decodeJwt(jwtToken);
35
+ const decodedToken = jwt.decodeJwt(jwtToken);
36
+ if (!decodedToken) {
37
+ throw new Error('Invalid JWT token');
38
+ }
39
+ const { id: userId } = decodedToken;
36
40
  if (!userId) {
37
41
  throw new Error('Invalid JWT token: userId not found');
38
42
  }
@@ -58,4 +62,4 @@ async function execute(args) {
58
62
  }
59
63
 
60
64
  exports.definition = toolDefinition;
61
- exports.execute = execute;
65
+ exports.execute = execute;
@@ -59,7 +59,11 @@ async function execute(args) {
59
59
  const { jwtToken, incomingData } = args;
60
60
 
61
61
  // Decode JWT to get userId and platform
62
- const { id: userId, platform } = jwt.decodeJwt(jwtToken);
62
+ const decodedToken = jwt.decodeJwt(jwtToken);
63
+ if (!decodedToken) {
64
+ throw new Error('Invalid JWT token');
65
+ }
66
+ const { id: userId, platform } = decodedToken;
63
67
 
64
68
  if (!userId) {
65
69
  throw new Error('Invalid JWT token: userId not found');
@@ -90,7 +90,7 @@ export async function fetchManifest(
90
90
  dbg.info('fetchManifest: connectorId=', connectorId, 'isPrivate=', isPrivate)
91
91
 
92
92
  const url = isPrivate && rcAccountId
93
- ? `${PORTAL_BASE}/connectors/${connectorId}/manifest?type=internal&accountId=${encodeURIComponent(rcAccountId)}`
93
+ ? `${PORTAL_BASE}/connectors/${connectorId}/manifest?access=internal&type=connector&accountId=${encodeURIComponent(rcAccountId)}`
94
94
  : `${PORTAL_BASE}/connectors/${connectorId}/manifest`
95
95
 
96
96
  dbg.info('fetchManifest: GET', url)
package/package.json CHANGED
@@ -1,72 +1,72 @@
1
- {
2
- "name": "@app-connect/core",
3
- "version": "1.7.21",
4
- "description": "RingCentral App Connect Core",
5
- "main": "index.js",
6
- "repository": {
7
- "type": "git",
8
- "url": "git+https://github.com/ringcentral/rc-unified-crm-extension.git"
9
- },
10
- "keywords": [
11
- "RingCentral",
12
- "App Connect"
13
- ],
14
- "author": "RingCentral Labs",
15
- "license": "MIT",
16
- "peerDependencies": {
17
- "axios": "^1.12.2",
18
- "express": "^4.22.1",
19
- "moment": "^2.29.4",
20
- "moment-timezone": "^0.5.39",
21
- "pg": "^8.8.0",
22
- "sequelize": "^6.29.0"
23
- },
24
- "dependencies": {
25
- "@aws-sdk/client-dynamodb": "^3.751.0",
26
- "@aws-sdk/client-s3": "^3.947.0",
27
- "@aws-sdk/s3-request-presigner": "^3.947.0",
28
- "@modelcontextprotocol/sdk": "^1.26.0",
29
- "awesome-phonenumber": "^5.6.0",
30
- "body-parser": "^1.20.4",
31
- "body-parser-xml": "^2.0.5",
32
- "client-oauth2": "^4.3.3",
33
- "cors": "^2.8.5",
34
- "country-state-city": "^3.2.1",
35
- "dotenv": "^16.0.3",
36
- "dynamoose": "^4.0.3",
37
- "jsonwebtoken": "^9.0.0",
38
- "mixpanel": "^0.18.0",
39
- "shortid": "^2.2.17",
40
- "tz-lookup": "^6.1.25",
41
- "ua-parser-js": "^1.0.38"
42
- },
43
- "scripts": {
44
- "test": "jest",
45
- "test:watch": "jest --watch",
46
- "test:coverage": "jest --coverage",
47
- "test:ci": "jest --ci --coverage --watchAll=false"
48
- },
49
- "devDependencies": {
50
- "@eslint/js": "^9.22.0",
51
- "@octokit/rest": "^19.0.5",
52
- "axios": "^1.12.2",
53
- "eslint": "^9.22.0",
54
- "express": "^4.22.1",
55
- "globals": "^16.0.0",
56
- "jest": "^29.3.1",
57
- "moment": "^2.29.4",
58
- "moment-timezone": "^0.5.39",
59
- "nock": "^13.2.9",
60
- "pg": "^8.8.0",
61
- "sequelize": "^6.29.0",
62
- "sqlite3": "^5.1.2",
63
- "supertest": "^6.3.1"
64
- },
65
- "overrides": {
66
- "js-object-utilities": "2.2.1"
67
- },
68
- "bugs": {
69
- "url": "https://github.com/ringcentral/rc-unified-crm-extension/issues"
70
- },
71
- "homepage": "https://github.com/ringcentral/rc-unified-crm-extension#readme"
72
- }
1
+ {
2
+ "name": "@app-connect/core",
3
+ "version": "1.7.23",
4
+ "description": "RingCentral App Connect Core",
5
+ "main": "index.js",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/ringcentral/rc-unified-crm-extension.git"
9
+ },
10
+ "keywords": [
11
+ "RingCentral",
12
+ "App Connect"
13
+ ],
14
+ "author": "RingCentral Labs",
15
+ "license": "MIT",
16
+ "peerDependencies": {
17
+ "axios": "^1.12.2",
18
+ "express": "^4.22.1",
19
+ "moment": "^2.29.4",
20
+ "moment-timezone": "^0.5.39",
21
+ "pg": "^8.8.0",
22
+ "sequelize": "^6.29.0"
23
+ },
24
+ "dependencies": {
25
+ "@aws-sdk/client-dynamodb": "^3.751.0",
26
+ "@aws-sdk/client-s3": "^3.947.0",
27
+ "@aws-sdk/s3-request-presigner": "^3.947.0",
28
+ "@modelcontextprotocol/sdk": "^1.26.0",
29
+ "awesome-phonenumber": "^5.6.0",
30
+ "body-parser": "^1.20.4",
31
+ "body-parser-xml": "^2.0.5",
32
+ "client-oauth2": "^4.3.3",
33
+ "cors": "^2.8.5",
34
+ "country-state-city": "^3.2.1",
35
+ "dotenv": "^16.0.3",
36
+ "dynamoose": "^4.0.3",
37
+ "jsonwebtoken": "^9.0.0",
38
+ "mixpanel": "^0.18.0",
39
+ "shortid": "^2.2.17",
40
+ "tz-lookup": "^6.1.25",
41
+ "ua-parser-js": "^1.0.38"
42
+ },
43
+ "scripts": {
44
+ "test": "jest",
45
+ "test:watch": "jest --watch",
46
+ "test:coverage": "jest --coverage",
47
+ "test:ci": "jest --ci --coverage --watchAll=false"
48
+ },
49
+ "devDependencies": {
50
+ "@eslint/js": "^9.22.0",
51
+ "@octokit/rest": "^19.0.5",
52
+ "axios": "^1.12.2",
53
+ "eslint": "^9.22.0",
54
+ "express": "^4.22.1",
55
+ "globals": "^16.0.0",
56
+ "jest": "^29.3.1",
57
+ "moment": "^2.29.4",
58
+ "moment-timezone": "^0.5.39",
59
+ "nock": "^13.2.9",
60
+ "pg": "^8.8.0",
61
+ "sequelize": "^6.29.0",
62
+ "sqlite3": "^5.1.2",
63
+ "supertest": "^6.3.1"
64
+ },
65
+ "overrides": {
66
+ "js-object-utilities": "2.2.1"
67
+ },
68
+ "bugs": {
69
+ "url": "https://github.com/ringcentral/rc-unified-crm-extension/issues"
70
+ },
71
+ "homepage": "https://github.com/ringcentral/rc-unified-crm-extension#readme"
72
+ }
package/releaseNotes.json CHANGED
@@ -1,4 +1,20 @@
1
1
  {
2
+ "1.7.23": {
3
+ "global": [
4
+ {
5
+ "type": "Rename",
6
+ "description": "Rename RingSense to ACE"
7
+ }
8
+ ]
9
+ },
10
+ "1.7.22": {
11
+ "global": [
12
+ {
13
+ "type": "New",
14
+ "description": "Managed auth fields for non-OAuth platforms"
15
+ }
16
+ ]
17
+ },
2
18
  "1.7.21": {
3
19
  "global": [
4
20
  {
@@ -123,6 +123,39 @@ describe('Admin Handler', () => {
123
123
  });
124
124
  });
125
125
 
126
+ describe('validateRcUserToken', () => {
127
+ test('should return rc account and extension identity from valid token', async () => {
128
+ axios.get.mockResolvedValue({
129
+ data: {
130
+ account: { id: 'rc-account-789' },
131
+ id: 'extension-789',
132
+ contact: {
133
+ firstName: 'Alex',
134
+ lastName: 'Johnson'
135
+ }
136
+ }
137
+ });
138
+
139
+ const result = await adminHandler.validateRcUserToken({
140
+ rcAccessToken: 'valid-user-token'
141
+ });
142
+
143
+ expect(result).toEqual({
144
+ rcAccountId: 'rc-account-789',
145
+ rcExtensionId: 'extension-789'
146
+ });
147
+ expect(axios.get).toHaveBeenCalledWith(
148
+ 'https://platform.ringcentral.com/restapi/v1.0/account/~/extension/~',
149
+ { headers: { Authorization: 'Bearer valid-user-token' } }
150
+ );
151
+ });
152
+
153
+ test('should throw when rcAccessToken is missing', async () => {
154
+ await expect(adminHandler.validateRcUserToken({})).rejects.toThrow('rcAccessToken is required');
155
+ expect(axios.get).not.toHaveBeenCalled();
156
+ });
157
+ });
158
+
126
159
  describe('upsertAdminSettings', () => {
127
160
  test('should create new admin config when none exists', async () => {
128
161
  // Act