@app-connect/core 1.7.37 → 1.7.38

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 (286) hide show
  1. package/README.md +17 -16
  2. package/dist/connector/developerPortal.d.ts +6 -0
  3. package/{connector → dist/connector}/developerPortal.js +29 -12
  4. package/dist/connector/mock.d.ts +1 -0
  5. package/{connector → dist/connector}/mock.js +33 -17
  6. package/dist/connector/proxy/engine.d.ts +10 -0
  7. package/dist/connector/proxy/engine.js +222 -0
  8. package/dist/connector/proxy/index.d.ts +5 -0
  9. package/dist/connector/proxy/index.js +511 -0
  10. package/dist/connector/registry.d.ts +1 -0
  11. package/dist/connector/registry.js +243 -0
  12. package/dist/handlers/admin.d.ts +1 -0
  13. package/{handlers → dist/handlers}/admin.js +137 -78
  14. package/dist/handlers/appointment.d.ts +9 -0
  15. package/{handlers → dist/handlers}/appointment.js +58 -29
  16. package/dist/handlers/auth.d.ts +9 -0
  17. package/{handlers → dist/handlers}/auth.js +68 -40
  18. package/dist/handlers/calldown.d.ts +10 -0
  19. package/dist/handlers/calldown.js +134 -0
  20. package/dist/handlers/contact.d.ts +1 -0
  21. package/{handlers → dist/handlers}/contact.js +61 -39
  22. package/dist/handlers/disposition.d.ts +1 -0
  23. package/{handlers → dist/handlers}/disposition.js +20 -10
  24. package/dist/handlers/log.d.ts +1 -0
  25. package/{handlers → dist/handlers}/log.js +91 -124
  26. package/dist/handlers/managedAuth.d.ts +1 -0
  27. package/{handlers → dist/handlers}/managedAuth.js +134 -61
  28. package/dist/handlers/managedOAuth.d.ts +13 -0
  29. package/{handlers → dist/handlers}/managedOAuth.js +102 -30
  30. package/dist/handlers/plugin.d.ts +1 -0
  31. package/{handlers → dist/handlers}/plugin.js +59 -27
  32. package/dist/handlers/user.d.ts +1 -0
  33. package/{handlers → dist/handlers}/user.js +45 -27
  34. package/dist/index.d.ts +1 -0
  35. package/{index.js → dist/index.js} +538 -449
  36. package/dist/jest.config.d.ts +1 -0
  37. package/dist/jest.config.js +65 -0
  38. package/dist/lib/analytics.d.ts +4 -0
  39. package/{lib → dist/lib}/analytics.js +8 -6
  40. package/dist/lib/authSession.d.ts +5 -0
  41. package/{lib → dist/lib}/authSession.js +15 -43
  42. package/dist/lib/cacheCleanup.d.ts +4 -0
  43. package/{lib → dist/lib}/cacheCleanup.js +5 -17
  44. package/dist/lib/callLogComposer.d.ts +109 -0
  45. package/{lib → dist/lib}/callLogComposer.js +157 -197
  46. package/dist/lib/callLogLookup.d.ts +6 -0
  47. package/{lib → dist/lib}/callLogLookup.js +12 -34
  48. package/dist/lib/constants.d.ts +6 -0
  49. package/dist/lib/constants.js +9 -0
  50. package/dist/lib/debugTracer.d.ts +17 -0
  51. package/{lib → dist/lib}/debugTracer.js +7 -83
  52. package/dist/lib/encode.d.ts +3 -0
  53. package/dist/lib/encode.js +50 -0
  54. package/dist/lib/errorHandler.d.ts +7 -0
  55. package/dist/lib/errorHandler.js +161 -0
  56. package/dist/lib/generalErrorMessage.d.ts +7 -0
  57. package/{lib → dist/lib}/generalErrorMessage.js +6 -6
  58. package/dist/lib/jwt.d.ts +4 -0
  59. package/dist/lib/jwt.js +18 -0
  60. package/dist/lib/logger.d.ts +24 -0
  61. package/dist/lib/logger.js +126 -0
  62. package/dist/lib/migrateCallLogsSchema.d.ts +32 -0
  63. package/{lib → dist/lib}/migrateCallLogsSchema.js +75 -160
  64. package/dist/lib/oauth.d.ts +4 -0
  65. package/{lib → dist/lib}/oauth.js +157 -160
  66. package/dist/lib/ringcentral.d.ts +24 -0
  67. package/dist/lib/ringcentral.js +226 -0
  68. package/dist/lib/s3ErrorLogReport.d.ts +4 -0
  69. package/{lib → dist/lib}/s3ErrorLogReport.js +11 -23
  70. package/dist/lib/sharedSMSComposer.d.ts +7 -0
  71. package/{lib → dist/lib}/sharedSMSComposer.js +21 -107
  72. package/dist/lib/util.d.ts +7 -0
  73. package/{lib → dist/lib}/util.js +9 -20
  74. package/dist/mcp/lib/oauthError.d.ts +1 -0
  75. package/dist/mcp/lib/oauthError.js +25 -0
  76. package/dist/mcp/lib/validator.d.ts +1 -0
  77. package/{mcp → dist/mcp}/lib/validator.js +16 -20
  78. package/dist/mcp/lib/widgetSessionToken.d.ts +1 -0
  79. package/dist/mcp/lib/widgetSessionToken.js +46 -0
  80. package/dist/mcp/mcpHandler.d.ts +8 -0
  81. package/{mcp → dist/mcp}/mcpHandler.js +98 -88
  82. package/dist/mcp/tools/cancelAppointment.d.ts +1 -0
  83. package/{mcp → dist/mcp}/tools/cancelAppointment.js +7 -16
  84. package/dist/mcp/tools/checkAuthStatus.d.ts +1 -0
  85. package/{mcp → dist/mcp}/tools/checkAuthStatus.js +21 -14
  86. package/dist/mcp/tools/confirmAppointment.d.ts +1 -0
  87. package/{mcp → dist/mcp}/tools/confirmAppointment.js +7 -16
  88. package/dist/mcp/tools/createAppointment.d.ts +1 -0
  89. package/{mcp → dist/mcp}/tools/createAppointment.js +7 -19
  90. package/dist/mcp/tools/createCallLog.d.ts +1 -0
  91. package/{mcp → dist/mcp}/tools/createCallLog.js +18 -29
  92. package/dist/mcp/tools/createContact.d.ts +1 -0
  93. package/{mcp → dist/mcp}/tools/createContact.js +8 -20
  94. package/dist/mcp/tools/createMessageLog.d.ts +1 -0
  95. package/{mcp → dist/mcp}/tools/createMessageLog.js +14 -26
  96. package/dist/mcp/tools/doAuth.d.ts +1 -0
  97. package/{mcp → dist/mcp}/tools/doAuth.js +17 -13
  98. package/dist/mcp/tools/findContactByName.d.ts +1 -0
  99. package/{mcp → dist/mcp}/tools/findContactByName.js +9 -15
  100. package/dist/mcp/tools/findContactByPhone.d.ts +1 -0
  101. package/{mcp → dist/mcp}/tools/findContactByPhone.js +8 -15
  102. package/dist/mcp/tools/getCallLog.d.ts +1 -0
  103. package/{mcp → dist/mcp}/tools/getCallLog.js +12 -21
  104. package/dist/mcp/tools/getGoogleFilePicker.d.ts +1 -0
  105. package/{mcp → dist/mcp}/tools/getGoogleFilePicker.js +9 -18
  106. package/dist/mcp/tools/getHelp.d.ts +1 -0
  107. package/{mcp → dist/mcp}/tools/getHelp.js +5 -6
  108. package/dist/mcp/tools/getPublicConnectors.d.ts +1 -0
  109. package/{mcp → dist/mcp}/tools/getPublicConnectors.js +19 -18
  110. package/dist/mcp/tools/getSessionInfo.d.ts +1 -0
  111. package/{mcp → dist/mcp}/tools/getSessionInfo.js +10 -16
  112. package/dist/mcp/tools/index.d.ts +8 -0
  113. package/dist/mcp/tools/index.js +50 -0
  114. package/dist/mcp/tools/listAppointments.d.ts +1 -0
  115. package/{mcp → dist/mcp}/tools/listAppointments.js +7 -19
  116. package/dist/mcp/tools/logout.d.ts +1 -0
  117. package/{mcp → dist/mcp}/tools/logout.js +16 -14
  118. package/dist/mcp/tools/rcGetCallLogs.d.ts +1 -0
  119. package/{mcp → dist/mcp}/tools/rcGetCallLogs.js +10 -8
  120. package/dist/mcp/tools/updateAppointment.d.ts +1 -0
  121. package/{mcp → dist/mcp}/tools/updateAppointment.js +17 -22
  122. package/dist/mcp/tools/updateCallLog.d.ts +1 -0
  123. package/{mcp → dist/mcp}/tools/updateCallLog.js +16 -26
  124. package/dist/models/accountDataModel.d.ts +3 -0
  125. package/dist/models/accountDataModel.js +39 -0
  126. package/dist/models/adminConfigModel.d.ts +2 -0
  127. package/dist/models/adminConfigModel.js +39 -0
  128. package/dist/models/cacheModel.d.ts +2 -0
  129. package/{models → dist/models}/cacheModel.js +7 -3
  130. package/dist/models/callDownListModel.d.ts +2 -0
  131. package/{models → dist/models}/callDownListModel.js +8 -4
  132. package/dist/models/callLogModel.d.ts +2 -0
  133. package/dist/models/callLogModel.js +43 -0
  134. package/dist/models/dynamo/connectorSchema.d.ts +2 -0
  135. package/dist/models/dynamo/connectorSchema.js +141 -0
  136. package/dist/models/dynamo/lockSchema.d.ts +2 -0
  137. package/{models → dist/models}/dynamo/lockSchema.js +5 -7
  138. package/dist/models/dynamo/noteCacheSchema.d.ts +2 -0
  139. package/{models → dist/models}/dynamo/noteCacheSchema.js +4 -6
  140. package/dist/models/llmSessionModel.d.ts +2 -0
  141. package/dist/models/llmSessionModel.js +21 -0
  142. package/dist/models/messageLogModel.d.ts +2 -0
  143. package/{models → dist/models}/messageLogModel.js +8 -4
  144. package/dist/models/sequelize.d.ts +2 -0
  145. package/dist/models/sequelize.js +68 -0
  146. package/dist/models/userModel.d.ts +2 -0
  147. package/dist/models/userModel.js +49 -0
  148. package/dist/package.json +100 -0
  149. package/dist/releaseNotes.json +1222 -0
  150. package/dist/tsconfig.json +14 -0
  151. package/dist/types/admin.d.ts +141 -0
  152. package/dist/types/analytics.d.ts +14 -0
  153. package/dist/types/appointment.d.ts +50 -0
  154. package/dist/types/auth.d.ts +137 -0
  155. package/dist/types/authSession.d.ts +35 -0
  156. package/dist/types/cacheCleanup.d.ts +12 -0
  157. package/dist/types/calldown.d.ts +54 -0
  158. package/dist/types/common.d.ts +46 -0
  159. package/dist/types/connector.d.ts +165 -0
  160. package/dist/types/contact.d.ts +125 -0
  161. package/dist/types/errorHandler.d.ts +60 -0
  162. package/dist/types/index.d.ts +76 -0
  163. package/dist/types/json.d.ts +9 -0
  164. package/dist/types/jwt.d.ts +8 -0
  165. package/dist/types/logging.d.ts +224 -0
  166. package/dist/types/managedAuth.d.ts +114 -0
  167. package/dist/types/managedOAuth.d.ts +59 -0
  168. package/dist/types/manifest.d.ts +120 -0
  169. package/dist/types/migration.d.ts +15 -0
  170. package/dist/types/plugin.d.ts +74 -0
  171. package/dist/types/ringcentral.d.ts +61 -0
  172. package/dist/types/s3ErrorLogReport.d.ts +16 -0
  173. package/dist/types/sharedSMSComposer.d.ts +105 -0
  174. package/dist/types/user.d.ts +46 -0
  175. package/dist/types/util.d.ts +7 -0
  176. package/package.json +34 -6
  177. package/releaseNotes.json +8 -0
  178. package/.env.test +0 -5
  179. package/connector/proxy/engine.js +0 -165
  180. package/connector/proxy/index.js +0 -500
  181. package/connector/registry.js +0 -252
  182. package/docs/README.md +0 -50
  183. package/docs/architecture.md +0 -94
  184. package/docs/connectors.md +0 -116
  185. package/docs/handlers.md +0 -128
  186. package/docs/libraries.md +0 -98
  187. package/docs/models.md +0 -146
  188. package/docs/routes.md +0 -119
  189. package/docs/tests.md +0 -73
  190. package/handlers/calldown.js +0 -99
  191. package/jest.config.js +0 -57
  192. package/lib/constants.js +0 -9
  193. package/lib/encode.js +0 -30
  194. package/lib/errorHandler.js +0 -218
  195. package/lib/jwt.js +0 -18
  196. package/lib/logger.js +0 -190
  197. package/lib/ringcentral.js +0 -267
  198. package/mcp/README.md +0 -420
  199. package/mcp/tools/index.js +0 -51
  200. package/mcp/ui/App/App.tsx +0 -358
  201. package/mcp/ui/App/components/AuthInfoForm.tsx +0 -113
  202. package/mcp/ui/App/components/AuthSuccess.tsx +0 -22
  203. package/mcp/ui/App/components/ConnectorList.tsx +0 -82
  204. package/mcp/ui/App/components/DebugPanel.tsx +0 -43
  205. package/mcp/ui/App/components/OAuthConnect.tsx +0 -270
  206. package/mcp/ui/App/lib/callTool.ts +0 -130
  207. package/mcp/ui/App/lib/debugLog.ts +0 -41
  208. package/mcp/ui/App/lib/developerPortal.ts +0 -111
  209. package/mcp/ui/App/main.css +0 -6
  210. package/mcp/ui/App/root.tsx +0 -13
  211. package/mcp/ui/dist/index.html +0 -53
  212. package/mcp/ui/vite.config.ts +0 -16
  213. package/models/accountDataModel.js +0 -34
  214. package/models/adminConfigModel.js +0 -35
  215. package/models/callLogModel.js +0 -39
  216. package/models/dynamo/connectorSchema.js +0 -146
  217. package/models/llmSessionModel.js +0 -17
  218. package/models/sequelize.js +0 -17
  219. package/models/userModel.js +0 -45
  220. package/test/connector/developerPortal.test.js +0 -166
  221. package/test/connector/mock.test.js +0 -131
  222. package/test/connector/proxy/engine.test.js +0 -211
  223. package/test/connector/proxy/index.test.js +0 -525
  224. package/test/connector/proxy/sample.json +0 -162
  225. package/test/connector/registry.test.js +0 -416
  226. package/test/handlers/admin.test.js +0 -960
  227. package/test/handlers/appointment.test.js +0 -260
  228. package/test/handlers/auth.test.js +0 -1096
  229. package/test/handlers/calldown.test.js +0 -310
  230. package/test/handlers/contact.test.js +0 -1014
  231. package/test/handlers/disposition.test.js +0 -396
  232. package/test/handlers/log.test.js +0 -1847
  233. package/test/handlers/managedAuth.test.js +0 -458
  234. package/test/handlers/managedOAuth.test.js +0 -262
  235. package/test/handlers/plugin.test.js +0 -429
  236. package/test/handlers/user.test.js +0 -381
  237. package/test/index.test.js +0 -301
  238. package/test/lib/analytics.test.js +0 -146
  239. package/test/lib/authSession.test.js +0 -173
  240. package/test/lib/cacheCleanup.test.js +0 -127
  241. package/test/lib/callLogComposer.test.js +0 -1231
  242. package/test/lib/debugTracer.test.js +0 -328
  243. package/test/lib/encode.test.js +0 -59
  244. package/test/lib/errorHandler.test.js +0 -246
  245. package/test/lib/generalErrorMessage.test.js +0 -82
  246. package/test/lib/jwt.test.js +0 -176
  247. package/test/lib/logger.test.js +0 -206
  248. package/test/lib/oauth.test.js +0 -359
  249. package/test/lib/ringcentral.test.js +0 -467
  250. package/test/lib/s3ErrorLogReport.test.js +0 -187
  251. package/test/lib/sharedSMSComposer.test.js +0 -1084
  252. package/test/lib/util.test.js +0 -329
  253. package/test/mcp/mcpHandler.test.js +0 -73
  254. package/test/mcp/mcpHandlerMore.test.js +0 -384
  255. package/test/mcp/tools/appointmentTools.test.js +0 -362
  256. package/test/mcp/tools/checkAuthStatus.test.js +0 -83
  257. package/test/mcp/tools/createCallLog.test.js +0 -436
  258. package/test/mcp/tools/createContact.test.js +0 -58
  259. package/test/mcp/tools/createMessageLog.test.js +0 -595
  260. package/test/mcp/tools/doAuth.test.js +0 -113
  261. package/test/mcp/tools/findContactByName.test.js +0 -275
  262. package/test/mcp/tools/findContactByPhone.test.js +0 -296
  263. package/test/mcp/tools/getCallLog.test.js +0 -298
  264. package/test/mcp/tools/getGoogleFilePicker.test.js +0 -281
  265. package/test/mcp/tools/getPublicConnectors.test.js +0 -107
  266. package/test/mcp/tools/getSessionInfo.test.js +0 -127
  267. package/test/mcp/tools/logout.test.js +0 -233
  268. package/test/mcp/tools/rcGetCallLogs.test.js +0 -56
  269. package/test/mcp/tools/updateCallLog.test.js +0 -360
  270. package/test/models/accountDataModel.test.js +0 -98
  271. package/test/models/callDownListModel.test.js +0 -125
  272. package/test/models/dynamo/connectorSchema.test.js +0 -189
  273. package/test/models/dynamo/lockSchema.test.js +0 -37
  274. package/test/models/dynamo/noteCacheSchema.test.js +0 -45
  275. package/test/models/llmSessionModel.test.js +0 -91
  276. package/test/models/models.test.js +0 -660
  277. package/test/routes/calldownRoutes.test.js +0 -224
  278. package/test/routes/coreRouterBroadRoutes.test.js +0 -855
  279. package/test/routes/dispositionRoutes.test.js +0 -192
  280. package/test/routes/managedAuthRoutes.test.js +0 -386
  281. package/test/routes/pluginRoutes.test.js +0 -262
  282. package/test/setup.js +0 -178
  283. /package/{mcp → dist/mcp}/ui/index.html +0 -0
  284. /package/{mcp → dist/mcp}/ui/package-lock.json +0 -0
  285. /package/{mcp → dist/mcp}/ui/package.json +0 -0
  286. /package/{mcp → dist/mcp}/ui/tsconfig.json +0 -0
@@ -1,855 +0,0 @@
1
- const express = require('express');
2
- const request = require('supertest');
3
-
4
- jest.mock('../../handlers/log', () => ({
5
- saveNoteCache: jest.fn(),
6
- getCallLog: jest.fn(),
7
- createCallLog: jest.fn(),
8
- updateCallLog: jest.fn(),
9
- createMessageLog: jest.fn(),
10
- handleAsyncPluginCallback: jest.fn(),
11
- }));
12
- jest.mock('../../handlers/contact', () => ({
13
- findContact: jest.fn(),
14
- createContact: jest.fn(),
15
- findContactWithName: jest.fn(),
16
- }));
17
- jest.mock('../../handlers/appointment', () => ({
18
- listAppointments: jest.fn(),
19
- createAppointment: jest.fn(),
20
- updateAppointment: jest.fn(),
21
- refreshAppointment: jest.fn(),
22
- confirmAppointment: jest.fn(),
23
- cancelAppointment: jest.fn(),
24
- }));
25
- jest.mock('../../handlers/auth', () => ({
26
- getLicenseStatus: jest.fn(),
27
- authValidation: jest.fn(),
28
- onOAuthCallback: jest.fn(),
29
- onApiKeyLogin: jest.fn(),
30
- onRingcentralOAuthCallback: jest.fn(),
31
- }));
32
- jest.mock('../../handlers/admin', () => ({
33
- validateRcUserToken: jest.fn(),
34
- validateAdminRole: jest.fn(),
35
- upsertAdminSettings: jest.fn(),
36
- getAdminSettings: jest.fn(),
37
- getUserMapping: jest.fn(),
38
- reinitializeUserMapping: jest.fn(),
39
- getServerLoggingSettings: jest.fn(),
40
- updateServerLoggingSettings: jest.fn(),
41
- getAdminReport: jest.fn(),
42
- getUserReport: jest.fn(),
43
- }));
44
- jest.mock('../../handlers/user', () => ({
45
- getUserSettingsByAdmin: jest.fn(),
46
- refreshUserInfo: jest.fn(),
47
- getUserSettings: jest.fn(),
48
- updateUserSettings: jest.fn(),
49
- }));
50
- jest.mock('../../handlers/disposition', () => ({
51
- upsertCallDisposition: jest.fn(),
52
- }));
53
- jest.mock('../../handlers/calldown', () => ({
54
- schedule: jest.fn(),
55
- list: jest.fn(),
56
- remove: jest.fn(),
57
- update: jest.fn(),
58
- }));
59
- jest.mock('../../handlers/plugin', () => ({
60
- registerPluginAccount: jest.fn(),
61
- unregisterPluginAccount: jest.fn(),
62
- getPluginLicenseStatus: jest.fn(),
63
- }));
64
- jest.mock('../../handlers/managedAuth', () => ({
65
- getManagedAuthState: jest.fn(),
66
- getManagedAuthAdminSettings: jest.fn(),
67
- upsertUserManagedAuthValues: jest.fn(),
68
- upsertOrgManagedAuthValues: jest.fn(),
69
- }));
70
- jest.mock('../../handlers/managedOAuth', () => ({
71
- getManagedOAuthState: jest.fn(),
72
- upsertPendingManagedOAuth: jest.fn(),
73
- clearPendingManagedOAuth: jest.fn(),
74
- resetManagedOAuth: jest.fn(),
75
- }));
76
- jest.mock('../../connector/mock', () => ({
77
- createUser: jest.fn(),
78
- deleteUser: jest.fn(),
79
- getCallLog: jest.fn(),
80
- createCallLog: jest.fn(),
81
- cleanUpMockLogs: jest.fn(),
82
- }));
83
- jest.mock('../../connector/registry', () => ({
84
- getManifest: jest.fn(),
85
- getReleaseNotes: jest.fn(),
86
- getConnector: jest.fn(),
87
- }));
88
- jest.mock('../../lib/analytics', () => ({
89
- init: jest.fn(),
90
- track: jest.fn(),
91
- }));
92
- jest.mock('../../lib/jwt', () => ({
93
- decodeJwt: jest.fn(),
94
- generateJwt: jest.fn(),
95
- }));
96
- jest.mock('../../lib/util', () => ({
97
- getHashValue: jest.fn((value) => `hash-${value}`),
98
- }));
99
- jest.mock('../../lib/s3ErrorLogReport', () => ({
100
- getUploadUrl: jest.fn(),
101
- }));
102
- jest.mock('../../lib/authSession', () => ({
103
- updateAuthSession: jest.fn(),
104
- }));
105
- jest.mock('../../mcp/mcpHandler', () => ({
106
- handleMcpRequest: jest.fn((req, res) => res.status(200).json({ jsonrpc: '2.0', result: 'mcp-ok' })),
107
- handleWidgetToolCall: jest.fn((req, res) => res.status(200).json({ successful: true })),
108
- }));
109
- jest.mock('../../models/userModel', () => ({
110
- UserModel: {
111
- findByPk: jest.fn(),
112
- },
113
- }));
114
-
115
- const logCore = require('../../handlers/log');
116
- const contactCore = require('../../handlers/contact');
117
- const appointmentCore = require('../../handlers/appointment');
118
- const authCore = require('../../handlers/auth');
119
- const adminCore = require('../../handlers/admin');
120
- const userCore = require('../../handlers/user');
121
- const dispositionCore = require('../../handlers/disposition');
122
- const calldown = require('../../handlers/calldown');
123
- const pluginCore = require('../../handlers/plugin');
124
- const managedAuthCore = require('../../handlers/managedAuth');
125
- const managedOAuthCore = require('../../handlers/managedOAuth');
126
- const mockConnector = require('../../connector/mock');
127
- const connectorRegistry = require('../../connector/registry');
128
- const analytics = require('../../lib/analytics');
129
- const jwt = require('../../lib/jwt');
130
- const s3ErrorLogReport = require('../../lib/s3ErrorLogReport');
131
- const { updateAuthSession } = require('../../lib/authSession');
132
- const mcpHandler = require('../../mcp/mcpHandler');
133
- const { UserModel } = require('../../models/userModel');
134
- const {
135
- createCoreRouter,
136
- createCoreApp,
137
- createCoreMiddleware,
138
- initializeCore,
139
- } = require('../../index');
140
-
141
- describe('Core router broad route coverage', () => {
142
- const decodedJwt = {
143
- id: 'user-1',
144
- platform: 'testCRM',
145
- exp: Math.floor(Date.now() / 1000) + 60 * 60,
146
- };
147
- const mockUser = {
148
- id: 'user-1',
149
- platform: 'testCRM',
150
- hostname: 'crm.example.com',
151
- rcAccountId: 'rc-account-1',
152
- userSettings: {},
153
- };
154
- let app;
155
-
156
- function authQuery() {
157
- return { jwtToken: 'valid-crm-jwt' };
158
- }
159
-
160
- beforeEach(() => {
161
- jest.clearAllMocks();
162
- process.env.HASH_KEY = 'hash-key';
163
- process.env.APP_SERVER = 'https://app.example.com';
164
- process.env.RINGCENTRAL_SERVER = 'https://platform.example.com';
165
- process.env.RINGCENTRAL_CLIENT_ID = 'rc-client-id';
166
- process.env.RINGCENTRAL_CLIENT_SECRET = 'rc-client-secret';
167
- process.env.CHATGPT_VERIFICATION_CODE = 'verify-code';
168
- process.env.APP_SERVER_SECRET_KEY = 'secret-key';
169
- process.env.IS_PROD = 'false';
170
-
171
- jwt.decodeJwt.mockReturnValue(decodedJwt);
172
- jwt.generateJwt.mockReturnValue('generated-crm-jwt');
173
- UserModel.findByPk.mockResolvedValue(mockUser);
174
- connectorRegistry.getReleaseNotes.mockReturnValue({
175
- '1.0.0': { testCRM: { notes: ['connector note'] } },
176
- });
177
- connectorRegistry.getManifest.mockReturnValue({
178
- author: { name: 'Test Author' },
179
- version: '1.0.0',
180
- platforms: {
181
- testCRM: {
182
- serverSideLogging: {
183
- url: 'https://logging.example.com',
184
- },
185
- },
186
- },
187
- });
188
- connectorRegistry.getConnector.mockReturnValue({
189
- getAuthType: jest.fn(() => 'oauth'),
190
- getOauthInfo: jest.fn(),
191
- getUserInfo: jest.fn(),
192
- createCallLog: jest.fn(),
193
- updateCallLog: jest.fn(),
194
- getCallLog: jest.fn(),
195
- createMessageLog: jest.fn(),
196
- updateMessageLog: jest.fn(),
197
- createContact: jest.fn(),
198
- findContact: jest.fn(),
199
- listAppointments: jest.fn(),
200
- createAppointment: jest.fn(),
201
- updateAppointment: jest.fn(),
202
- refreshAppointment: jest.fn(),
203
- confirmAppointment: jest.fn(),
204
- cancelAppointment: jest.fn(),
205
- unAuthorize: jest.fn().mockResolvedValue({
206
- returnMessage: {
207
- messageType: 'success',
208
- message: 'Disconnected',
209
- },
210
- }),
211
- upsertCallDisposition: jest.fn(),
212
- findContactWithName: jest.fn(),
213
- getUserList: jest.fn(),
214
- getLicenseStatus: jest.fn(),
215
- getLogFormatType: jest.fn(),
216
- refreshUserInfo: jest.fn(),
217
- });
218
-
219
- adminCore.validateRcUserToken.mockResolvedValue({
220
- rcAccountId: 'rc-account-1',
221
- rcExtensionId: 'rc-extension-1',
222
- });
223
- adminCore.validateAdminRole.mockResolvedValue({
224
- isValidated: true,
225
- rcAccountId: 'rc-account-1',
226
- });
227
- adminCore.upsertAdminSettings.mockResolvedValue();
228
- adminCore.getAdminSettings.mockResolvedValue({ userSettings: { theme: 'dark' } });
229
- adminCore.getUserMapping.mockResolvedValue({ users: ['mapped-user'] });
230
- adminCore.reinitializeUserMapping.mockResolvedValue({ users: ['remapped-user'] });
231
- adminCore.getServerLoggingSettings.mockResolvedValue({ enabled: true });
232
- adminCore.updateServerLoggingSettings.mockResolvedValue({
233
- successful: true,
234
- returnMessage: { messageType: 'success', message: 'Updated' },
235
- });
236
- adminCore.getAdminReport.mockResolvedValue({ rows: [{ id: 'admin-row' }] });
237
- adminCore.getUserReport.mockResolvedValue({ rows: [{ id: 'user-row' }] });
238
-
239
- managedAuthCore.getManagedAuthState.mockResolvedValue({ hasManagedAuth: true });
240
- managedAuthCore.getManagedAuthAdminSettings.mockResolvedValue({ shared: true });
241
- managedAuthCore.upsertUserManagedAuthValues.mockResolvedValue();
242
- managedAuthCore.upsertOrgManagedAuthValues.mockResolvedValue();
243
- managedOAuthCore.getManagedOAuthState.mockResolvedValue({ isConfigured: true });
244
- managedOAuthCore.upsertPendingManagedOAuth.mockResolvedValue();
245
- managedOAuthCore.clearPendingManagedOAuth.mockResolvedValue();
246
- managedOAuthCore.resetManagedOAuth.mockResolvedValue();
247
-
248
- authCore.getLicenseStatus.mockResolvedValue({ isLicenseValid: true });
249
- authCore.authValidation.mockResolvedValue({
250
- successful: true,
251
- returnMessage: { messageType: 'success', message: 'Valid' },
252
- failReason: '',
253
- status: 200,
254
- });
255
- authCore.onOAuthCallback.mockResolvedValue({
256
- userInfo: { id: 'user-1', name: 'CRM User' },
257
- returnMessage: { messageType: 'success', message: 'Connected' },
258
- });
259
- authCore.onApiKeyLogin.mockResolvedValue({
260
- userInfo: { id: 'user-1', name: 'CRM User' },
261
- returnMessage: { messageType: 'success', message: 'Connected' },
262
- });
263
- authCore.onRingcentralOAuthCallback.mockResolvedValue();
264
-
265
- userCore.getUserSettingsByAdmin.mockResolvedValue({ fields: [] });
266
- userCore.refreshUserInfo.mockResolvedValue({
267
- successful: true,
268
- returnMessage: { messageType: 'success', message: 'Refreshed' },
269
- });
270
- userCore.getUserSettings.mockResolvedValue({ timezone: 'UTC' });
271
- userCore.updateUserSettings.mockResolvedValue({ userSettings: { timezone: 'UTC' } });
272
-
273
- contactCore.findContact.mockResolvedValue({
274
- successful: true,
275
- returnMessage: { messageType: 'success', message: 'Found' },
276
- contact: [{ id: 'contact-1', isNewContact: false }],
277
- extraDataTracking: { source: 'contact' },
278
- });
279
- contactCore.createContact.mockResolvedValue({
280
- successful: true,
281
- returnMessage: { messageType: 'success', message: 'Created' },
282
- contact: { id: 'contact-2' },
283
- extraDataTracking: { source: 'create-contact' },
284
- });
285
- contactCore.findContactWithName.mockResolvedValue({
286
- successful: true,
287
- returnMessage: { messageType: 'success', message: 'Found' },
288
- contact: [{ id: 'contact-3' }],
289
- });
290
-
291
- appointmentCore.listAppointments.mockResolvedValue({
292
- successful: true,
293
- appointments: [{ id: 'appt-1' }],
294
- extraDataTracking: { source: 'appointments' },
295
- });
296
- appointmentCore.createAppointment.mockResolvedValue({
297
- successful: true,
298
- appointmentId: 'appt-2',
299
- appointment: { id: 'appt-2' },
300
- returnMessage: { messageType: 'success', message: 'Created' },
301
- extraDataTracking: { source: 'create-appointment' },
302
- });
303
- appointmentCore.updateAppointment.mockResolvedValue({
304
- successful: true,
305
- appointment: { id: 'appt-2' },
306
- returnMessage: { messageType: 'success', message: 'Updated' },
307
- });
308
- appointmentCore.refreshAppointment.mockResolvedValue({
309
- successful: true,
310
- appointment: { id: 'appt-2' },
311
- returnMessage: { messageType: 'success', message: 'Refreshed' },
312
- });
313
- appointmentCore.confirmAppointment.mockResolvedValue({
314
- successful: true,
315
- appointment: { id: 'appt-2' },
316
- returnMessage: { messageType: 'success', message: 'Confirmed' },
317
- });
318
- appointmentCore.cancelAppointment.mockResolvedValue({
319
- successful: true,
320
- appointment: { id: 'appt-2' },
321
- returnMessage: { messageType: 'success', message: 'Cancelled' },
322
- });
323
-
324
- logCore.saveNoteCache.mockResolvedValue({
325
- successful: true,
326
- returnMessage: { messageType: 'success', message: 'Cached' },
327
- extraDataTracking: { cache: true },
328
- });
329
- logCore.getCallLog.mockResolvedValue({
330
- successful: true,
331
- logs: [{ sessionId: 'session-1' }],
332
- returnMessage: { messageType: 'success', message: 'Found' },
333
- extraDataTracking: { logs: 1 },
334
- });
335
- logCore.createCallLog.mockResolvedValue({
336
- successful: true,
337
- logId: 'log-1',
338
- returnMessage: { messageType: 'success', message: 'Logged' },
339
- extraDataTracking: { created: true },
340
- });
341
- logCore.updateCallLog.mockResolvedValue({
342
- successful: true,
343
- logId: 'log-1',
344
- updatedNote: 'updated',
345
- returnMessage: { messageType: 'success', message: 'Updated' },
346
- extraDataTracking: { updated: true },
347
- });
348
- logCore.createMessageLog.mockResolvedValue({
349
- successful: true,
350
- returnMessage: { messageType: 'success', message: 'Message logged' },
351
- logIds: ['msg-1'],
352
- extraDataTracking: { messages: 1 },
353
- });
354
- logCore.handleAsyncPluginCallback.mockResolvedValue({
355
- statusCode: 202,
356
- body: { successful: true },
357
- });
358
-
359
- dispositionCore.upsertCallDisposition.mockResolvedValue({
360
- successful: true,
361
- returnMessage: { messageType: 'success', message: 'Disposition saved' },
362
- extraDataTracking: { disposition: true },
363
- });
364
- calldown.schedule.mockResolvedValue({ id: 'calldown-1' });
365
- calldown.list.mockResolvedValue({ items: [{ id: 'calldown-1' }] });
366
- calldown.remove.mockResolvedValue();
367
- calldown.update.mockResolvedValue();
368
- pluginCore.registerPluginAccount.mockResolvedValue({ successful: true });
369
- pluginCore.unregisterPluginAccount.mockResolvedValue({ successful: true });
370
- pluginCore.getPluginLicenseStatus.mockResolvedValue({ licenseStatus: true });
371
- mockConnector.createUser.mockResolvedValue({ id: 'mockUser' });
372
- mockConnector.deleteUser.mockResolvedValue(true);
373
- mockConnector.getCallLog.mockResolvedValue([{ sessionId: 'session-1', matched: true }]);
374
- mockConnector.createCallLog.mockResolvedValue();
375
- mockConnector.cleanUpMockLogs.mockResolvedValue();
376
- s3ErrorLogReport.getUploadUrl.mockResolvedValue('https://upload.example.com/report');
377
-
378
- app = express();
379
- app.use(express.json());
380
- app.use('/', createCoreRouter());
381
- });
382
-
383
- afterEach(() => {
384
- delete process.env.IS_PROD;
385
- });
386
-
387
- test('serves manifest, release, implementation, metadata, mock, and MCP routes', async () => {
388
- await expect(request(app).get('/isAlive')).resolves.toMatchObject({ status: 200, text: 'OK' });
389
- expect((await request(app).get('/releaseNotes')).status).toBe(200);
390
- const manifestResponse = await request(app)
391
- .get('/crmManifest')
392
- .query({ platformName: 'testCRM' });
393
- expect(manifestResponse.status).toBe(200);
394
- expect(manifestResponse.body.author.name).toBe('Test Author');
395
- expect((await request(app).get('/serverVersionInfo')).body).toEqual({ version: '1.0.0' });
396
-
397
- const interfacesResponse = await request(app)
398
- .get('/implementedInterfaces')
399
- .query({ platform: 'testCRM' });
400
- expect(interfacesResponse.status).toBe(200);
401
- expect(interfacesResponse.body.createCallLog).toBe(true);
402
-
403
- await expect(request(app).get('/.well-known/openai-apps-challenge')).resolves.toMatchObject({ text: 'verify-code' });
404
- expect((await request(app).get('/.well-known/oauth-protected-resource')).body.resource).toBe('https://app.example.com');
405
- expect((await request(app).get('/.well-known/oauth-authorization-server')).body.registration_endpoint).toBe('https://app.example.com/oauth/register');
406
- expect((await request(app).post('/oauth/register')).body).toEqual({
407
- client_id: 'rc-client-id',
408
- client_secret: 'rc-client-secret',
409
- });
410
- const redirectResponse = await request(app)
411
- .get('/oauth/authorize_shim')
412
- .query({
413
- response_type: 'code',
414
- client_id: 'client-id',
415
- redirect_uri: 'https://chat.example.com/callback',
416
- state: 'state-1',
417
- scope: 'ReadAccounts',
418
- });
419
- expect(redirectResponse.status).toBe(302);
420
- expect(redirectResponse.headers.location).toContain('/restapi/oauth/authorize?');
421
-
422
- await expect(request(app).post('/registerMockUser').query({ secretKey: 'secret-key' }).send({ userName: 'A' })).resolves.toMatchObject({ status: 200 });
423
- await expect(request(app).delete('/deleteMockUser').query({ secretKey: 'secret-key', userName: 'A' })).resolves.toMatchObject({ status: 200 });
424
- await expect(request(app).get('/mockCallLog').query({ secretKey: 'secret-key', sessionIds: 's1' })).resolves.toMatchObject({ status: 200 });
425
- await expect(request(app).post('/mockCallLog').query({ secretKey: 'secret-key' }).send({ sessionId: 's1' })).resolves.toMatchObject({ status: 200 });
426
- await expect(request(app).delete('/mockCallLog').query({ secretKey: 'secret-key' })).resolves.toMatchObject({ status: 200 });
427
-
428
- expect((await request(app).options('/mcp')).status).toBe(200);
429
- expect((await request(app).post('/mcp').send({ method: 'tools/list' })).body).toEqual({ jsonrpc: '2.0', result: 'mcp-ok' });
430
- expect(mcpHandler.handleMcpRequest).toHaveBeenCalled();
431
- expect((await request(app).options('/mcp/widget-tool-call')).status).toBe(200);
432
- expect((await request(app).post('/mcp/widget-tool-call').send({ name: 'tool' })).body).toEqual({ successful: true });
433
- });
434
-
435
- test('covers auth, admin, managed auth, managed OAuth, and user routes', async () => {
436
- expect((await request(app).get('/licenseStatus').query(authQuery())).body).toEqual({ isLicenseValid: true });
437
- expect((await request(app).get('/authValidation').query(authQuery())).body).toEqual({
438
- successful: true,
439
- returnMessage: { messageType: 'success', message: 'Valid' },
440
- });
441
- expect((await request(app).get('/apiKeyManagedAuthState').query({ platform: 'testCRM', rcAccessToken: 'rc-token' })).body).toEqual({ hasManagedAuth: true });
442
- expect((await request(app).get('/oauthManagedAuthState').query({ platform: 'testCRM', rcAccessToken: 'rc-token' })).body).toEqual({ isConfigured: true });
443
-
444
- await expect(request(app).post('/admin/settings').query({ rcAccessToken: 'rc-token' }).send({ adminSettings: { a: 1 } })).resolves.toMatchObject({ status: 200 });
445
- expect((await request(app).get('/admin/settings').query({ ...authQuery(), rcAccessToken: 'rc-token' })).body).toEqual({ userSettings: { theme: 'dark' } });
446
- expect((await request(app).get('/admin/managedAuth').query({ ...authQuery(), rcAccessToken: 'rc-token', connectorId: 'connector-1' })).body).toEqual({ shared: true });
447
- await expect(request(app).post('/admin/managedAuth').query({ ...authQuery(), rcAccessToken: 'rc-token' }).send({ scope: 'user', rcExtensionId: 'ext-1', values: { key: 'value' } })).resolves.toMatchObject({ status: 200 });
448
- await expect(request(app).post('/admin/managedAuth').query({ ...authQuery(), rcAccessToken: 'rc-token' }).send({ scope: 'org', values: { key: 'value' } })).resolves.toMatchObject({ status: 200 });
449
- await expect(request(app).post('/admin/managedOAuth/cache').query({ rcAccessToken: 'rc-token' }).send({ values: { clientId: 'id' } })).resolves.toMatchObject({ status: 200 });
450
- await expect(request(app).delete('/admin/managedOAuth/cache').query({ rcAccessToken: 'rc-token' })).resolves.toMatchObject({ status: 200 });
451
- await expect(request(app).delete('/admin/managedOAuth/account').query({ rcAccessToken: 'rc-token', platform: 'testCRM' })).resolves.toMatchObject({ status: 200 });
452
- expect((await request(app).post('/admin/userMapping').query({ ...authQuery(), rcAccessToken: 'rc-token' }).send({ rcExtensionList: ['100'] })).body).toEqual({ users: ['mapped-user'] });
453
- expect((await request(app).post('/admin/reinitializeUserMapping').query({ ...authQuery(), rcAccessToken: 'rc-token' }).send({ rcExtensionList: ['100'] })).body).toEqual({ users: ['remapped-user'] });
454
- expect((await request(app).get('/admin/serverLoggingSettings').query(authQuery())).body).toEqual({ enabled: true });
455
- expect((await request(app).post('/admin/serverLoggingSettings').query(authQuery()).send({ additionalFieldValues: { enabled: true } })).body).toEqual({
456
- successful: true,
457
- returnMessage: { messageType: 'success', message: 'Updated' },
458
- });
459
-
460
- expect((await request(app).get('/user/preloadSettings').query({ rcAccessToken: 'rc-token' })).body).toEqual({ fields: [] });
461
- expect((await request(app).post('/user/refreshInfo').query(authQuery()).send({})).body).toEqual({
462
- successful: true,
463
- returnMessage: { messageType: 'success', message: 'Refreshed' },
464
- });
465
- expect((await request(app).get('/user/settings').query({ ...authQuery(), rcAccessToken: 'rc-token' })).body).toEqual({ timezone: 'UTC' });
466
- expect((await request(app).post('/user/settings').query(authQuery()).send({ userSettings: { timezone: 'UTC' } })).body).toEqual({ userSettings: { timezone: 'UTC' } });
467
- await expect(request(app).get('/hostname').query(authQuery())).resolves.toMatchObject({ status: 200, text: 'crm.example.com' });
468
- expect((await request(app).get('/userInfoHash').query({ extensionId: 'ext', accountId: 'acc' })).body).toEqual({
469
- extensionId: 'hash-ext',
470
- accountId: 'hash-acc',
471
- });
472
- });
473
-
474
- test('covers login, contact, appointment, log, report, and plugin routes', async () => {
475
- const callbackState = encodeURIComponent('platform=testCRM&hostname=crm.example.com');
476
- const callbackResponse = await request(app)
477
- .get('/oauth-callback')
478
- .query({
479
- callbackUri: `https://redirect.example.com/callback?state=${callbackState}`,
480
- code: 'oauth-code',
481
- });
482
- expect(callbackResponse.body).toEqual({
483
- jwtToken: 'generated-crm-jwt',
484
- name: 'CRM User',
485
- returnMessage: { messageType: 'success', message: 'Connected' },
486
- });
487
-
488
- const mcpState = encodeURIComponent('platform=testCRM&hostname=crm.example.com&sessionId=session-1');
489
- await expect(request(app).get('/oauth-callback').query({
490
- callbackUri: `https://redirect.example.com/callback?state=${mcpState}`,
491
- code: 'oauth-code',
492
- })).resolves.toMatchObject({ status: 200, text: 'Authentication successful. Please go back to AI Agent and confirm it.' });
493
- expect(updateAuthSession).toHaveBeenCalledWith('session-1', expect.objectContaining({ status: 'completed' }));
494
-
495
- expect((await request(app).post('/apiKeyLogin').send({ platform: 'testCRM', apiKey: 'api-key', rcAccessToken: 'rc-token' })).body).toEqual({
496
- jwtToken: 'generated-crm-jwt',
497
- name: 'CRM User',
498
- returnMessage: { messageType: 'success', message: 'Connected' },
499
- });
500
- expect((await request(app).post('/unAuthorize').query(authQuery()).send({})).body).toEqual({
501
- messageType: 'success',
502
- message: 'Disconnected',
503
- });
504
-
505
- expect((await request(app).get('/contact').query({ ...authQuery(), phoneNumber: '+15551234567' })).body).toEqual({
506
- successful: true,
507
- returnMessage: { messageType: 'success', message: 'Found' },
508
- contact: [{ id: 'contact-1', isNewContact: false }],
509
- });
510
- expect((await request(app).post('/contact').query(authQuery()).send({ phoneNumber: '+1555', newContactName: 'Alice' })).body.contact).toEqual({ id: 'contact-2' });
511
- expect((await request(app).get('/custom/contact/search').query({ ...authQuery(), name: 'Alice' })).body.contact).toEqual([{ id: 'contact-3' }]);
512
-
513
- expect((await request(app).get('/appointments').query(authQuery())).body.appointments).toEqual([{ id: 'appt-1' }]);
514
- expect((await request(app).post('/appointments').query(authQuery()).send({ payload: { title: 'Meet' } })).body.appointmentId).toBe('appt-2');
515
- expect((await request(app).patch('/appointments/appt-2').query(authQuery()).send({ patch: { title: 'Updated' } })).body.appointmentId).toBe('appt-2');
516
- expect((await request(app).get('/appointments/appt-2/refresh').query(authQuery())).body.appointmentId).toBe('appt-2');
517
- expect((await request(app).post('/appointments/appt-2/confirm').query(authQuery())).body.appointmentId).toBe('appt-2');
518
- expect((await request(app).post('/appointments/appt-2/cancel').query(authQuery())).body.appointmentId).toBe('appt-2');
519
-
520
- expect((await request(app).post('/callLog/cacheNote').query(authQuery()).send({ sessionId: 's1', note: 'note' })).body.successful).toBe(true);
521
- expect((await request(app).get('/callLog').query({ ...authQuery(), sessionIds: 's1', requireDetails: 'true' })).body.logs).toEqual([{ sessionId: 'session-1' }]);
522
- expect((await request(app).post('/callLog').query(authQuery()).send({ logInfo: { accountId: 'acc' } })).body.logId).toBe('log-1');
523
- expect((await request(app).patch('/callLog').query(authQuery()).send({ accountId: 'acc' })).body.updatedNote).toBe('updated');
524
- expect((await request(app).put('/callDisposition').query(authQuery()).send({ sessionId: 's1', dispositions: ['left voicemail'] })).body.successful).toBe(true);
525
- expect((await request(app).post('/messageLog').query(authQuery()).send({ messages: [] })).body.logIds).toEqual(['msg-1']);
526
-
527
- expect((await request(app).get('/ringcentral/admin/report').query({ ...authQuery(), timezone: 'UTC' })).body).toEqual({ rows: [{ id: 'admin-row' }] });
528
- expect((await request(app).get('/ringcentral/admin/userReport').query({ ...authQuery(), rcExtensionId: 'ext-1' })).body).toEqual({ rows: [{ id: 'user-row' }] });
529
- await expect(request(app).get('/ringcentral/oauth/callback').query({ ...authQuery(), code: 'rc-code' })).resolves.toMatchObject({ status: 200 });
530
- expect((await request(app).get('/debug/report/url').query(authQuery())).body).toEqual({ presignedUrl: 'https://upload.example.com/report' });
531
- expect((await request(app).post('/plugin/async-callback/task-1').send({ successful: true })).body).toEqual({ successful: true });
532
- await expect(request(app).post('/plugin/register').query({ rcAccessToken: 'rc-token' }).send({ pluginId: 'p1', rcAccountId: 'rc-account-1' })).resolves.toMatchObject({ status: 200 });
533
- await expect(request(app).delete('/plugin/unregister').query({ rcAccessToken: 'rc-token', pluginId: 'p1', rcAccountId: 'rc-account-1' })).resolves.toMatchObject({ status: 200 });
534
- expect((await request(app).get('/plugin/licenseStatus').query({ ...authQuery(), rcAccountId: 'rc-account-1', pluginId: 'p1' })).body).toEqual({ licenseStatus: true });
535
- });
536
-
537
- test('covers no-token and missing-parameter validation branches', async () => {
538
- const noTokenCases = [
539
- ['get', '/authValidation'],
540
- ['get', '/admin/settings'],
541
- ['get', '/admin/managedAuth'],
542
- ['post', '/admin/managedAuth', { scope: 'org' }],
543
- ['post', '/admin/userMapping', { rcExtensionList: ['100'] }],
544
- ['post', '/admin/reinitializeUserMapping', { rcExtensionList: ['100'] }],
545
- ['get', '/admin/serverLoggingSettings'],
546
- ['post', '/admin/serverLoggingSettings', { additionalFieldValues: { enabled: true } }],
547
- ['post', '/user/refreshInfo', {}],
548
- ['get', '/user/settings'],
549
- ['post', '/user/settings', { userSettings: {} }],
550
- ['get', '/hostname'],
551
- ['post', '/unAuthorize', {}],
552
- ['get', '/contact'],
553
- ['post', '/contact', { phoneNumber: '+1555', newContactName: 'Alice' }],
554
- ['get', '/appointments'],
555
- ['post', '/appointments', { payload: { title: 'Meet' } }],
556
- ['patch', '/appointments/appt-2', { patch: { title: 'Meet' } }],
557
- ['get', '/appointments/appt-2/refresh'],
558
- ['post', '/appointments/appt-2/confirm', {}],
559
- ['post', '/appointments/appt-2/cancel', {}],
560
- ['get', '/callLog'],
561
- ['post', '/callLog', { logInfo: { accountId: 'acc' } }],
562
- ['patch', '/callLog', { accountId: 'acc' }],
563
- ['put', '/callDisposition', { sessionId: 's1' }],
564
- ['post', '/messageLog', { messages: [] }],
565
- ['post', '/calldown', { contactId: 'c1' }],
566
- ['get', '/calldown'],
567
- ['delete', '/calldown/item-1'],
568
- ['patch', '/calldown/item-1', { status: 'called' }],
569
- ['get', '/custom/contact/search'],
570
- ['get', '/ringcentral/admin/report'],
571
- ['get', '/ringcentral/admin/userReport'],
572
- ['get', '/ringcentral/oauth/callback'],
573
- ['get', '/debug/report/url'],
574
- ];
575
-
576
- for (const [method, path, body] of noTokenCases) {
577
- const req = request(app)[method](path);
578
- const response = body === undefined ? await req : await req.send(body);
579
- expect(response.status).toBe(400);
580
- }
581
-
582
- await expect(request(app).get('/implementedInterfaces')).resolves.toMatchObject({ status: 400 });
583
- await expect(request(app).get('/apiKeyManagedAuthState').query({ rcAccessToken: 'rc-token' })).resolves.toMatchObject({ status: 400 });
584
- await expect(request(app).get('/apiKeyManagedAuthState').query({ platform: 'testCRM' })).resolves.toMatchObject({ status: 400 });
585
- await expect(request(app).get('/oauthManagedAuthState').query({ rcAccessToken: 'rc-token' })).resolves.toMatchObject({ status: 400 });
586
- await expect(request(app).get('/oauthManagedAuthState').query({ platform: 'testCRM' })).resolves.toMatchObject({ status: 400 });
587
- await expect(request(app).post('/admin/serverLoggingSettings').query(authQuery()).send({})).resolves.toMatchObject({ status: 400 });
588
- await expect(request(app).get('/user/preloadSettings')).resolves.toMatchObject({ status: 400 });
589
- await expect(request(app).get('/oauth-callback')).resolves.toMatchObject({ status: 400 });
590
- await expect(request(app).get('/oauth-callback').query({ callbackUri: 'https://redirect.example.com/callback' })).resolves.toMatchObject({ status: 400 });
591
- await expect(request(app).post('/apiKeyLogin').send({ apiKey: 'api-key' })).resolves.toMatchObject({ status: 400 });
592
- await expect(request(app).delete('/admin/managedOAuth/account').query({ rcAccessToken: 'rc-token' })).resolves.toMatchObject({ status: 400 });
593
- });
594
-
595
- test('covers invalid JWT and revoke-session branches', async () => {
596
- async function expectInvalidJwt(requestPromise, expectedText = null) {
597
- jwt.decodeJwt.mockReturnValue(null);
598
- const response = await requestPromise();
599
- expect(response.status).toBe(400);
600
- if (expectedText) {
601
- expect(response.text).toContain(expectedText);
602
- }
603
- jwt.decodeJwt.mockReturnValue(decodedJwt);
604
- }
605
-
606
- await expectInvalidJwt(() => request(app).get('/licenseStatus').query({ jwtToken: 'bad' }), 'Invalid JWT token');
607
- await expectInvalidJwt(() => request(app).get('/authValidation').query({ jwtToken: 'bad' }));
608
- await expectInvalidJwt(() => request(app).get('/contact').query({ jwtToken: 'bad', phoneNumber: '+1555' }));
609
- await expectInvalidJwt(() => request(app).post('/contact').query({ jwtToken: 'bad' }).send({ phoneNumber: '+1555' }));
610
- await expectInvalidJwt(() => request(app).get('/appointments').query({ jwtToken: 'bad' }));
611
- await expectInvalidJwt(() => request(app).post('/appointments').query({ jwtToken: 'bad' }).send({ payload: {} }));
612
- await expectInvalidJwt(() => request(app).patch('/appointments/appt-2').query({ jwtToken: 'bad' }).send({ patch: {} }));
613
- await expectInvalidJwt(() => request(app).get('/appointments/appt-2/refresh').query({ jwtToken: 'bad' }));
614
- await expectInvalidJwt(() => request(app).post('/appointments/appt-2/confirm').query({ jwtToken: 'bad' }));
615
- await expectInvalidJwt(() => request(app).post('/appointments/appt-2/cancel').query({ jwtToken: 'bad' }));
616
- await expectInvalidJwt(() => request(app).get('/callLog').query({ jwtToken: 'bad' }));
617
- await expectInvalidJwt(() => request(app).post('/callLog').query({ jwtToken: 'bad' }).send({ logInfo: {} }));
618
- await expectInvalidJwt(() => request(app).patch('/callLog').query({ jwtToken: 'bad' }).send({}));
619
- await expectInvalidJwt(() => request(app).put('/callDisposition').query({ jwtToken: 'bad' }).send({ sessionId: 's1' }), 'Invalid JWT token');
620
- await expectInvalidJwt(() => request(app).post('/messageLog').query({ jwtToken: 'bad' }).send({ messages: [] }));
621
- await expectInvalidJwt(() => request(app).get('/custom/contact/search').query({ jwtToken: 'bad', name: 'Alice' }), 'Invalid JWT token');
622
-
623
- contactCore.findContact.mockResolvedValueOnce({
624
- successful: false,
625
- returnMessage: { messageType: 'warning', message: 'Reconnect' },
626
- isRevokeUserSession: true,
627
- });
628
- expect((await request(app).get('/contact').query({ ...authQuery(), phoneNumber: '+1555' })).status).toBe(401);
629
-
630
- contactCore.createContact.mockResolvedValueOnce({
631
- successful: false,
632
- returnMessage: { messageType: 'warning', message: 'Reconnect' },
633
- isRevokeUserSession: true,
634
- });
635
- expect((await request(app).post('/contact').query(authQuery()).send({ phoneNumber: '+1555' })).status).toBe(401);
636
-
637
- for (const [method, path, mockFn, body] of [
638
- ['get', '/appointments', appointmentCore.listAppointments],
639
- ['post', '/appointments', appointmentCore.createAppointment, { payload: {} }],
640
- ['patch', '/appointments/appt-2', appointmentCore.updateAppointment, { patch: {} }],
641
- ['get', '/appointments/appt-2/refresh', appointmentCore.refreshAppointment],
642
- ['post', '/appointments/appt-2/confirm', appointmentCore.confirmAppointment, {}],
643
- ['post', '/appointments/appt-2/cancel', appointmentCore.cancelAppointment, {}],
644
- ]) {
645
- mockFn.mockResolvedValueOnce({
646
- successful: false,
647
- returnMessage: { messageType: 'warning', message: 'Reconnect' },
648
- isRevokeUserSession: true,
649
- });
650
- const req = request(app)[method](path).query(authQuery());
651
- const response = body === undefined ? await req : await req.send(body);
652
- expect(response.status).toBe(401);
653
- }
654
-
655
- logCore.getCallLog.mockResolvedValueOnce({
656
- successful: false,
657
- returnMessage: { messageType: 'warning', message: 'Reconnect' },
658
- isRevokeUserSession: true,
659
- });
660
- expect((await request(app).get('/callLog').query(authQuery())).status).toBe(401);
661
-
662
- logCore.createCallLog.mockResolvedValueOnce({
663
- successful: false,
664
- returnMessage: { messageType: 'warning', message: 'Reconnect' },
665
- isRevokeUserSession: true,
666
- });
667
- expect((await request(app).post('/callLog').query(authQuery()).send({ logInfo: { accountId: 'acc' } })).status).toBe(401);
668
-
669
- dispositionCore.upsertCallDisposition.mockResolvedValueOnce({
670
- successful: false,
671
- returnMessage: { messageType: 'warning', message: 'Reconnect' },
672
- isRevokeUserSession: true,
673
- });
674
- expect((await request(app).put('/callDisposition').query(authQuery()).send({ sessionId: 's1' })).status).toBe(401);
675
-
676
- logCore.createMessageLog.mockResolvedValueOnce({
677
- successful: false,
678
- returnMessage: { messageType: 'warning', message: 'Reconnect' },
679
- isRevokeUserSession: true,
680
- });
681
- expect((await request(app).post('/messageLog').query(authQuery()).send({ messages: [] })).status).toBe(401);
682
-
683
- contactCore.findContactWithName.mockResolvedValueOnce({
684
- successful: false,
685
- returnMessage: { messageType: 'warning', message: 'Reconnect' },
686
- isRevokeUserSession: true,
687
- });
688
- expect((await request(app).get('/custom/contact/search').query({ ...authQuery(), name: 'Alice' })).status).toBe(401);
689
- });
690
-
691
- test('covers route catch branches for mocked handler failures', async () => {
692
- connectorRegistry.getConnector.mockImplementationOnce(() => {
693
- throw new Error('connector unavailable');
694
- });
695
- await expect(request(app).get('/implementedInterfaces').query({ platform: 'testCRM' })).resolves.toMatchObject({ status: 400 });
696
-
697
- authCore.getLicenseStatus.mockRejectedValueOnce(new Error('license failed'));
698
- expect((await request(app).get('/licenseStatus').query(authQuery())).body.licenseStatus).toBe('Invalid (Connect to get license status)');
699
-
700
- authCore.authValidation.mockRejectedValueOnce({ response: { status: 503 }, message: 'validation failed' });
701
- await expect(request(app).get('/authValidation').query(authQuery())).resolves.toMatchObject({ status: 400 });
702
-
703
- adminCore.validateRcUserToken.mockRejectedValueOnce(new Error('rc token invalid'));
704
- await expect(request(app).get('/apiKeyManagedAuthState').query({ platform: 'testCRM', rcAccessToken: 'rc-token' })).resolves.toMatchObject({ status: 400 });
705
-
706
- adminCore.validateAdminRole.mockRejectedValueOnce(new Error('admin validation failed'));
707
- await expect(request(app).get('/oauthManagedAuthState').query({ platform: 'testCRM', rcAccessToken: 'rc-token' })).resolves.toMatchObject({ status: 400 });
708
-
709
- UserModel.findByPk.mockResolvedValueOnce(null);
710
- await expect(request(app).get('/admin/settings').query({ ...authQuery(), rcAccessToken: 'rc-token' })).resolves.toMatchObject({ status: 400 });
711
-
712
- adminCore.getAdminSettings.mockResolvedValueOnce(null);
713
- expect((await request(app).get('/admin/settings').query({ ...authQuery(), rcAccessToken: 'rc-token' })).body).toEqual({
714
- customConnector: null,
715
- userSettings: {},
716
- });
717
-
718
- adminCore.validateAdminRole.mockResolvedValueOnce({ isValidated: false, rcAccountId: 'rc-account-1' });
719
- await expect(request(app).post('/admin/settings').query({ rcAccessToken: 'rc-token' }).send({ adminSettings: {} })).resolves.toMatchObject({ status: 403 });
720
-
721
- UserModel.findByPk.mockResolvedValueOnce(null);
722
- await expect(request(app).get('/admin/managedAuth').query({ ...authQuery(), rcAccessToken: 'rc-token' })).resolves.toMatchObject({ status: 400 });
723
-
724
- adminCore.validateAdminRole.mockResolvedValueOnce({ isValidated: false, rcAccountId: 'rc-account-1' });
725
- await expect(request(app).post('/admin/managedOAuth/cache').query({ rcAccessToken: 'rc-token' }).send({ values: {} })).resolves.toMatchObject({ status: 403 });
726
-
727
- managedOAuthCore.upsertPendingManagedOAuth.mockRejectedValueOnce(new Error('cache failed'));
728
- await expect(request(app).post('/admin/managedOAuth/cache').query({ rcAccessToken: 'rc-token' }).send({ values: {} })).resolves.toMatchObject({ status: 400 });
729
-
730
- managedOAuthCore.clearPendingManagedOAuth.mockRejectedValueOnce(new Error('clear failed'));
731
- await expect(request(app).delete('/admin/managedOAuth/cache').query({ rcAccessToken: 'rc-token' })).resolves.toMatchObject({ status: 400 });
732
-
733
- managedOAuthCore.resetManagedOAuth.mockRejectedValueOnce(new Error('reset failed'));
734
- await expect(request(app).delete('/admin/managedOAuth/account').query({ rcAccessToken: 'rc-token', platform: 'testCRM' })).resolves.toMatchObject({ status: 400 });
735
-
736
- adminCore.getUserMapping.mockResolvedValueOnce({ isRevokeUserSession: true });
737
- await expect(request(app).post('/admin/userMapping').query({ ...authQuery(), rcAccessToken: 'rc-token' }).send({ rcExtensionList: ['100'] })).resolves.toMatchObject({ status: 401 });
738
-
739
- adminCore.reinitializeUserMapping.mockResolvedValueOnce({ isRevokeUserSession: true });
740
- await expect(request(app).post('/admin/reinitializeUserMapping').query({ ...authQuery(), rcAccessToken: 'rc-token' }).send({ rcExtensionList: ['100'] })).resolves.toMatchObject({ status: 401 });
741
-
742
- UserModel.findByPk.mockResolvedValueOnce(null);
743
- await expect(request(app).get('/admin/serverLoggingSettings').query(authQuery())).resolves.toMatchObject({ status: 400 });
744
-
745
- adminCore.updateServerLoggingSettings.mockRejectedValueOnce(new Error('settings failed'));
746
- await expect(request(app).post('/admin/serverLoggingSettings').query(authQuery()).send({ additionalFieldValues: { enabled: true } })).resolves.toMatchObject({ status: 400 });
747
-
748
- userCore.getUserSettingsByAdmin.mockRejectedValueOnce(new Error('preload failed'));
749
- await expect(request(app).get('/user/preloadSettings').query({ rcAccessToken: 'rc-token' })).resolves.toMatchObject({ status: 400 });
750
-
751
- userCore.refreshUserInfo.mockRejectedValueOnce(new Error('refresh failed'));
752
- await expect(request(app).post('/user/refreshInfo').query(authQuery()).send({})).resolves.toMatchObject({ status: 400 });
753
-
754
- UserModel.findByPk.mockResolvedValueOnce(null);
755
- await expect(request(app).get('/user/settings').query(authQuery())).resolves.toMatchObject({ status: 400 });
756
-
757
- jwt.decodeJwt.mockReturnValueOnce({ id: 'user-1' }).mockReturnValueOnce({ id: 'user-1' });
758
- await expect(request(app).post('/user/settings').query({ jwtToken: 'valid-crm-jwt' }).send({ userSettings: {} })).resolves.toMatchObject({ status: 400 });
759
- jwt.decodeJwt.mockReturnValue(decodedJwt);
760
-
761
- UserModel.findByPk.mockResolvedValueOnce(null);
762
- await expect(request(app).get('/hostname').query(authQuery())).resolves.toMatchObject({ status: 400 });
763
-
764
- authCore.onOAuthCallback.mockRejectedValueOnce(new Error('oauth failed'));
765
- const failedState = encodeURIComponent('platform=testCRM&hostname=crm.example.com&sessionId=session-failed');
766
- await expect(request(app).get('/oauth-callback').query({
767
- callbackUri: `https://redirect.example.com/callback?state=${failedState}`,
768
- code: 'oauth-code',
769
- })).resolves.toMatchObject({ status: 400 });
770
-
771
- authCore.onApiKeyLogin.mockResolvedValueOnce({
772
- userInfo: null,
773
- returnMessage: { messageType: 'warning', message: 'Rejected' },
774
- });
775
- await expect(request(app).post('/apiKeyLogin').send({ platform: 'testCRM', apiKey: 'api-key' })).resolves.toMatchObject({ status: 400 });
776
-
777
- connectorRegistry.getConnector.mockReturnValueOnce({
778
- unAuthorize: jest.fn().mockRejectedValue(new Error('logout failed')),
779
- });
780
- await expect(request(app).post('/unAuthorize').query(authQuery()).send({})).resolves.toMatchObject({ status: 400 });
781
-
782
- contactCore.findContact.mockRejectedValueOnce({ response: { status: 500 }, message: 'find failed' });
783
- await expect(request(app).get('/contact').query({ ...authQuery(), phoneNumber: '+1555' })).resolves.toMatchObject({ status: 400 });
784
-
785
- contactCore.createContact.mockRejectedValueOnce({ response: { status: 500 }, message: 'create failed' });
786
- await expect(request(app).post('/contact').query(authQuery()).send({ phoneNumber: '+1555' })).resolves.toMatchObject({ status: 400 });
787
-
788
- appointmentCore.listAppointments.mockRejectedValueOnce({ response: { status: 500 }, message: 'list failed' });
789
- await expect(request(app).get('/appointments').query(authQuery())).resolves.toMatchObject({ status: 400 });
790
-
791
- appointmentCore.createAppointment.mockRejectedValueOnce({ response: { status: 500 }, message: 'create appointment failed' });
792
- await expect(request(app).post('/appointments').query(authQuery()).send({ payload: {} })).resolves.toMatchObject({ status: 400 });
793
-
794
- appointmentCore.updateAppointment.mockRejectedValueOnce({ response: { status: 500 }, message: 'update appointment failed' });
795
- await expect(request(app).patch('/appointments/appt-2').query(authQuery()).send({ patch: {} })).resolves.toMatchObject({ status: 400 });
796
-
797
- appointmentCore.refreshAppointment.mockRejectedValueOnce({ response: { status: 500 }, message: 'refresh appointment failed' });
798
- await expect(request(app).get('/appointments/appt-2/refresh').query(authQuery())).resolves.toMatchObject({ status: 400 });
799
-
800
- appointmentCore.confirmAppointment.mockRejectedValueOnce({ response: { status: 500 }, message: 'confirm appointment failed' });
801
- await expect(request(app).post('/appointments/appt-2/confirm').query(authQuery())).resolves.toMatchObject({ status: 400 });
802
-
803
- appointmentCore.cancelAppointment.mockRejectedValueOnce({ response: { status: 500 }, message: 'cancel appointment failed' });
804
- await expect(request(app).post('/appointments/appt-2/cancel').query(authQuery())).resolves.toMatchObject({ status: 400 });
805
-
806
- logCore.saveNoteCache.mockRejectedValueOnce({ response: { status: 500 }, message: 'cache failed' });
807
- await expect(request(app).post('/callLog/cacheNote').query(authQuery()).send({ sessionId: 's1' })).resolves.toMatchObject({ status: 400 });
808
-
809
- logCore.getCallLog.mockRejectedValueOnce({ response: { status: 500 }, message: 'get log failed' });
810
- await expect(request(app).get('/callLog').query(authQuery())).resolves.toMatchObject({ status: 400 });
811
-
812
- logCore.createCallLog.mockRejectedValueOnce({ response: { status: 500 }, message: 'create log failed' });
813
- await expect(request(app).post('/callLog').query(authQuery()).send({ logInfo: { accountId: 'acc' } })).resolves.toMatchObject({ status: 400 });
814
-
815
- logCore.updateCallLog.mockRejectedValueOnce({ response: { status: 500 }, message: 'update log failed' });
816
- await expect(request(app).patch('/callLog').query(authQuery()).send({ accountId: 'acc' })).resolves.toMatchObject({ status: 400 });
817
-
818
- dispositionCore.upsertCallDisposition.mockRejectedValueOnce({ response: { status: 500 }, message: 'disposition failed' });
819
- await expect(request(app).put('/callDisposition').query(authQuery()).send({ sessionId: 's1' })).resolves.toMatchObject({ status: 400 });
820
-
821
- logCore.createMessageLog.mockRejectedValueOnce({ response: { status: 500 }, message: 'message failed' });
822
- await expect(request(app).post('/messageLog').query(authQuery()).send({ messages: [] })).resolves.toMatchObject({ status: 400 });
823
-
824
- calldown.list.mockRejectedValueOnce({ response: { status: 500 }, message: 'calldown failed' });
825
- await expect(request(app).get('/calldown').query(authQuery())).resolves.toMatchObject({ status: 400 });
826
-
827
- contactCore.findContactWithName.mockRejectedValueOnce({ response: { status: 500 }, message: 'search failed' });
828
- await expect(request(app).get('/custom/contact/search').query({ ...authQuery(), name: 'Alice' })).resolves.toMatchObject({ status: 400 });
829
-
830
- UserModel.findByPk.mockResolvedValueOnce(null);
831
- await expect(request(app).get('/ringcentral/admin/report').query(authQuery())).resolves.toMatchObject({ status: 400 });
832
-
833
- UserModel.findByPk.mockResolvedValueOnce(null);
834
- await expect(request(app).get('/ringcentral/admin/userReport').query(authQuery())).resolves.toMatchObject({ status: 400 });
835
-
836
- UserModel.findByPk.mockResolvedValueOnce(null);
837
- await expect(request(app).get('/ringcentral/oauth/callback').query({ ...authQuery(), code: 'rc-code' })).resolves.toMatchObject({ status: 400 });
838
-
839
- logCore.handleAsyncPluginCallback.mockRejectedValueOnce(new Error('plugin failed'));
840
- await expect(request(app).post('/plugin/async-callback/task-1').send({ successful: true })).resolves.toMatchObject({ status: 500 });
841
- });
842
-
843
- test('covers exported app and initialization helpers', async () => {
844
- const middleware = createCoreMiddleware();
845
- expect(middleware).toHaveLength(3);
846
-
847
- await initializeCore({ skipDatabaseInit: true });
848
- expect(analytics.init).toHaveBeenCalled();
849
-
850
- const fullApp = createCoreApp({ skipDatabaseInit: true, skipAnalyticsInit: true });
851
- const response = await request(fullApp).get('/isAlive');
852
- expect(response.status).toBe(200);
853
- expect(response.text).toBe('OK');
854
- });
855
- });