@app-connect/core 1.7.24 → 1.7.26
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/.env.test +5 -5
- package/README.md +441 -441
- package/connector/developerPortal.js +31 -42
- package/connector/mock.js +84 -77
- package/connector/proxy/engine.js +164 -163
- package/connector/proxy/index.js +500 -500
- package/connector/registry.js +252 -252
- package/docs/README.md +50 -50
- package/docs/architecture.md +93 -93
- package/docs/connectors.md +116 -117
- package/docs/handlers.md +125 -125
- package/docs/libraries.md +101 -101
- package/docs/models.md +144 -144
- package/docs/routes.md +115 -115
- package/docs/tests.md +73 -73
- package/handlers/admin.js +523 -523
- package/handlers/appointment.js +193 -0
- package/handlers/auth.js +296 -296
- package/handlers/calldown.js +99 -99
- package/handlers/contact.js +280 -280
- package/handlers/disposition.js +82 -80
- package/handlers/log.js +984 -973
- package/handlers/managedAuth.js +446 -446
- package/handlers/plugin.js +208 -208
- package/handlers/user.js +142 -142
- package/index.js +3140 -2652
- package/jest.config.js +56 -56
- package/lib/analytics.js +54 -54
- package/lib/authSession.js +109 -109
- package/lib/cacheCleanup.js +21 -0
- package/lib/callLogComposer.js +898 -898
- package/lib/callLogLookup.js +34 -0
- package/lib/constants.js +8 -8
- package/lib/debugTracer.js +177 -177
- package/lib/encode.js +30 -30
- package/lib/errorHandler.js +218 -206
- package/lib/generalErrorMessage.js +41 -41
- package/lib/jwt.js +18 -18
- package/lib/logger.js +190 -190
- package/lib/migrateCallLogsSchema.js +116 -0
- package/lib/ringcentral.js +266 -266
- package/lib/s3ErrorLogReport.js +65 -65
- package/lib/sharedSMSComposer.js +471 -471
- package/lib/util.js +67 -67
- package/mcp/README.md +412 -395
- package/mcp/lib/validator.js +91 -91
- package/mcp/mcpHandler.js +425 -425
- package/mcp/tools/cancelAppointment.js +101 -0
- package/mcp/tools/checkAuthStatus.js +105 -105
- package/mcp/tools/confirmAppointment.js +101 -0
- package/mcp/tools/createAppointment.js +157 -0
- package/mcp/tools/createCallLog.js +327 -316
- package/mcp/tools/createContact.js +117 -117
- package/mcp/tools/createMessageLog.js +287 -287
- package/mcp/tools/doAuth.js +60 -60
- package/mcp/tools/findContactByName.js +93 -93
- package/mcp/tools/findContactByPhone.js +101 -101
- package/mcp/tools/getCallLog.js +111 -102
- package/mcp/tools/getGoogleFilePicker.js +99 -99
- package/mcp/tools/getHelp.js +43 -43
- package/mcp/tools/getPublicConnectors.js +94 -94
- package/mcp/tools/getSessionInfo.js +90 -90
- package/mcp/tools/index.js +51 -41
- package/mcp/tools/listAppointments.js +163 -0
- package/mcp/tools/logout.js +96 -96
- package/mcp/tools/rcGetCallLogs.js +65 -65
- package/mcp/tools/updateAppointment.js +154 -0
- package/mcp/tools/updateCallLog.js +130 -126
- package/mcp/ui/App/App.tsx +358 -358
- package/mcp/ui/App/components/AuthInfoForm.tsx +113 -113
- package/mcp/ui/App/components/AuthSuccess.tsx +22 -22
- package/mcp/ui/App/components/ConnectorList.tsx +82 -82
- package/mcp/ui/App/components/DebugPanel.tsx +43 -43
- package/mcp/ui/App/components/OAuthConnect.tsx +270 -270
- package/mcp/ui/App/lib/callTool.ts +130 -130
- package/mcp/ui/App/lib/debugLog.ts +41 -41
- package/mcp/ui/App/lib/developerPortal.ts +111 -111
- package/mcp/ui/App/main.css +5 -5
- package/mcp/ui/App/root.tsx +13 -13
- package/mcp/ui/index.html +13 -13
- package/mcp/ui/package-lock.json +6356 -6356
- package/mcp/ui/package.json +25 -25
- package/mcp/ui/tsconfig.json +26 -26
- package/mcp/ui/vite.config.ts +16 -16
- package/models/accountDataModel.js +33 -33
- package/models/adminConfigModel.js +35 -35
- package/models/cacheModel.js +30 -26
- package/models/callDownListModel.js +34 -34
- package/models/callLogModel.js +33 -27
- package/models/dynamo/connectorSchema.js +146 -146
- package/models/dynamo/lockSchema.js +24 -24
- package/models/dynamo/noteCacheSchema.js +29 -29
- package/models/llmSessionModel.js +17 -17
- package/models/messageLogModel.js +25 -25
- package/models/sequelize.js +16 -16
- package/models/userModel.js +45 -45
- package/package.json +72 -72
- package/releaseNotes.json +1093 -1073
- package/test/connector/proxy/engine.test.js +126 -93
- package/test/connector/proxy/index.test.js +279 -279
- package/test/connector/proxy/sample.json +161 -161
- package/test/connector/registry.test.js +415 -415
- package/test/handlers/admin.test.js +616 -616
- package/test/handlers/auth.test.js +1018 -1015
- package/test/handlers/contact.test.js +1014 -1014
- package/test/handlers/log.test.js +1298 -1160
- package/test/handlers/managedAuth.test.js +458 -458
- package/test/handlers/plugin.test.js +380 -380
- package/test/index.test.js +105 -105
- package/test/lib/cacheCleanup.test.js +42 -0
- package/test/lib/callLogComposer.test.js +1231 -1231
- package/test/lib/debugTracer.test.js +328 -328
- package/test/lib/jwt.test.js +176 -176
- package/test/lib/logger.test.js +206 -206
- package/test/lib/oauth.test.js +359 -359
- package/test/lib/ringcentral.test.js +467 -467
- package/test/lib/sharedSMSComposer.test.js +1084 -1084
- package/test/lib/util.test.js +329 -329
- package/test/mcp/tools/checkAuthStatus.test.js +83 -82
- package/test/mcp/tools/createCallLog.test.js +436 -436
- package/test/mcp/tools/createContact.test.js +58 -58
- package/test/mcp/tools/createMessageLog.test.js +595 -595
- package/test/mcp/tools/doAuth.test.js +113 -113
- package/test/mcp/tools/findContactByName.test.js +275 -275
- package/test/mcp/tools/findContactByPhone.test.js +296 -296
- package/test/mcp/tools/getCallLog.test.js +298 -298
- package/test/mcp/tools/getGoogleFilePicker.test.js +281 -281
- package/test/mcp/tools/getPublicConnectors.test.js +107 -107
- package/test/mcp/tools/getSessionInfo.test.js +127 -127
- package/test/mcp/tools/logout.test.js +233 -233
- package/test/mcp/tools/rcGetCallLogs.test.js +56 -56
- package/test/mcp/tools/updateCallLog.test.js +360 -360
- package/test/models/accountDataModel.test.js +98 -98
- package/test/models/dynamo/connectorSchema.test.js +189 -189
- package/test/models/models.test.js +568 -539
- package/test/routes/managedAuthRoutes.test.js +104 -129
- package/test/setup.js +178 -178
package/test/index.test.js
CHANGED
|
@@ -1,105 +1,105 @@
|
|
|
1
|
-
const express = require('express');
|
|
2
|
-
const request = require('supertest');
|
|
3
|
-
|
|
4
|
-
jest.mock('../lib/jwt', () => ({
|
|
5
|
-
decodeJwt: jest.fn(),
|
|
6
|
-
generateJwt: jest.fn(),
|
|
7
|
-
}));
|
|
8
|
-
jest.mock('../handlers/auth', () => ({
|
|
9
|
-
authValidation: jest.fn(),
|
|
10
|
-
}));
|
|
11
|
-
jest.mock('../lib/analytics', () => ({
|
|
12
|
-
init: jest.fn(),
|
|
13
|
-
track: jest.fn(),
|
|
14
|
-
}));
|
|
15
|
-
|
|
16
|
-
const jwt = require('../lib/jwt');
|
|
17
|
-
const authCore = require('../handlers/auth');
|
|
18
|
-
const { createCoreRouter, createCoreMiddleware } = require('../index');
|
|
19
|
-
|
|
20
|
-
function buildApp() {
|
|
21
|
-
const app = express();
|
|
22
|
-
createCoreMiddleware().forEach((m) => app.use(m));
|
|
23
|
-
app.use('/', createCoreRouter());
|
|
24
|
-
return app;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
describe('Core Router JWT normalization', () => {
|
|
28
|
-
beforeEach(() => {
|
|
29
|
-
jest.clearAllMocks();
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
test('should accept query jwtToken without refreshing it', async () => {
|
|
33
|
-
jwt.decodeJwt.mockReturnValue({ id: 'user-1', platform: 'testCRM' });
|
|
34
|
-
authCore.authValidation.mockResolvedValue({
|
|
35
|
-
successful: true,
|
|
36
|
-
returnMessage: { message: 'ok' },
|
|
37
|
-
failReason: null,
|
|
38
|
-
status: 200,
|
|
39
|
-
});
|
|
40
|
-
const app = buildApp();
|
|
41
|
-
|
|
42
|
-
const response = await request(app).get('/authValidation?jwtToken=query-token');
|
|
43
|
-
|
|
44
|
-
expect(response.status).toBe(200);
|
|
45
|
-
expect(response.headers['x-refreshed-jwt-token']).toBeUndefined();
|
|
46
|
-
expect(authCore.authValidation).toHaveBeenCalledWith({
|
|
47
|
-
platform: 'testCRM',
|
|
48
|
-
userId: 'user-1',
|
|
49
|
-
});
|
|
50
|
-
expect(jwt.generateJwt).not.toHaveBeenCalled();
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
test('should refresh near-expiry bearer token and expose header', async () => {
|
|
54
|
-
const nowMs = 1700000000000;
|
|
55
|
-
const nowSeconds = Math.floor(nowMs / 1000);
|
|
56
|
-
const nowSpy = jest.spyOn(Date, 'now').mockReturnValue(nowMs);
|
|
57
|
-
jwt.decodeJwt.mockImplementation((token) => {
|
|
58
|
-
if (token === 'old-token') {
|
|
59
|
-
return { id: 'user-1', platform: 'testCRM', exp: nowSeconds + 60 };
|
|
60
|
-
}
|
|
61
|
-
if (token === 'new-token') {
|
|
62
|
-
return { id: 'user-1', platform: 'testCRM', exp: nowSeconds + (14 * 24 * 60 * 60) };
|
|
63
|
-
}
|
|
64
|
-
return null;
|
|
65
|
-
});
|
|
66
|
-
jwt.generateJwt.mockReturnValue('new-token');
|
|
67
|
-
const app = buildApp();
|
|
68
|
-
|
|
69
|
-
const response = await request(app)
|
|
70
|
-
.get('/isAlive')
|
|
71
|
-
.set('Authorization', 'Bearer old-token')
|
|
72
|
-
.set('Origin', 'https://example.com');
|
|
73
|
-
|
|
74
|
-
expect(response.status).toBe(200);
|
|
75
|
-
expect(response.headers['x-refreshed-jwt-token']).toBe('new-token');
|
|
76
|
-
expect(response.headers['access-control-expose-headers']).toContain('x-refreshed-jwt-token');
|
|
77
|
-
expect(jwt.generateJwt).toHaveBeenCalledWith({ id: 'user-1', platform: 'testCRM' });
|
|
78
|
-
nowSpy.mockRestore();
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
test('should treat invalid bearer token as unauthenticated for authValidation route', async () => {
|
|
82
|
-
jwt.decodeJwt.mockReturnValue(null);
|
|
83
|
-
const app = buildApp();
|
|
84
|
-
|
|
85
|
-
const response = await request(app)
|
|
86
|
-
.get('/authValidation?jwtToken=query-token')
|
|
87
|
-
.set('Authorization', 'Bearer invalid-token');
|
|
88
|
-
|
|
89
|
-
expect(response.status).toBe(400);
|
|
90
|
-
expect(response.text).toContain('authorize CRM platform');
|
|
91
|
-
expect(authCore.authValidation).not.toHaveBeenCalled();
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
test('should bypass normalization for /mcp routes', async () => {
|
|
95
|
-
const app = buildApp();
|
|
96
|
-
|
|
97
|
-
const response = await request(app)
|
|
98
|
-
.get('/mcp')
|
|
99
|
-
.set('Authorization', 'Bearer maybe-token');
|
|
100
|
-
|
|
101
|
-
expect(response.status).toBe(404);
|
|
102
|
-
expect(jwt.decodeJwt).not.toHaveBeenCalled();
|
|
103
|
-
});
|
|
104
|
-
});
|
|
105
|
-
|
|
1
|
+
const express = require('express');
|
|
2
|
+
const request = require('supertest');
|
|
3
|
+
|
|
4
|
+
jest.mock('../lib/jwt', () => ({
|
|
5
|
+
decodeJwt: jest.fn(),
|
|
6
|
+
generateJwt: jest.fn(),
|
|
7
|
+
}));
|
|
8
|
+
jest.mock('../handlers/auth', () => ({
|
|
9
|
+
authValidation: jest.fn(),
|
|
10
|
+
}));
|
|
11
|
+
jest.mock('../lib/analytics', () => ({
|
|
12
|
+
init: jest.fn(),
|
|
13
|
+
track: jest.fn(),
|
|
14
|
+
}));
|
|
15
|
+
|
|
16
|
+
const jwt = require('../lib/jwt');
|
|
17
|
+
const authCore = require('../handlers/auth');
|
|
18
|
+
const { createCoreRouter, createCoreMiddleware } = require('../index');
|
|
19
|
+
|
|
20
|
+
function buildApp() {
|
|
21
|
+
const app = express();
|
|
22
|
+
createCoreMiddleware().forEach((m) => app.use(m));
|
|
23
|
+
app.use('/', createCoreRouter());
|
|
24
|
+
return app;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
describe('Core Router JWT normalization', () => {
|
|
28
|
+
beforeEach(() => {
|
|
29
|
+
jest.clearAllMocks();
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test('should accept query jwtToken without refreshing it', async () => {
|
|
33
|
+
jwt.decodeJwt.mockReturnValue({ id: 'user-1', platform: 'testCRM' });
|
|
34
|
+
authCore.authValidation.mockResolvedValue({
|
|
35
|
+
successful: true,
|
|
36
|
+
returnMessage: { message: 'ok' },
|
|
37
|
+
failReason: null,
|
|
38
|
+
status: 200,
|
|
39
|
+
});
|
|
40
|
+
const app = buildApp();
|
|
41
|
+
|
|
42
|
+
const response = await request(app).get('/authValidation?jwtToken=query-token');
|
|
43
|
+
|
|
44
|
+
expect(response.status).toBe(200);
|
|
45
|
+
expect(response.headers['x-refreshed-jwt-token']).toBeUndefined();
|
|
46
|
+
expect(authCore.authValidation).toHaveBeenCalledWith({
|
|
47
|
+
platform: 'testCRM',
|
|
48
|
+
userId: 'user-1',
|
|
49
|
+
});
|
|
50
|
+
expect(jwt.generateJwt).not.toHaveBeenCalled();
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test('should refresh near-expiry bearer token and expose header', async () => {
|
|
54
|
+
const nowMs = 1700000000000;
|
|
55
|
+
const nowSeconds = Math.floor(nowMs / 1000);
|
|
56
|
+
const nowSpy = jest.spyOn(Date, 'now').mockReturnValue(nowMs);
|
|
57
|
+
jwt.decodeJwt.mockImplementation((token) => {
|
|
58
|
+
if (token === 'old-token') {
|
|
59
|
+
return { id: 'user-1', platform: 'testCRM', exp: nowSeconds + 60 };
|
|
60
|
+
}
|
|
61
|
+
if (token === 'new-token') {
|
|
62
|
+
return { id: 'user-1', platform: 'testCRM', exp: nowSeconds + (14 * 24 * 60 * 60) };
|
|
63
|
+
}
|
|
64
|
+
return null;
|
|
65
|
+
});
|
|
66
|
+
jwt.generateJwt.mockReturnValue('new-token');
|
|
67
|
+
const app = buildApp();
|
|
68
|
+
|
|
69
|
+
const response = await request(app)
|
|
70
|
+
.get('/isAlive')
|
|
71
|
+
.set('Authorization', 'Bearer old-token')
|
|
72
|
+
.set('Origin', 'https://example.com');
|
|
73
|
+
|
|
74
|
+
expect(response.status).toBe(200);
|
|
75
|
+
expect(response.headers['x-refreshed-jwt-token']).toBe('new-token');
|
|
76
|
+
expect(response.headers['access-control-expose-headers']).toContain('x-refreshed-jwt-token');
|
|
77
|
+
expect(jwt.generateJwt).toHaveBeenCalledWith({ id: 'user-1', platform: 'testCRM' });
|
|
78
|
+
nowSpy.mockRestore();
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test('should treat invalid bearer token as unauthenticated for authValidation route', async () => {
|
|
82
|
+
jwt.decodeJwt.mockReturnValue(null);
|
|
83
|
+
const app = buildApp();
|
|
84
|
+
|
|
85
|
+
const response = await request(app)
|
|
86
|
+
.get('/authValidation?jwtToken=query-token')
|
|
87
|
+
.set('Authorization', 'Bearer invalid-token');
|
|
88
|
+
|
|
89
|
+
expect(response.status).toBe(400);
|
|
90
|
+
expect(response.text).toContain('authorize CRM platform');
|
|
91
|
+
expect(authCore.authValidation).not.toHaveBeenCalled();
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test('should bypass normalization for /mcp routes', async () => {
|
|
95
|
+
const app = buildApp();
|
|
96
|
+
|
|
97
|
+
const response = await request(app)
|
|
98
|
+
.get('/mcp')
|
|
99
|
+
.set('Authorization', 'Bearer maybe-token');
|
|
100
|
+
|
|
101
|
+
expect(response.status).toBe(404);
|
|
102
|
+
expect(jwt.decodeJwt).not.toHaveBeenCalled();
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
const { CacheModel } = require('../../models/cacheModel');
|
|
2
|
+
const { clearExpiredCache } = require('../../lib/cacheCleanup');
|
|
3
|
+
|
|
4
|
+
describe('cacheCleanup', () => {
|
|
5
|
+
beforeEach(async () => {
|
|
6
|
+
await CacheModel.destroy({ where: {} });
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
test('deletes only cache records whose expiry has passed', async () => {
|
|
10
|
+
const now = new Date('2026-05-08T00:00:00.000Z');
|
|
11
|
+
|
|
12
|
+
await CacheModel.bulkCreate([
|
|
13
|
+
{
|
|
14
|
+
id: 'expired-cache',
|
|
15
|
+
userId: 'user-1',
|
|
16
|
+
cacheKey: 'auth-session',
|
|
17
|
+
status: 'expired',
|
|
18
|
+
expiry: new Date('2026-05-07T23:59:59.000Z')
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
id: 'active-cache',
|
|
22
|
+
userId: 'user-1',
|
|
23
|
+
cacheKey: 'auth-session',
|
|
24
|
+
status: 'pending',
|
|
25
|
+
expiry: new Date('2026-05-08T00:00:01.000Z')
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
id: 'cache-without-expiry',
|
|
29
|
+
userId: 'user-2',
|
|
30
|
+
cacheKey: 'contacts',
|
|
31
|
+
status: 'active'
|
|
32
|
+
}
|
|
33
|
+
]);
|
|
34
|
+
|
|
35
|
+
const deletedCount = await clearExpiredCache({ now });
|
|
36
|
+
|
|
37
|
+
expect(deletedCount).toBe(1);
|
|
38
|
+
expect(await CacheModel.findByPk('expired-cache')).toBeNull();
|
|
39
|
+
expect(await CacheModel.findByPk('active-cache')).not.toBeNull();
|
|
40
|
+
expect(await CacheModel.findByPk('cache-without-expiry')).not.toBeNull();
|
|
41
|
+
});
|
|
42
|
+
});
|