@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,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
-