@app-connect/core 1.7.21 → 1.7.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -1
- package/connector/developerPortal.js +4 -4
- package/docs/README.md +50 -0
- package/docs/architecture.md +93 -0
- package/docs/connectors.md +117 -0
- package/docs/handlers.md +125 -0
- package/docs/libraries.md +101 -0
- package/docs/models.md +144 -0
- package/docs/routes.md +115 -0
- package/docs/tests.md +73 -0
- package/handlers/admin.js +22 -2
- package/handlers/auth.js +57 -10
- package/handlers/log.js +217 -109
- package/handlers/managedAuth.js +446 -0
- package/handlers/plugin.js +183 -1
- package/handlers/user.js +1 -1
- package/index.js +410 -35
- package/lib/callLogComposer.js +36 -36
- package/lib/jwt.js +1 -1
- package/lib/util.js +0 -18
- package/mcp/tools/createCallLog.js +5 -1
- package/mcp/tools/createContact.js +5 -1
- package/mcp/tools/createMessageLog.js +5 -1
- package/mcp/tools/findContactByName.js +5 -1
- package/mcp/tools/findContactByPhone.js +6 -2
- package/mcp/tools/getCallLog.js +5 -1
- package/mcp/tools/rcGetCallLogs.js +6 -2
- package/mcp/tools/updateCallLog.js +5 -1
- package/mcp/ui/App/lib/developerPortal.ts +1 -1
- package/package.json +72 -72
- package/releaseNotes.json +16 -0
- package/test/handlers/admin.test.js +33 -0
- package/test/handlers/auth.test.js +402 -6
- package/test/handlers/log.test.js +60 -0
- package/test/handlers/managedAuth.test.js +458 -0
- package/test/handlers/plugin.test.js +93 -0
- package/test/index.test.js +105 -0
- package/test/lib/callLogComposer.test.js +21 -21
- package/test/lib/jwt.test.js +15 -0
- package/test/lib/util.test.js +1 -332
- package/test/mcp/tools/createCallLog.test.js +11 -0
- package/test/mcp/tools/createContact.test.js +58 -0
- package/test/mcp/tools/createMessageLog.test.js +15 -0
- package/test/mcp/tools/findContactByName.test.js +12 -0
- package/test/mcp/tools/findContactByPhone.test.js +12 -0
- package/test/mcp/tools/getCallLog.test.js +12 -0
- package/test/mcp/tools/rcGetCallLogs.test.js +56 -0
- package/test/mcp/tools/updateCallLog.test.js +14 -0
- package/test/routes/managedAuthRoutes.test.js +129 -0
- package/test/setup.js +2 -0
|
@@ -22,6 +22,8 @@ const oauth = require('../../lib/oauth');
|
|
|
22
22
|
const { Connector } = require('../../models/dynamo/connectorSchema');
|
|
23
23
|
const { RingCentral } = require('../../lib/ringcentral');
|
|
24
24
|
const adminCore = require('../../handlers/admin');
|
|
25
|
+
const { AccountDataModel } = require('../../models/accountDataModel');
|
|
26
|
+
const { encode } = require('../../lib/encode');
|
|
25
27
|
|
|
26
28
|
describe('Auth Handler', () => {
|
|
27
29
|
const originalEnv = process.env;
|
|
@@ -31,6 +33,7 @@ describe('Auth Handler', () => {
|
|
|
31
33
|
jest.clearAllMocks();
|
|
32
34
|
global.testUtils.resetConnectorRegistry();
|
|
33
35
|
process.env = { ...originalEnv };
|
|
36
|
+
process.env.APP_SERVER_SECRET_KEY = 'test-app-server-secret-key-123456';
|
|
34
37
|
});
|
|
35
38
|
|
|
36
39
|
afterEach(() => {
|
|
@@ -38,6 +41,10 @@ describe('Auth Handler', () => {
|
|
|
38
41
|
});
|
|
39
42
|
|
|
40
43
|
describe('onApiKeyLogin', () => {
|
|
44
|
+
afterEach(async () => {
|
|
45
|
+
await AccountDataModel.destroy({ where: {} });
|
|
46
|
+
});
|
|
47
|
+
|
|
41
48
|
test('should handle successful API key login', async () => {
|
|
42
49
|
// Arrange
|
|
43
50
|
const mockUserInfo = {
|
|
@@ -60,7 +67,7 @@ describe('Auth Handler', () => {
|
|
|
60
67
|
getBasicAuth: jest.fn().mockReturnValue('dGVzdC1hcGkta2V5Og=='),
|
|
61
68
|
getUserInfo: jest.fn().mockResolvedValue(mockUserInfo)
|
|
62
69
|
});
|
|
63
|
-
|
|
70
|
+
|
|
64
71
|
connectorRegistry.getConnector.mockReturnValue(mockConnector);
|
|
65
72
|
|
|
66
73
|
const requestData = {
|
|
@@ -82,7 +89,7 @@ describe('Auth Handler', () => {
|
|
|
82
89
|
expect(mockConnector.getUserInfo).toHaveBeenCalledWith({
|
|
83
90
|
authHeader: 'Basic dGVzdC1hcGkta2V5Og==',
|
|
84
91
|
hostname: 'test.example.com',
|
|
85
|
-
additionalInfo: {},
|
|
92
|
+
additionalInfo: { apiKey: 'test-api-key' },
|
|
86
93
|
apiKey: 'test-api-key',
|
|
87
94
|
platform: 'testCRM',
|
|
88
95
|
proxyId: undefined
|
|
@@ -105,7 +112,7 @@ describe('Auth Handler', () => {
|
|
|
105
112
|
getBasicAuth: jest.fn().mockReturnValue('dGVzdC1hcGkta2V5Og=='),
|
|
106
113
|
getUserInfo: jest.fn().mockResolvedValue(mockUserInfo)
|
|
107
114
|
});
|
|
108
|
-
|
|
115
|
+
|
|
109
116
|
connectorRegistry.getConnector.mockReturnValue(mockConnector);
|
|
110
117
|
|
|
111
118
|
const requestData = {
|
|
@@ -123,6 +130,394 @@ describe('Auth Handler', () => {
|
|
|
123
130
|
expect(result.returnMessage).toEqual(mockUserInfo.returnMessage);
|
|
124
131
|
});
|
|
125
132
|
|
|
133
|
+
test('should mark managed auth auto-login failure so the next attempt can fall back to manual auth', async () => {
|
|
134
|
+
connectorRegistry.getManifest.mockReturnValue({
|
|
135
|
+
platforms: {
|
|
136
|
+
testCRM: {
|
|
137
|
+
auth: {
|
|
138
|
+
type: 'apiKey',
|
|
139
|
+
apiKey: {
|
|
140
|
+
page: {
|
|
141
|
+
content: [
|
|
142
|
+
{ const: 'tenantId', required: true, managed: true, managedScope: 'account' },
|
|
143
|
+
{ const: 'apiKey', required: true, managed: true, managedScope: 'user' }
|
|
144
|
+
]
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
await AccountDataModel.create({
|
|
153
|
+
rcAccountId: 'rc-account-fail',
|
|
154
|
+
platformName: 'testCRM',
|
|
155
|
+
dataKey: 'managed-auth-org',
|
|
156
|
+
data: {
|
|
157
|
+
fields: {
|
|
158
|
+
tenantId: { version: 1, encrypted: true, value: encode(JSON.stringify('tenant-1')) }
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
await AccountDataModel.create({
|
|
163
|
+
rcAccountId: 'rc-account-fail',
|
|
164
|
+
platformName: 'testCRM',
|
|
165
|
+
dataKey: 'managed-auth-user:101',
|
|
166
|
+
data: {
|
|
167
|
+
rcExtensionId: '101',
|
|
168
|
+
rcUserName: 'Agent 101',
|
|
169
|
+
fields: {
|
|
170
|
+
apiKey: { version: 1, encrypted: true, value: encode(JSON.stringify('bad-stored-key')) }
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
const mockConnector = global.testUtils.createMockConnector({
|
|
176
|
+
getBasicAuth: jest.fn().mockReturnValue('encoded-bad-key'),
|
|
177
|
+
getUserInfo: jest.fn().mockResolvedValue({
|
|
178
|
+
successful: false,
|
|
179
|
+
platformUserInfo: null,
|
|
180
|
+
returnMessage: {
|
|
181
|
+
messageType: 'error',
|
|
182
|
+
message: 'Invalid API key',
|
|
183
|
+
ttl: 3000
|
|
184
|
+
}
|
|
185
|
+
})
|
|
186
|
+
});
|
|
187
|
+
connectorRegistry.getConnector.mockReturnValue(mockConnector);
|
|
188
|
+
|
|
189
|
+
const result = await authHandler.onApiKeyLogin({
|
|
190
|
+
platform: 'testCRM',
|
|
191
|
+
hostname: 'test.example.com',
|
|
192
|
+
rcAccountId: 'rc-account-fail',
|
|
193
|
+
rcExtensionId: '101',
|
|
194
|
+
additionalInfo: {}
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
expect(result.userInfo).toBeNull();
|
|
198
|
+
const failureRecord = await AccountDataModel.findOne({
|
|
199
|
+
where: {
|
|
200
|
+
rcAccountId: 'rc-account-fail',
|
|
201
|
+
platformName: 'testCRM',
|
|
202
|
+
dataKey: 'managed-auth-login-failure:101'
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
expect(failureRecord).not.toBeNull();
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
test('should merge stored org managed auth values into additionalInfo', async () => {
|
|
209
|
+
connectorRegistry.getManifest.mockReturnValue({
|
|
210
|
+
platforms: {
|
|
211
|
+
testCRM: {
|
|
212
|
+
auth: {
|
|
213
|
+
type: 'apiKey',
|
|
214
|
+
apiKey: {
|
|
215
|
+
page: {
|
|
216
|
+
content: [
|
|
217
|
+
{ const: 'apiKey', required: true, managed: true, managedScope: 'account' },
|
|
218
|
+
{ const: 'tenantId', required: true, managed: true, managedScope: 'account' },
|
|
219
|
+
{ const: 'userToken', required: true }
|
|
220
|
+
]
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
await AccountDataModel.create({
|
|
228
|
+
rcAccountId: 'rc-account-1',
|
|
229
|
+
platformName: 'testCRM',
|
|
230
|
+
dataKey: 'managed-auth-org',
|
|
231
|
+
data: {
|
|
232
|
+
fields: {
|
|
233
|
+
apiKey: { version: 1, encrypted: true, value: encode(JSON.stringify('stored-api-key')) },
|
|
234
|
+
tenantId: { version: 1, encrypted: true, value: encode(JSON.stringify('tenant-1')) }
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
const mockUserInfo = {
|
|
240
|
+
successful: true,
|
|
241
|
+
platformUserInfo: {
|
|
242
|
+
id: 'test-user-id',
|
|
243
|
+
name: 'Test User',
|
|
244
|
+
platformAdditionalInfo: {}
|
|
245
|
+
},
|
|
246
|
+
returnMessage: { messageType: 'success', message: 'ok' }
|
|
247
|
+
};
|
|
248
|
+
const mockConnector = global.testUtils.createMockConnector({
|
|
249
|
+
getBasicAuth: jest.fn().mockReturnValue('encoded-shared'),
|
|
250
|
+
getUserInfo: jest.fn().mockResolvedValue(mockUserInfo)
|
|
251
|
+
});
|
|
252
|
+
connectorRegistry.getConnector.mockReturnValue(mockConnector);
|
|
253
|
+
|
|
254
|
+
await authHandler.onApiKeyLogin({
|
|
255
|
+
platform: 'testCRM',
|
|
256
|
+
hostname: 'test.example.com',
|
|
257
|
+
rcAccountId: 'rc-account-1',
|
|
258
|
+
additionalInfo: { userToken: 'user-token-1' }
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
expect(mockConnector.getBasicAuth).toHaveBeenCalledWith({ apiKey: 'stored-api-key' });
|
|
262
|
+
expect(mockConnector.getUserInfo).toHaveBeenCalledWith(expect.objectContaining({
|
|
263
|
+
additionalInfo: expect.objectContaining({
|
|
264
|
+
apiKey: 'stored-api-key',
|
|
265
|
+
tenantId: 'tenant-1',
|
|
266
|
+
userToken: 'user-token-1'
|
|
267
|
+
})
|
|
268
|
+
}));
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
test('should allow submitted shared fields to satisfy missing required managed auth values', async () => {
|
|
272
|
+
connectorRegistry.getManifest.mockReturnValue({
|
|
273
|
+
platforms: {
|
|
274
|
+
testCRM: {
|
|
275
|
+
auth: {
|
|
276
|
+
type: 'apiKey',
|
|
277
|
+
apiKey: {
|
|
278
|
+
page: {
|
|
279
|
+
content: [
|
|
280
|
+
{ const: 'companyId', required: true, managed: true, managedScope: 'account' },
|
|
281
|
+
{ const: 'userToken', required: true }
|
|
282
|
+
]
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
const mockConnector = global.testUtils.createMockConnector({
|
|
291
|
+
getBasicAuth: jest.fn(),
|
|
292
|
+
getUserInfo: jest.fn().mockResolvedValue({
|
|
293
|
+
successful: true,
|
|
294
|
+
platformUserInfo: {
|
|
295
|
+
id: 'test-user-id',
|
|
296
|
+
name: 'Test User',
|
|
297
|
+
platformAdditionalInfo: {}
|
|
298
|
+
},
|
|
299
|
+
returnMessage: { messageType: 'success', message: 'ok' }
|
|
300
|
+
})
|
|
301
|
+
});
|
|
302
|
+
connectorRegistry.getConnector.mockReturnValue(mockConnector);
|
|
303
|
+
|
|
304
|
+
const result = await authHandler.onApiKeyLogin({
|
|
305
|
+
platform: 'testCRM',
|
|
306
|
+
hostname: 'test.example.com',
|
|
307
|
+
rcAccountId: 'rc-account-2',
|
|
308
|
+
additionalInfo: {
|
|
309
|
+
companyId: 'company-123',
|
|
310
|
+
userToken: 'user-token-1'
|
|
311
|
+
}
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
expect(result.userInfo).not.toBeNull();
|
|
315
|
+
expect(mockConnector.getUserInfo).toHaveBeenCalledWith(expect.objectContaining({
|
|
316
|
+
additionalInfo: expect.objectContaining({
|
|
317
|
+
companyId: 'company-123',
|
|
318
|
+
userToken: 'user-token-1'
|
|
319
|
+
})
|
|
320
|
+
}));
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
test('should not persist submitted managed auth values from end users', async () => {
|
|
324
|
+
connectorRegistry.getManifest.mockReturnValue({
|
|
325
|
+
platforms: {
|
|
326
|
+
testCRM: {
|
|
327
|
+
auth: {
|
|
328
|
+
type: 'apiKey',
|
|
329
|
+
apiKey: {
|
|
330
|
+
page: {
|
|
331
|
+
content: [
|
|
332
|
+
{ const: 'companyId', required: false, managed: true, managedScope: 'account' },
|
|
333
|
+
{ const: 'userToken', required: true }
|
|
334
|
+
]
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
const mockUserInfo = {
|
|
343
|
+
successful: true,
|
|
344
|
+
platformUserInfo: {
|
|
345
|
+
id: 'test-user-id',
|
|
346
|
+
name: 'Test User',
|
|
347
|
+
platformAdditionalInfo: {}
|
|
348
|
+
},
|
|
349
|
+
returnMessage: { messageType: 'success', message: 'ok' }
|
|
350
|
+
};
|
|
351
|
+
const mockConnector = global.testUtils.createMockConnector({
|
|
352
|
+
getBasicAuth: jest.fn().mockReturnValue('encoded'),
|
|
353
|
+
getUserInfo: jest.fn().mockResolvedValue(mockUserInfo)
|
|
354
|
+
});
|
|
355
|
+
connectorRegistry.getConnector.mockReturnValue(mockConnector);
|
|
356
|
+
|
|
357
|
+
await authHandler.onApiKeyLogin({
|
|
358
|
+
platform: 'testCRM',
|
|
359
|
+
hostname: 'test.example.com',
|
|
360
|
+
rcAccountId: 'rc-account-2',
|
|
361
|
+
additionalInfo: {
|
|
362
|
+
companyId: 'company-123',
|
|
363
|
+
userToken: 'user-token-1'
|
|
364
|
+
}
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
expect(mockConnector.getUserInfo).toHaveBeenCalledWith(expect.objectContaining({
|
|
368
|
+
additionalInfo: expect.objectContaining({
|
|
369
|
+
companyId: 'company-123',
|
|
370
|
+
userToken: 'user-token-1'
|
|
371
|
+
})
|
|
372
|
+
}));
|
|
373
|
+
|
|
374
|
+
const stored = await AccountDataModel.findOne({
|
|
375
|
+
where: {
|
|
376
|
+
rcAccountId: 'rc-account-2',
|
|
377
|
+
platformName: 'testCRM',
|
|
378
|
+
dataKey: 'managed-auth-org'
|
|
379
|
+
}
|
|
380
|
+
});
|
|
381
|
+
expect(stored).toBeNull();
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
test('should allow manual fallback values to override stored managed credentials and clear failure state after success', async () => {
|
|
385
|
+
connectorRegistry.getManifest.mockReturnValue({
|
|
386
|
+
platforms: {
|
|
387
|
+
testCRM: {
|
|
388
|
+
auth: {
|
|
389
|
+
type: 'apiKey',
|
|
390
|
+
apiKey: {
|
|
391
|
+
page: {
|
|
392
|
+
content: [
|
|
393
|
+
{ const: 'apiKey', required: true, managed: true, managedScope: 'user' },
|
|
394
|
+
{ const: 'tenantId', required: true, managed: true, managedScope: 'account' }
|
|
395
|
+
]
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
await AccountDataModel.create({
|
|
404
|
+
rcAccountId: 'rc-account-recover',
|
|
405
|
+
platformName: 'testCRM',
|
|
406
|
+
dataKey: 'managed-auth-org',
|
|
407
|
+
data: {
|
|
408
|
+
fields: {
|
|
409
|
+
tenantId: { version: 1, encrypted: true, value: encode(JSON.stringify('stored-tenant')) }
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
});
|
|
413
|
+
await AccountDataModel.create({
|
|
414
|
+
rcAccountId: 'rc-account-recover',
|
|
415
|
+
platformName: 'testCRM',
|
|
416
|
+
dataKey: 'managed-auth-user:202',
|
|
417
|
+
data: {
|
|
418
|
+
rcExtensionId: '202',
|
|
419
|
+
rcUserName: 'Agent 202',
|
|
420
|
+
fields: {
|
|
421
|
+
apiKey: { version: 1, encrypted: true, value: encode(JSON.stringify('stored-bad-key')) }
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
});
|
|
425
|
+
await AccountDataModel.create({
|
|
426
|
+
rcAccountId: 'rc-account-recover',
|
|
427
|
+
platformName: 'testCRM',
|
|
428
|
+
dataKey: 'managed-auth-login-failure:202',
|
|
429
|
+
data: {
|
|
430
|
+
failedAt: '2026-04-07T00:00:00.000Z'
|
|
431
|
+
}
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
const mockConnector = global.testUtils.createMockConnector({
|
|
435
|
+
getBasicAuth: jest.fn().mockReturnValue('encoded-manual-key'),
|
|
436
|
+
getUserInfo: jest.fn().mockResolvedValue({
|
|
437
|
+
successful: true,
|
|
438
|
+
platformUserInfo: {
|
|
439
|
+
id: 'test-user-id',
|
|
440
|
+
name: 'Recovered User',
|
|
441
|
+
platformAdditionalInfo: {}
|
|
442
|
+
},
|
|
443
|
+
returnMessage: { messageType: 'success', message: 'ok' }
|
|
444
|
+
})
|
|
445
|
+
});
|
|
446
|
+
connectorRegistry.getConnector.mockReturnValue(mockConnector);
|
|
447
|
+
|
|
448
|
+
const result = await authHandler.onApiKeyLogin({
|
|
449
|
+
platform: 'testCRM',
|
|
450
|
+
hostname: 'test.example.com',
|
|
451
|
+
rcAccountId: 'rc-account-recover',
|
|
452
|
+
rcExtensionId: '202',
|
|
453
|
+
additionalInfo: {
|
|
454
|
+
apiKey: 'manual-good-key',
|
|
455
|
+
tenantId: 'manual-tenant'
|
|
456
|
+
}
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
expect(result.userInfo).not.toBeNull();
|
|
460
|
+
expect(mockConnector.getBasicAuth).toHaveBeenCalledWith({ apiKey: 'manual-good-key' });
|
|
461
|
+
expect(mockConnector.getUserInfo).toHaveBeenCalledWith(expect.objectContaining({
|
|
462
|
+
additionalInfo: {
|
|
463
|
+
apiKey: 'manual-good-key',
|
|
464
|
+
tenantId: 'manual-tenant'
|
|
465
|
+
}
|
|
466
|
+
}));
|
|
467
|
+
|
|
468
|
+
const failureRecord = await AccountDataModel.findOne({
|
|
469
|
+
where: {
|
|
470
|
+
rcAccountId: 'rc-account-recover',
|
|
471
|
+
platformName: 'testCRM',
|
|
472
|
+
dataKey: 'managed-auth-login-failure:202'
|
|
473
|
+
}
|
|
474
|
+
});
|
|
475
|
+
expect(failureRecord).toBeNull();
|
|
476
|
+
});
|
|
477
|
+
|
|
478
|
+
test('should return warning when required auth fields are missing', async () => {
|
|
479
|
+
connectorRegistry.getManifest.mockReturnValue({
|
|
480
|
+
platforms: {
|
|
481
|
+
testCRM: {
|
|
482
|
+
auth: {
|
|
483
|
+
type: 'apiKey',
|
|
484
|
+
apiKey: {
|
|
485
|
+
page: {
|
|
486
|
+
content: [
|
|
487
|
+
{ const: 'tenantId', required: true, managed: true, managedScope: 'account' },
|
|
488
|
+
{ const: 'userToken', required: true }
|
|
489
|
+
]
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
const mockConnector = global.testUtils.createMockConnector({
|
|
498
|
+
getBasicAuth: jest.fn(),
|
|
499
|
+
getUserInfo: jest.fn()
|
|
500
|
+
});
|
|
501
|
+
connectorRegistry.getConnector.mockReturnValue(mockConnector);
|
|
502
|
+
|
|
503
|
+
const result = await authHandler.onApiKeyLogin({
|
|
504
|
+
platform: 'testCRM',
|
|
505
|
+
hostname: 'test.example.com',
|
|
506
|
+
rcAccountId: 'rc-account-4',
|
|
507
|
+
additionalInfo: {}
|
|
508
|
+
});
|
|
509
|
+
|
|
510
|
+
expect(result.userInfo).toBeNull();
|
|
511
|
+
expect(result.returnMessage).toEqual({
|
|
512
|
+
messageType: 'warning',
|
|
513
|
+
message: 'Missing required authentication fields.',
|
|
514
|
+
ttl: 3000,
|
|
515
|
+
missingRequiredFieldConsts: ['tenantId', 'userToken']
|
|
516
|
+
});
|
|
517
|
+
expect(mockConnector.getBasicAuth).not.toHaveBeenCalled();
|
|
518
|
+
expect(mockConnector.getUserInfo).not.toHaveBeenCalled();
|
|
519
|
+
});
|
|
520
|
+
|
|
126
521
|
test('should throw error when connector not found', async () => {
|
|
127
522
|
// Arrange
|
|
128
523
|
connectorRegistry.getConnector.mockImplementation(() => {
|
|
@@ -160,7 +555,7 @@ describe('Auth Handler', () => {
|
|
|
160
555
|
getOauthInfo: jest.fn().mockResolvedValue({}),
|
|
161
556
|
authValidation: jest.fn().mockResolvedValue(mockValidationResponse)
|
|
162
557
|
});
|
|
163
|
-
|
|
558
|
+
|
|
164
559
|
connectorRegistry.getConnector.mockReturnValue(mockConnector);
|
|
165
560
|
|
|
166
561
|
// Mock UserModel.findOne to return a user
|
|
@@ -229,7 +624,7 @@ describe('Auth Handler', () => {
|
|
|
229
624
|
getOauthInfo: jest.fn().mockResolvedValue({}),
|
|
230
625
|
authValidation: jest.fn().mockResolvedValue(mockValidationResponse)
|
|
231
626
|
});
|
|
232
|
-
|
|
627
|
+
|
|
233
628
|
connectorRegistry.getConnector.mockReturnValue(mockConnector);
|
|
234
629
|
|
|
235
630
|
// Mock UserModel.findOne to return a user
|
|
@@ -616,4 +1011,5 @@ describe('Auth Handler', () => {
|
|
|
616
1011
|
expect(RingCentral).not.toHaveBeenCalled();
|
|
617
1012
|
});
|
|
618
1013
|
});
|
|
619
|
-
});
|
|
1014
|
+
});
|
|
1015
|
+
|
|
@@ -24,6 +24,7 @@ jest.mock('../../models/dynamo/connectorSchema', () => ({
|
|
|
24
24
|
getProxyConfig: jest.fn()
|
|
25
25
|
}
|
|
26
26
|
}));
|
|
27
|
+
jest.mock('axios');
|
|
27
28
|
|
|
28
29
|
const logHandler = require('../../handlers/log');
|
|
29
30
|
const { CallLogModel } = require('../../models/callLogModel');
|
|
@@ -34,6 +35,7 @@ const connectorRegistry = require('../../connector/registry');
|
|
|
34
35
|
const oauth = require('../../lib/oauth');
|
|
35
36
|
const { composeCallLog } = require('../../lib/callLogComposer');
|
|
36
37
|
const { NoteCache } = require('../../models/dynamo/noteCacheSchema');
|
|
38
|
+
const axios = require('axios');
|
|
37
39
|
const { sequelize } = require('../../models/sequelize');
|
|
38
40
|
|
|
39
41
|
describe('Log Handler', () => {
|
|
@@ -61,6 +63,7 @@ describe('Log Handler', () => {
|
|
|
61
63
|
id: 'test-user-id',
|
|
62
64
|
platform: 'testCRM',
|
|
63
65
|
accessToken: 'test-access-token',
|
|
66
|
+
rcAccountId: '12345',
|
|
64
67
|
platformAdditionalInfo: {}
|
|
65
68
|
};
|
|
66
69
|
|
|
@@ -217,6 +220,63 @@ describe('Log Handler', () => {
|
|
|
217
220
|
expect(savedLog.thirdPartyLogId).toBe('new-log-123');
|
|
218
221
|
});
|
|
219
222
|
|
|
223
|
+
test('should call plugin with Bearer auth and without query jwt token', async () => {
|
|
224
|
+
await UserModel.create(mockUser);
|
|
225
|
+
await AccountDataModel.create({
|
|
226
|
+
rcAccountId: mockUser.rcAccountId,
|
|
227
|
+
platformName: 'testPlugin',
|
|
228
|
+
dataKey: 'pluginData',
|
|
229
|
+
data: {
|
|
230
|
+
name: 'plugin.sample',
|
|
231
|
+
supportedLogTypes: ['call'],
|
|
232
|
+
isAsync: false,
|
|
233
|
+
endpointUrl: 'https://plugins.example.com/plugin/testPlugin',
|
|
234
|
+
jwtToken: 'plugin-jwt-token'
|
|
235
|
+
}
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
const mockConnector = {
|
|
239
|
+
getAuthType: jest.fn().mockResolvedValue('apiKey'),
|
|
240
|
+
getBasicAuth: jest.fn().mockReturnValue('base64-encoded'),
|
|
241
|
+
getLogFormatType: jest.fn().mockReturnValue('text/plain'),
|
|
242
|
+
createCallLog: jest.fn().mockResolvedValue({
|
|
243
|
+
logId: 'new-log-123',
|
|
244
|
+
returnMessage: { message: 'Call logged', messageType: 'success', ttl: 2000 }
|
|
245
|
+
})
|
|
246
|
+
};
|
|
247
|
+
connectorRegistry.getConnector.mockReturnValue(mockConnector);
|
|
248
|
+
composeCallLog.mockReturnValue('Composed log details');
|
|
249
|
+
|
|
250
|
+
axios.post.mockResolvedValue({
|
|
251
|
+
data: {
|
|
252
|
+
...mockIncomingData,
|
|
253
|
+
note: 'updated by plugin'
|
|
254
|
+
},
|
|
255
|
+
headers: {
|
|
256
|
+
'x-refreshed-jwt-token': 'refreshed-plugin-jwt'
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
const result = await logHandler.createCallLog({
|
|
261
|
+
platform: 'testCRM',
|
|
262
|
+
userId: 'test-user-id',
|
|
263
|
+
incomingData: mockIncomingData,
|
|
264
|
+
hashedAccountId: 'hashed-123',
|
|
265
|
+
isFromSSCL: false
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
expect(result.successful).toBe(true);
|
|
269
|
+
expect(axios.post).toHaveBeenCalledWith(
|
|
270
|
+
'https://plugins.example.com/plugin/testPlugin',
|
|
271
|
+
{ data: mockIncomingData, config: null },
|
|
272
|
+
{
|
|
273
|
+
headers: {
|
|
274
|
+
Authorization: 'Bearer plugin-jwt-token'
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
);
|
|
278
|
+
});
|
|
279
|
+
|
|
220
280
|
test('should successfully create call log with oauth auth', async () => {
|
|
221
281
|
// Arrange
|
|
222
282
|
const oauthUser = { ...mockUser };
|