@app-connect/core 1.7.36 → 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 (287) 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} +548 -450
  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/dist/lib/migrateCallLogsSchema.js +339 -0
  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 +13 -1
  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/migrateCallLogsSchema.js +0 -237
  198. package/lib/ringcentral.js +0 -267
  199. package/mcp/README.md +0 -420
  200. package/mcp/tools/index.js +0 -51
  201. package/mcp/ui/App/App.tsx +0 -358
  202. package/mcp/ui/App/components/AuthInfoForm.tsx +0 -113
  203. package/mcp/ui/App/components/AuthSuccess.tsx +0 -22
  204. package/mcp/ui/App/components/ConnectorList.tsx +0 -82
  205. package/mcp/ui/App/components/DebugPanel.tsx +0 -43
  206. package/mcp/ui/App/components/OAuthConnect.tsx +0 -270
  207. package/mcp/ui/App/lib/callTool.ts +0 -130
  208. package/mcp/ui/App/lib/debugLog.ts +0 -41
  209. package/mcp/ui/App/lib/developerPortal.ts +0 -111
  210. package/mcp/ui/App/main.css +0 -6
  211. package/mcp/ui/App/root.tsx +0 -13
  212. package/mcp/ui/dist/index.html +0 -53
  213. package/mcp/ui/vite.config.ts +0 -16
  214. package/models/accountDataModel.js +0 -34
  215. package/models/adminConfigModel.js +0 -35
  216. package/models/callLogModel.js +0 -39
  217. package/models/dynamo/connectorSchema.js +0 -146
  218. package/models/llmSessionModel.js +0 -17
  219. package/models/sequelize.js +0 -17
  220. package/models/userModel.js +0 -45
  221. package/test/connector/developerPortal.test.js +0 -166
  222. package/test/connector/mock.test.js +0 -131
  223. package/test/connector/proxy/engine.test.js +0 -211
  224. package/test/connector/proxy/index.test.js +0 -525
  225. package/test/connector/proxy/sample.json +0 -162
  226. package/test/connector/registry.test.js +0 -416
  227. package/test/handlers/admin.test.js +0 -960
  228. package/test/handlers/appointment.test.js +0 -260
  229. package/test/handlers/auth.test.js +0 -1096
  230. package/test/handlers/calldown.test.js +0 -310
  231. package/test/handlers/contact.test.js +0 -1014
  232. package/test/handlers/disposition.test.js +0 -396
  233. package/test/handlers/log.test.js +0 -1847
  234. package/test/handlers/managedAuth.test.js +0 -458
  235. package/test/handlers/managedOAuth.test.js +0 -262
  236. package/test/handlers/plugin.test.js +0 -429
  237. package/test/handlers/user.test.js +0 -381
  238. package/test/index.test.js +0 -301
  239. package/test/lib/analytics.test.js +0 -146
  240. package/test/lib/authSession.test.js +0 -173
  241. package/test/lib/cacheCleanup.test.js +0 -127
  242. package/test/lib/callLogComposer.test.js +0 -1231
  243. package/test/lib/debugTracer.test.js +0 -328
  244. package/test/lib/encode.test.js +0 -59
  245. package/test/lib/errorHandler.test.js +0 -246
  246. package/test/lib/generalErrorMessage.test.js +0 -82
  247. package/test/lib/jwt.test.js +0 -176
  248. package/test/lib/logger.test.js +0 -206
  249. package/test/lib/oauth.test.js +0 -359
  250. package/test/lib/ringcentral.test.js +0 -467
  251. package/test/lib/s3ErrorLogReport.test.js +0 -187
  252. package/test/lib/sharedSMSComposer.test.js +0 -1084
  253. package/test/lib/util.test.js +0 -329
  254. package/test/mcp/mcpHandler.test.js +0 -73
  255. package/test/mcp/mcpHandlerMore.test.js +0 -384
  256. package/test/mcp/tools/appointmentTools.test.js +0 -362
  257. package/test/mcp/tools/checkAuthStatus.test.js +0 -83
  258. package/test/mcp/tools/createCallLog.test.js +0 -436
  259. package/test/mcp/tools/createContact.test.js +0 -58
  260. package/test/mcp/tools/createMessageLog.test.js +0 -595
  261. package/test/mcp/tools/doAuth.test.js +0 -113
  262. package/test/mcp/tools/findContactByName.test.js +0 -275
  263. package/test/mcp/tools/findContactByPhone.test.js +0 -296
  264. package/test/mcp/tools/getCallLog.test.js +0 -298
  265. package/test/mcp/tools/getGoogleFilePicker.test.js +0 -281
  266. package/test/mcp/tools/getPublicConnectors.test.js +0 -107
  267. package/test/mcp/tools/getSessionInfo.test.js +0 -127
  268. package/test/mcp/tools/logout.test.js +0 -233
  269. package/test/mcp/tools/rcGetCallLogs.test.js +0 -56
  270. package/test/mcp/tools/updateCallLog.test.js +0 -360
  271. package/test/models/accountDataModel.test.js +0 -98
  272. package/test/models/callDownListModel.test.js +0 -125
  273. package/test/models/dynamo/connectorSchema.test.js +0 -189
  274. package/test/models/dynamo/lockSchema.test.js +0 -37
  275. package/test/models/dynamo/noteCacheSchema.test.js +0 -45
  276. package/test/models/llmSessionModel.test.js +0 -91
  277. package/test/models/models.test.js +0 -660
  278. package/test/routes/calldownRoutes.test.js +0 -224
  279. package/test/routes/coreRouterBroadRoutes.test.js +0 -855
  280. package/test/routes/dispositionRoutes.test.js +0 -192
  281. package/test/routes/managedAuthRoutes.test.js +0 -386
  282. package/test/routes/pluginRoutes.test.js +0 -262
  283. package/test/setup.js +0 -178
  284. /package/{mcp → dist/mcp}/ui/index.html +0 -0
  285. /package/{mcp → dist/mcp}/ui/package-lock.json +0 -0
  286. /package/{mcp → dist/mcp}/ui/package.json +0 -0
  287. /package/{mcp → dist/mcp}/ui/tsconfig.json +0 -0
@@ -1,960 +0,0 @@
1
- // Use in-memory SQLite for isolated model tests
2
- jest.mock('../../models/sequelize', () => {
3
- const { Sequelize } = require('sequelize');
4
- return {
5
- sequelize: new Sequelize({
6
- dialect: 'sqlite',
7
- storage: ':memory:',
8
- logging: false,
9
- }),
10
- };
11
- });
12
-
13
- jest.mock('axios');
14
- jest.mock('../../connector/registry');
15
- jest.mock('../../lib/oauth');
16
- jest.mock('../../lib/ringcentral');
17
- jest.mock('../../models/dynamo/connectorSchema', () => ({
18
- Connector: {
19
- getProxyConfig: jest.fn()
20
- }
21
- }));
22
-
23
- const axios = require('axios');
24
- const adminHandler = require('../../handlers/admin');
25
- const { AdminConfigModel } = require('../../models/adminConfigModel');
26
- const { UserModel } = require('../../models/userModel');
27
- const connectorRegistry = require('../../connector/registry');
28
- const oauth = require('../../lib/oauth');
29
- const { RingCentral } = require('../../lib/ringcentral');
30
- const { Connector } = require('../../models/dynamo/connectorSchema');
31
- const { sequelize } = require('../../models/sequelize');
32
- const { getHashValue } = require('../../lib/util');
33
-
34
- describe('Admin Handler', () => {
35
- beforeAll(async () => {
36
- await AdminConfigModel.sync({ force: true });
37
- await UserModel.sync({ force: true });
38
- });
39
-
40
- afterEach(async () => {
41
- await AdminConfigModel.destroy({ where: {} });
42
- await UserModel.destroy({ where: {} });
43
- jest.clearAllMocks();
44
- });
45
-
46
- afterAll(async () => {
47
- await sequelize.close();
48
- });
49
-
50
- describe('validateAdminRole', () => {
51
- test('should return validated true when user has admin permissions', async () => {
52
- // Arrange
53
- axios.get.mockResolvedValue({
54
- data: {
55
- permissions: {
56
- admin: { enabled: true }
57
- },
58
- account: { id: 'rc-account-123' },
59
- id: 'extension-123'
60
- }
61
- });
62
-
63
- // Act
64
- const result = await adminHandler.validateAdminRole({
65
- rcAccessToken: 'valid-token'
66
- });
67
-
68
- // Assert
69
- expect(result.isValidated).toBe(true);
70
- expect(result.rcAccountId).toBe('rc-account-123');
71
- expect(axios.get).toHaveBeenCalledWith(
72
- 'https://platform.ringcentral.com/restapi/v1.0/account/~/extension/~',
73
- { headers: { Authorization: 'Bearer valid-token' } }
74
- );
75
- });
76
-
77
- test('should return validated false when user lacks admin permissions', async () => {
78
- // Arrange
79
- axios.get.mockResolvedValue({
80
- data: {
81
- permissions: {
82
- admin: { enabled: false }
83
- },
84
- account: { id: 'rc-account-456' },
85
- id: 'extension-456'
86
- }
87
- });
88
-
89
- // Act
90
- const result = await adminHandler.validateAdminRole({
91
- rcAccessToken: 'non-admin-token'
92
- });
93
-
94
- // Assert
95
- expect(result.isValidated).toBe(false);
96
- expect(result.rcAccountId).toBe('rc-account-456');
97
- });
98
-
99
- test('should return validated true for dev pass list extension', async () => {
100
- // Arrange
101
- const originalEnv = process.env.ADMIN_EXTENSION_ID_DEV_PASS_LIST;
102
- process.env.ADMIN_EXTENSION_ID_DEV_PASS_LIST = '999,1000,1001';
103
-
104
- axios.get.mockResolvedValue({
105
- data: {
106
- permissions: {
107
- admin: { enabled: false }
108
- },
109
- account: { id: 'rc-account-dev' },
110
- id: 1000
111
- }
112
- });
113
-
114
- // Act
115
- const result = await adminHandler.validateAdminRole({
116
- rcAccessToken: 'dev-token'
117
- });
118
-
119
- // Assert
120
- expect(result.isValidated).toBe(true);
121
-
122
- // Cleanup
123
- process.env.ADMIN_EXTENSION_ID_DEV_PASS_LIST = originalEnv;
124
- });
125
- });
126
-
127
- describe('validateRcUserToken', () => {
128
- test('should return rc account and extension identity from valid token', async () => {
129
- axios.get.mockResolvedValue({
130
- data: {
131
- account: { id: 'rc-account-789' },
132
- id: 'extension-789',
133
- contact: {
134
- firstName: 'Alex',
135
- lastName: 'Johnson'
136
- }
137
- }
138
- });
139
-
140
- const result = await adminHandler.validateRcUserToken({
141
- rcAccessToken: 'valid-user-token'
142
- });
143
-
144
- expect(result).toEqual({
145
- rcAccountId: 'rc-account-789',
146
- rcExtensionId: 'extension-789'
147
- });
148
- expect(axios.get).toHaveBeenCalledWith(
149
- 'https://platform.ringcentral.com/restapi/v1.0/account/~/extension/~',
150
- { headers: { Authorization: 'Bearer valid-user-token' } }
151
- );
152
- });
153
-
154
- test('should throw when rcAccessToken is missing', async () => {
155
- await expect(adminHandler.validateRcUserToken({})).rejects.toThrow('rcAccessToken is required');
156
- expect(axios.get).not.toHaveBeenCalled();
157
- });
158
- });
159
-
160
- describe('upsertAdminSettings', () => {
161
- test('should create new admin config when none exists', async () => {
162
- // Act
163
- await adminHandler.upsertAdminSettings({
164
- hashedRcAccountId: 'hashed-123',
165
- adminSettings: {
166
- userSettings: { autoLogCalls: true, autoLogMessages: false }
167
- }
168
- });
169
-
170
- // Assert
171
- const config = await AdminConfigModel.findByPk('hashed-123');
172
- expect(config).not.toBeNull();
173
- expect(config.userSettings).toEqual({ autoLogCalls: true, autoLogMessages: false });
174
- });
175
-
176
- test('should update existing admin config', async () => {
177
- // Arrange
178
- await AdminConfigModel.create({
179
- id: 'hashed-existing',
180
- userSettings: { autoLogCalls: false, autoLogMessages: true }
181
- });
182
-
183
- // Act
184
- await adminHandler.upsertAdminSettings({
185
- hashedRcAccountId: 'hashed-existing',
186
- adminSettings: {
187
- userSettings: { autoLogCalls: true, autoLogMessages: false }
188
- }
189
- });
190
-
191
- // Assert
192
- const config = await AdminConfigModel.findByPk('hashed-existing');
193
- expect(config.userSettings).toEqual({ autoLogCalls: true, autoLogMessages: false });
194
- });
195
- });
196
-
197
- describe('getAdminSettings', () => {
198
- test('should return admin settings when they exist', async () => {
199
- // Arrange
200
- await AdminConfigModel.create({
201
- id: 'hashed-get-test',
202
- userSettings: { autoLogCalls: true, autoLogMessages: true }
203
- });
204
-
205
- // Act
206
- const result = await adminHandler.getAdminSettings({
207
- hashedRcAccountId: 'hashed-get-test'
208
- });
209
-
210
- // Assert
211
- expect(result).not.toBeNull();
212
- expect(result.userSettings).toEqual({ autoLogCalls: true, autoLogMessages: true });
213
- });
214
-
215
- test('should return null when settings do not exist', async () => {
216
- // Act
217
- const result = await adminHandler.getAdminSettings({
218
- hashedRcAccountId: 'non-existent'
219
- });
220
-
221
- // Assert
222
- expect(result).toBeNull();
223
- });
224
- });
225
-
226
- describe('updateAdminRcTokens', () => {
227
- test('should update tokens for existing config', async () => {
228
- // Arrange
229
- await AdminConfigModel.create({
230
- id: 'hashed-token-test',
231
- adminAccessToken: 'old-access',
232
- adminRefreshToken: 'old-refresh',
233
- adminTokenExpiry: new Date('2024-01-01')
234
- });
235
-
236
- const newExpiry = new Date('2024-12-31');
237
-
238
- // Act
239
- await adminHandler.updateAdminRcTokens({
240
- hashedRcAccountId: 'hashed-token-test',
241
- adminAccessToken: 'new-access',
242
- adminRefreshToken: 'new-refresh',
243
- adminTokenExpiry: newExpiry
244
- });
245
-
246
- // Assert
247
- const config = await AdminConfigModel.findByPk('hashed-token-test');
248
- expect(config.adminAccessToken).toBe('new-access');
249
- expect(config.adminRefreshToken).toBe('new-refresh');
250
- });
251
-
252
- test('should create new config with tokens when none exists', async () => {
253
- // Arrange
254
- const expiry = new Date('2024-12-31');
255
-
256
- // Act
257
- await adminHandler.updateAdminRcTokens({
258
- hashedRcAccountId: 'hashed-new-token',
259
- adminAccessToken: 'new-access-token',
260
- adminRefreshToken: 'new-refresh-token',
261
- adminTokenExpiry: expiry
262
- });
263
-
264
- // Assert
265
- const config = await AdminConfigModel.findByPk('hashed-new-token');
266
- expect(config).not.toBeNull();
267
- expect(config.adminAccessToken).toBe('new-access-token');
268
- expect(config.adminRefreshToken).toBe('new-refresh-token');
269
- });
270
- });
271
-
272
- describe('getServerLoggingSettings', () => {
273
- test('should return settings from platform module when available', async () => {
274
- // Arrange
275
- const mockUser = { platform: 'testCRM', accessToken: 'token' };
276
- const mockSettings = { enableAutoLog: true, logLevel: 'debug' };
277
-
278
- const mockConnector = {
279
- getServerLoggingSettings: jest.fn().mockResolvedValue(mockSettings)
280
- };
281
- connectorRegistry.getConnector.mockReturnValue(mockConnector);
282
-
283
- // Act
284
- const result = await adminHandler.getServerLoggingSettings({ user: mockUser });
285
-
286
- // Assert
287
- expect(result).toEqual(mockSettings);
288
- expect(mockConnector.getServerLoggingSettings).toHaveBeenCalledWith({ user: mockUser });
289
- });
290
-
291
- test('should return empty object when platform module lacks getServerLoggingSettings', async () => {
292
- // Arrange
293
- const mockUser = { platform: 'testCRM', accessToken: 'token' };
294
-
295
- const mockConnector = {};
296
- connectorRegistry.getConnector.mockReturnValue(mockConnector);
297
-
298
- // Act
299
- const result = await adminHandler.getServerLoggingSettings({ user: mockUser });
300
-
301
- // Assert
302
- expect(result).toEqual({});
303
- });
304
- });
305
-
306
- describe('updateServerLoggingSettings', () => {
307
- test('should update settings via platform module when available', async () => {
308
- // Arrange
309
- const mockUser = { platform: 'testCRM', accessToken: 'token' };
310
- const additionalFieldValues = { field1: 'value1' };
311
-
312
- const mockConnector = {
313
- getOauthInfo: jest.fn().mockResolvedValue({
314
- clientId: 'id',
315
- clientSecret: 'secret',
316
- accessTokenUri: 'https://token.url'
317
- }),
318
- updateServerLoggingSettings: jest.fn().mockResolvedValue({
319
- successful: true,
320
- returnMessage: { message: 'Settings updated' }
321
- })
322
- };
323
- connectorRegistry.getConnector.mockReturnValue(mockConnector);
324
- oauth.getOAuthApp.mockReturnValue({});
325
-
326
- // Act
327
- const result = await adminHandler.updateServerLoggingSettings({
328
- user: mockUser,
329
- additionalFieldValues
330
- });
331
-
332
- // Assert
333
- expect(result.successful).toBe(true);
334
- expect(mockConnector.updateServerLoggingSettings).toHaveBeenCalled();
335
- });
336
-
337
- test('should return empty object when platform module lacks updateServerLoggingSettings', async () => {
338
- // Arrange
339
- const mockUser = { platform: 'testCRM', accessToken: 'token' };
340
-
341
- const mockConnector = {
342
- getOauthInfo: jest.fn().mockResolvedValue({})
343
- };
344
- connectorRegistry.getConnector.mockReturnValue(mockConnector);
345
- oauth.getOAuthApp.mockReturnValue({});
346
-
347
- // Act
348
- const result = await adminHandler.updateServerLoggingSettings({
349
- user: mockUser,
350
- additionalFieldValues: {}
351
- });
352
-
353
- // Assert
354
- expect(result).toEqual({});
355
- });
356
- });
357
-
358
- describe('getAdminReport', () => {
359
- test('should return empty stats when RC credentials are not configured', async () => {
360
- // Arrange
361
- const originalServer = process.env.RINGCENTRAL_SERVER;
362
- delete process.env.RINGCENTRAL_SERVER;
363
-
364
- // Act
365
- const result = await adminHandler.getAdminReport({
366
- rcAccountId: 'account-123',
367
- timezone: 'America/Los_Angeles',
368
- timeFrom: '2024-01-01',
369
- timeTo: '2024-01-31',
370
- groupBy: 'Users'
371
- });
372
-
373
- // Assert
374
- expect(result).toEqual({ callLogStats: {} });
375
-
376
- // Cleanup
377
- if (originalServer) {
378
- process.env.RINGCENTRAL_SERVER = originalServer;
379
- }
380
- });
381
-
382
- test('should handle errors and return empty stats', async () => {
383
- // Arrange
384
- const originalServer = process.env.RINGCENTRAL_SERVER;
385
- const originalClientId = process.env.RINGCENTRAL_CLIENT_ID;
386
- const originalClientSecret = process.env.RINGCENTRAL_CLIENT_SECRET;
387
-
388
- process.env.RINGCENTRAL_SERVER = 'https://platform.ringcentral.com';
389
- process.env.RINGCENTRAL_CLIENT_ID = 'test-client-id';
390
- process.env.RINGCENTRAL_CLIENT_SECRET = 'test-client-secret';
391
-
392
- // Mock AdminConfigModel.findByPk to throw error
393
- jest.spyOn(AdminConfigModel, 'findByPk').mockRejectedValueOnce(new Error('Database error'));
394
-
395
- // Act
396
- const result = await adminHandler.getAdminReport({
397
- rcAccountId: 'account-123',
398
- timezone: 'America/Los_Angeles',
399
- timeFrom: '2024-01-01',
400
- timeTo: '2024-01-31',
401
- groupBy: 'Users'
402
- });
403
-
404
- // Assert
405
- expect(result).toEqual({ callLogStats: {} });
406
-
407
- // Cleanup
408
- process.env.RINGCENTRAL_SERVER = originalServer;
409
- process.env.RINGCENTRAL_CLIENT_ID = originalClientId;
410
- process.env.RINGCENTRAL_CLIENT_SECRET = originalClientSecret;
411
- });
412
- });
413
-
414
- describe('getUserReport', () => {
415
- test('should return empty stats when RC credentials are not configured', async () => {
416
- // Arrange
417
- const originalServer = process.env.RINGCENTRAL_SERVER;
418
- delete process.env.RINGCENTRAL_SERVER;
419
-
420
- // Act
421
- const result = await adminHandler.getUserReport({
422
- rcAccountId: 'account-123',
423
- rcExtensionId: 'extension-123',
424
- timezone: 'America/Los_Angeles',
425
- timeFrom: '2024-01-01',
426
- timeTo: '2024-01-31'
427
- });
428
-
429
- // Assert
430
- expect(result).toEqual({ callLogStats: {} });
431
-
432
- // Cleanup
433
- if (originalServer) {
434
- process.env.RINGCENTRAL_SERVER = originalServer;
435
- }
436
- });
437
-
438
- test('should handle errors and return null', async () => {
439
- // Arrange
440
- const originalServer = process.env.RINGCENTRAL_SERVER;
441
- const originalClientId = process.env.RINGCENTRAL_CLIENT_ID;
442
- const originalClientSecret = process.env.RINGCENTRAL_CLIENT_SECRET;
443
-
444
- process.env.RINGCENTRAL_SERVER = 'https://platform.ringcentral.com';
445
- process.env.RINGCENTRAL_CLIENT_ID = 'test-client-id';
446
- process.env.RINGCENTRAL_CLIENT_SECRET = 'test-client-secret';
447
-
448
- // Mock AdminConfigModel.findByPk to throw error
449
- jest.spyOn(AdminConfigModel, 'findByPk').mockRejectedValueOnce(new Error('Database error'));
450
-
451
- // Act
452
- const result = await adminHandler.getUserReport({
453
- rcAccountId: 'account-123',
454
- rcExtensionId: 'extension-123',
455
- timezone: 'America/Los_Angeles',
456
- timeFrom: '2024-01-01',
457
- timeTo: '2024-01-31'
458
- });
459
-
460
- // Assert
461
- expect(result).toBeNull();
462
-
463
- // Cleanup
464
- process.env.RINGCENTRAL_SERVER = originalServer;
465
- process.env.RINGCENTRAL_CLIENT_ID = originalClientId;
466
- process.env.RINGCENTRAL_CLIENT_SECRET = originalClientSecret;
467
- });
468
- });
469
-
470
- describe('getUserMapping', () => {
471
- test('should return empty array when platform module lacks getUserList', async () => {
472
- // Arrange
473
- await UserModel.create({
474
- id: 'test-user-id',
475
- platform: 'testCRM',
476
- accessToken: 'token',
477
- platformAdditionalInfo: {}
478
- });
479
-
480
- const mockConnector = {};
481
- connectorRegistry.getConnector.mockReturnValue(mockConnector);
482
-
483
- // Act
484
- const result = await adminHandler.getUserMapping({
485
- user: { id: 'test-user-id', platform: 'testCRM', platformAdditionalInfo: {} },
486
- hashedRcAccountId: 'hashed-123',
487
- rcExtensionList: []
488
- });
489
-
490
- // Assert
491
- expect(result).toEqual([]);
492
- });
493
-
494
- test('should return empty array when proxy config lacks getUserList operation', async () => {
495
- // Arrange
496
- const user = {
497
- id: 'test-user-id',
498
- platform: 'testCRM',
499
- accessToken: 'token',
500
- platformAdditionalInfo: { proxyId: 'proxy-123' }
501
- };
502
-
503
- Connector.getProxyConfig.mockResolvedValue({
504
- operations: {}
505
- });
506
-
507
- const mockConnector = {
508
- getUserList: jest.fn()
509
- };
510
- connectorRegistry.getConnector.mockReturnValue(mockConnector);
511
-
512
- // Act
513
- const result = await adminHandler.getUserMapping({
514
- user,
515
- hashedRcAccountId: 'hashed-123',
516
- rcExtensionList: []
517
- });
518
-
519
- // Assert
520
- expect(result).toEqual([]);
521
- });
522
-
523
- test('should map CRM users to RC extensions', async () => {
524
- // Arrange
525
- await AdminConfigModel.create({
526
- id: 'hashed-mapping',
527
- userMappings: []
528
- });
529
-
530
- const user = {
531
- id: 'test-user-id',
532
- platform: 'testCRM',
533
- accessToken: 'token',
534
- platformAdditionalInfo: {}
535
- };
536
-
537
- const crmUsers = [
538
- { id: 'crm-user-1', name: 'John Doe', email: 'john@example.com' },
539
- { id: 'crm-user-2', name: 'Jane Smith', email: 'jane@example.com' }
540
- ];
541
-
542
- const rcExtensions = [
543
- { id: 'ext-1', firstName: 'John', lastName: 'Doe', email: 'john@example.com', extensionNumber: '101' },
544
- { id: 'ext-2', firstName: 'Bob', lastName: 'Wilson', email: 'bob@example.com', extensionNumber: '102' }
545
- ];
546
-
547
- const mockConnector = {
548
- getAuthType: jest.fn().mockResolvedValue('apiKey'),
549
- getBasicAuth: jest.fn().mockReturnValue('base64'),
550
- getUserList: jest.fn().mockResolvedValue(crmUsers)
551
- };
552
- connectorRegistry.getConnector.mockReturnValue(mockConnector);
553
-
554
- // Act
555
- const result = await adminHandler.getUserMapping({
556
- user,
557
- hashedRcAccountId: 'hashed-mapping',
558
- rcExtensionList: rcExtensions
559
- });
560
-
561
- // Assert
562
- expect(result).toHaveLength(2);
563
- // John should be matched by email
564
- const johnMapping = result.find(m => m.crmUser.id === 'crm-user-1');
565
- expect(johnMapping.rcUser).toHaveLength(1);
566
- expect(johnMapping.rcUser[0].extensionId).toBe('ext-1');
567
-
568
- // Jane should not be matched
569
- const janeMapping = result.find(m => m.crmUser.id === 'crm-user-2');
570
- expect(janeMapping.rcUser).toHaveLength(0);
571
- });
572
-
573
- test('should preserve existing mappings', async () => {
574
- // Arrange
575
- await AdminConfigModel.create({
576
- id: 'hashed-existing-mapping',
577
- userMappings: [
578
- { crmUserId: 'crm-user-1', rcExtensionId: ['ext-existing'] }
579
- ]
580
- });
581
-
582
- const user = {
583
- id: 'test-user-id',
584
- platform: 'testCRM',
585
- accessToken: 'token',
586
- platformAdditionalInfo: {}
587
- };
588
-
589
- const crmUsers = [
590
- { id: 'crm-user-1', name: 'John Doe', email: 'john@example.com' }
591
- ];
592
-
593
- const rcExtensions = [
594
- { id: 'ext-existing', firstName: 'John', lastName: 'Doe', email: 'john@example.com', extensionNumber: '101' }
595
- ];
596
-
597
- const mockConnector = {
598
- getAuthType: jest.fn().mockResolvedValue('apiKey'),
599
- getBasicAuth: jest.fn().mockReturnValue('base64'),
600
- getUserList: jest.fn().mockResolvedValue(crmUsers)
601
- };
602
- connectorRegistry.getConnector.mockReturnValue(mockConnector);
603
-
604
- // Act
605
- const result = await adminHandler.getUserMapping({
606
- user,
607
- hashedRcAccountId: 'hashed-existing-mapping',
608
- rcExtensionList: rcExtensions
609
- });
610
-
611
- // Assert
612
- expect(result).toHaveLength(1);
613
- expect(result[0].rcUser[0].extensionId).toBe('ext-existing');
614
- });
615
- });
616
-
617
- describe('reports and mapping branch coverage', () => {
618
- beforeEach(() => {
619
- process.env.RINGCENTRAL_SERVER = 'https://platform.example.com';
620
- process.env.RINGCENTRAL_CLIENT_ID = 'client-id';
621
- process.env.RINGCENTRAL_CLIENT_SECRET = 'client-secret';
622
- process.env.APP_SERVER = 'https://app.example.com';
623
- process.env.HASH_KEY = 'hash-key';
624
- });
625
-
626
- afterEach(() => {
627
- delete process.env.RINGCENTRAL_SERVER;
628
- delete process.env.RINGCENTRAL_CLIENT_ID;
629
- delete process.env.RINGCENTRAL_CLIENT_SECRET;
630
- delete process.env.APP_SERVER;
631
- delete process.env.HASH_KEY;
632
- });
633
-
634
- test('getAdminReport builds aggregation rows and skips unnamed records', async () => {
635
- const hashedAccountId = getHashValue('account', process.env.HASH_KEY);
636
- await AdminConfigModel.create({
637
- id: hashedAccountId,
638
- adminAccessToken: 'admin-token',
639
- adminRefreshToken: 'refresh-token',
640
- adminTokenExpiry: new Date(Date.now() + 60 * 60 * 1000)
641
- });
642
- const getCallsAggregationData = jest.fn().mockResolvedValue({
643
- data: {
644
- groupedBy: 'Users',
645
- records: [
646
- {},
647
- {
648
- info: { name: 'Agent 1' },
649
- counters: {
650
- callsByDirection: { values: { inbound: 2, outbound: 1 } },
651
- callsByResponse: { values: { answered: 1 } }
652
- },
653
- timers: {
654
- allCalls: { values: 90 }
655
- }
656
- },
657
- {
658
- info: { name: 'Agent 2' },
659
- counters: {
660
- callsByDirection: { values: { inbound: 0, outbound: 0 } },
661
- callsByResponse: { values: { answered: 0 } }
662
- },
663
- timers: {
664
- allCalls: { values: 0 }
665
- }
666
- }
667
- ]
668
- }
669
- });
670
- RingCentral.mockImplementation(() => ({
671
- getCallsAggregationData
672
- }));
673
-
674
- const result = await adminHandler.getAdminReport({
675
- rcAccountId: 'account',
676
- timezone: 'UTC',
677
- timeFrom: '2026-07-01T00:00:00Z',
678
- timeTo: '2026-07-31T23:59:59Z',
679
- groupBy: 'undefined'
680
- });
681
-
682
- expect(result.groupedBy).toBe('Users');
683
- expect(result.itemKeys).toEqual(['Agent 1', 'Agent 2']);
684
- expect(result.callLogStats[0]).toMatchObject({
685
- name: 'Agent 1',
686
- inboundCallCount: 2,
687
- outboundCallCount: 1,
688
- answeredCallCount: 1,
689
- answeredCallPercentage: '50.00%',
690
- totalTalkTime: '90.00',
691
- averageTalkTime: '30.00'
692
- });
693
- expect(result.callLogStats[1]).toMatchObject({
694
- name: 'Agent 2',
695
- answeredCallPercentage: '0%',
696
- totalTalkTime: 0,
697
- averageTalkTime: 0
698
- });
699
- expect(getCallsAggregationData).toHaveBeenCalledWith(expect.objectContaining({
700
- groupBy: 'Company'
701
- }));
702
- });
703
-
704
- test('getUserReport builds call and SMS stats', async () => {
705
- const hashedAccountId = getHashValue('account', process.env.HASH_KEY);
706
- await AdminConfigModel.create({
707
- id: hashedAccountId,
708
- adminAccessToken: 'admin-token',
709
- adminRefreshToken: 'refresh-token',
710
- adminTokenExpiry: new Date(Date.now() + 60 * 60 * 1000)
711
- });
712
- RingCentral.mockImplementation(() => ({
713
- getCallLogData: jest.fn().mockResolvedValue({
714
- records: [
715
- { direction: 'Inbound', result: 'Call connected', duration: 120 },
716
- { direction: 'Inbound', result: 'Missed', duration: 0 },
717
- { direction: 'Outbound', result: 'Accepted', duration: 60 }
718
- ]
719
- }),
720
- getSMSData: jest.fn().mockResolvedValue({
721
- records: [
722
- { direction: 'Outbound' },
723
- { direction: 'Inbound' },
724
- { direction: 'Inbound' }
725
- ]
726
- })
727
- }));
728
-
729
- const result = await adminHandler.getUserReport({
730
- rcAccountId: 'account',
731
- rcExtensionId: '101',
732
- timezone: 'UTC',
733
- timeFrom: '2026-07-01T00:00:00Z',
734
- timeTo: '2026-07-31T23:59:59Z'
735
- });
736
-
737
- expect(result).toEqual({
738
- callLogStats: {
739
- inboundCallCount: 2,
740
- outboundCallCount: 1,
741
- answeredCallCount: 1,
742
- answeredCallPercentage: '50.00%',
743
- totalTalkTime: 3,
744
- averageTalkTime: 1
745
- },
746
- smsLogStats: {
747
- smsSentCount: 1,
748
- smsReceivedCount: 2
749
- }
750
- });
751
- });
752
-
753
- test('report helpers return empty data on missing env or provider errors', async () => {
754
- delete process.env.RINGCENTRAL_SERVER;
755
- await expect(adminHandler.getAdminReport({ rcAccountId: 'account' })).resolves.toEqual({
756
- callLogStats: {}
757
- });
758
- await expect(adminHandler.getUserReport({ rcAccountId: 'account' })).resolves.toEqual({
759
- callLogStats: {}
760
- });
761
-
762
- process.env.RINGCENTRAL_SERVER = 'https://platform.example.com';
763
- const hashedAccountId = getHashValue('account', process.env.HASH_KEY);
764
- await AdminConfigModel.create({
765
- id: hashedAccountId,
766
- adminAccessToken: 'admin-token',
767
- adminRefreshToken: 'refresh-token',
768
- adminTokenExpiry: new Date(Date.now() + 60 * 60 * 1000)
769
- });
770
- RingCentral.mockImplementation(() => ({
771
- getCallsAggregationData: jest.fn().mockRejectedValue(new Error('aggregation failed')),
772
- getCallLogData: jest.fn().mockRejectedValue(new Error('call log failed'))
773
- }));
774
-
775
- await expect(adminHandler.getAdminReport({ rcAccountId: 'account' })).resolves.toEqual({
776
- callLogStats: {}
777
- });
778
- await expect(adminHandler.getUserReport({ rcAccountId: 'account', rcExtensionId: '101' })).resolves.toBeNull();
779
- });
780
-
781
- test('getUserMapping updates existing mappings, creates new matches, and handles oauth revocation', async () => {
782
- await AdminConfigModel.create({
783
- id: 'hashed-branch-mapping',
784
- userMappings: [
785
- { crmUserId: 'crm-user-1', rcExtensionId: 'ext-existing' }
786
- ]
787
- });
788
- const user = {
789
- platform: 'testCRM',
790
- accessToken: 'api-key',
791
- platformAdditionalInfo: {}
792
- };
793
- const connector = {
794
- getAuthType: jest.fn().mockResolvedValue('apiKey'),
795
- getBasicAuth: jest.fn(() => 'encoded-key'),
796
- getUserList: jest.fn().mockResolvedValue([
797
- { id: 'crm-user-1', name: 'Existing User', email: 'existing@example.com' },
798
- { id: 'crm-user-2', name: 'Jane Smith', email: 'jane@example.com' },
799
- { id: 'crm-user-3', name: 'No Match', email: 'nomatch@example.com' }
800
- ])
801
- };
802
- connectorRegistry.getConnector.mockReturnValue(connector);
803
-
804
- const result = await adminHandler.getUserMapping({
805
- user,
806
- hashedRcAccountId: 'hashed-branch-mapping',
807
- rcExtensionList: [
808
- { id: 'ext-existing', name: 'Existing RC', email: 'existing@example.com', extensionNumber: '100' },
809
- { id: 'ext-jane', firstName: 'Jane', lastName: 'Smith', email: 'jane@example.com', extensionNumber: '101' }
810
- ]
811
- });
812
-
813
- expect(result).toHaveLength(3);
814
- expect(result[0].rcUser[0].extensionId).toBe('ext-existing');
815
- expect(result[1].rcUser[0].extensionId).toBe('ext-jane');
816
- expect(result[2].rcUser).toEqual([]);
817
- const updatedConfig = await AdminConfigModel.findByPk('hashed-branch-mapping');
818
- expect(updatedConfig.userMappings).toEqual(expect.arrayContaining([
819
- expect.objectContaining({ crmUserId: 'crm-user-2', rcExtensionId: ['ext-jane'] })
820
- ]));
821
-
822
- connectorRegistry.getConnector.mockReturnValue({
823
- getUserList: jest.fn(),
824
- getAuthType: jest.fn().mockResolvedValue('oauth'),
825
- getOauthInfo: jest.fn().mockResolvedValue({})
826
- });
827
- oauth.checkAndRefreshAccessToken.mockResolvedValueOnce(null);
828
- await expect(adminHandler.getUserMapping({
829
- user: {
830
- platform: 'testCRM',
831
- hostname: 'crm.example.com',
832
- accessToken: 'expired-token',
833
- platformAdditionalInfo: {}
834
- },
835
- hashedRcAccountId: 'hashed-branch-mapping',
836
- rcExtensionList: []
837
- })).resolves.toMatchObject({
838
- successful: false,
839
- isRevokeUserSession: true
840
- });
841
- });
842
-
843
- test('getUserMapping initializes mappings when no admin config exists and reports database errors', async () => {
844
- const user = {
845
- platform: 'testCRM',
846
- accessToken: 'api-key',
847
- platformAdditionalInfo: {}
848
- };
849
- connectorRegistry.getConnector.mockReturnValue({
850
- getAuthType: jest.fn().mockResolvedValue('apiKey'),
851
- getBasicAuth: jest.fn(() => 'encoded-key'),
852
- getUserList: jest.fn().mockResolvedValue([
853
- { id: 'crm-user-10', name: 'Alex Agent', email: 'alex@example.com' }
854
- ])
855
- });
856
-
857
- const result = await adminHandler.getUserMapping({
858
- user,
859
- hashedRcAccountId: 'hashed-new-mapping',
860
- rcExtensionList: [
861
- { id: 'ext-alex', firstName: 'Alex', lastName: 'Agent', email: 'alex@example.com', extensionNumber: '201' }
862
- ]
863
- });
864
-
865
- expect(result[0].rcUser[0].extensionId).toBe('ext-alex');
866
- const newConfig = await AdminConfigModel.findByPk('hashed-new-mapping');
867
- expect(newConfig.userMappings).toEqual([
868
- { crmUserId: 'crm-user-10', rcExtensionId: ['ext-alex'] }
869
- ]);
870
-
871
- jest.spyOn(AdminConfigModel, 'findByPk').mockRejectedValueOnce(new Error('read failed'));
872
- const errorResult = await adminHandler.getUserMapping({
873
- user,
874
- hashedRcAccountId: 'hashed-error',
875
- rcExtensionList: []
876
- });
877
- expect(errorResult.successful).toBe(false);
878
- AdminConfigModel.findByPk.mockRestore();
879
- });
880
-
881
- test('reinitializeUserMapping handles missing capability, proxy limits, oauth revoke, apiKey remap, and update failure', async () => {
882
- const baseUser = {
883
- platform: 'testCRM',
884
- accessToken: 'api-key',
885
- platformAdditionalInfo: {}
886
- };
887
-
888
- connectorRegistry.getConnector.mockReturnValueOnce({});
889
- await expect(adminHandler.reinitializeUserMapping({
890
- user: baseUser,
891
- hashedRcAccountId: 'hashed-reinit',
892
- rcExtensionList: []
893
- })).resolves.toEqual([]);
894
-
895
- connectorRegistry.getConnector.mockReturnValueOnce({ getUserList: jest.fn() });
896
- Connector.getProxyConfig.mockResolvedValueOnce({ operations: {} });
897
- await expect(adminHandler.reinitializeUserMapping({
898
- user: {
899
- ...baseUser,
900
- platformAdditionalInfo: { proxyId: 'proxy-1' }
901
- },
902
- hashedRcAccountId: 'hashed-reinit',
903
- rcExtensionList: []
904
- })).resolves.toEqual([]);
905
-
906
- connectorRegistry.getConnector.mockReturnValueOnce({
907
- getUserList: jest.fn(),
908
- getAuthType: jest.fn().mockResolvedValue('oauth'),
909
- getOauthInfo: jest.fn().mockResolvedValue({})
910
- });
911
- oauth.checkAndRefreshAccessToken.mockResolvedValueOnce(null);
912
- await expect(adminHandler.reinitializeUserMapping({
913
- user: {
914
- ...baseUser,
915
- hostname: 'crm.example.com'
916
- },
917
- hashedRcAccountId: 'hashed-reinit',
918
- rcExtensionList: []
919
- })).resolves.toMatchObject({
920
- successful: false,
921
- isRevokeUserSession: true
922
- });
923
-
924
- connectorRegistry.getConnector.mockReturnValueOnce({
925
- getUserList: jest.fn().mockResolvedValue([
926
- { id: 'crm-user-20', name: 'Matched User', email: 'matched@example.com' },
927
- { id: 'crm-user-21', name: 'Unmatched User', email: 'unmatched@example.com' }
928
- ]),
929
- getAuthType: jest.fn().mockResolvedValue('apiKey'),
930
- getBasicAuth: jest.fn(() => 'encoded-key')
931
- });
932
- const result = await adminHandler.reinitializeUserMapping({
933
- user: baseUser,
934
- hashedRcAccountId: 'hashed-reinit',
935
- rcExtensionList: [
936
- { id: 'ext-20', firstName: 'Matched', lastName: 'User', email: 'matched@example.com', extensionNumber: '301' }
937
- ]
938
- });
939
-
940
- expect(result).toHaveLength(2);
941
- expect(result[0].rcUser[0].extensionId).toBe('ext-20');
942
- expect(result[1].rcUser).toEqual([]);
943
-
944
- connectorRegistry.getConnector.mockReturnValueOnce({
945
- getUserList: jest.fn().mockResolvedValue([]),
946
- getAuthType: jest.fn().mockResolvedValue('apiKey'),
947
- getBasicAuth: jest.fn(() => 'encoded-key')
948
- });
949
- jest.spyOn(AdminConfigModel, 'create').mockRejectedValueOnce(new Error('write failed'));
950
- const failure = await adminHandler.reinitializeUserMapping({
951
- user: baseUser,
952
- hashedRcAccountId: 'hashed-reinit-fail',
953
- rcExtensionList: []
954
- });
955
- expect(failure.successful).toBe(false);
956
- AdminConfigModel.create.mockRestore();
957
- });
958
- });
959
- });
960
-