@app-connect/core 1.7.22 → 1.7.24
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/docs/libraries.md +1 -1
- package/handlers/admin.js +7 -4
- package/handlers/auth.js +13 -5
- package/handlers/log.js +217 -109
- package/handlers/plugin.js +183 -1
- package/handlers/user.js +1 -1
- package/index.js +165 -7
- package/lib/authSession.js +44 -12
- package/lib/callLogComposer.js +36 -36
- package/lib/util.js +0 -18
- package/mcp/mcpHandler.js +31 -8
- package/mcp/tools/checkAuthStatus.js +16 -14
- package/mcp/tools/getPublicConnectors.js +11 -1
- package/mcp/tools/getSessionInfo.js +90 -0
- package/mcp/tools/index.js +3 -1
- package/mcp/tools/logout.js +10 -1
- package/models/llmSessionModel.js +3 -0
- package/package.json +1 -1
- package/releaseNotes.json +20 -0
- package/test/handlers/admin.test.js +1 -2
- package/test/handlers/log.test.js +60 -0
- package/test/handlers/plugin.test.js +93 -0
- package/test/lib/callLogComposer.test.js +21 -21
- package/test/lib/util.test.js +1 -332
- package/test/mcp/tools/checkAuthStatus.test.js +82 -0
- package/test/mcp/tools/getSessionInfo.test.js +127 -0
- package/test/mcp/tools/logout.test.js +58 -0
- package/test/routes/managedAuthRoutes.test.js +0 -3
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
const getSessionInfo = require('../../../mcp/tools/getSessionInfo');
|
|
2
|
+
const jwt = require('../../../lib/jwt');
|
|
3
|
+
const { UserModel } = require('../../../models/userModel');
|
|
4
|
+
const { RingCentral } = require('../../../lib/ringcentral');
|
|
5
|
+
|
|
6
|
+
jest.mock('../../../lib/jwt');
|
|
7
|
+
jest.mock('../../../models/userModel');
|
|
8
|
+
jest.mock('../../../lib/ringcentral');
|
|
9
|
+
|
|
10
|
+
describe('MCP Tool: getSessionInfo', () => {
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
jest.clearAllMocks();
|
|
13
|
+
RingCentral.mockImplementation(() => ({
|
|
14
|
+
getExtensionInfo: jest.fn().mockResolvedValue({ name: 'Demo Extension' })
|
|
15
|
+
}));
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
describe('tool definition', () => {
|
|
19
|
+
test('should have correct tool definition', () => {
|
|
20
|
+
expect(getSessionInfo.definition).toBeDefined();
|
|
21
|
+
expect(getSessionInfo.definition.name).toBe('getSessionInfo');
|
|
22
|
+
expect(getSessionInfo.definition.description).toContain('session info');
|
|
23
|
+
expect(getSessionInfo.definition.inputSchema).toBeDefined();
|
|
24
|
+
expect(getSessionInfo.definition.inputSchema.properties).toEqual({});
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
describe('execute', () => {
|
|
29
|
+
test('should return unauthenticated session info when no CRM jwtToken exists', async () => {
|
|
30
|
+
const result = await getSessionInfo.execute({
|
|
31
|
+
openaiSessionId: 'session-123',
|
|
32
|
+
rcExtensionId: 'ext-456',
|
|
33
|
+
rcAccessToken: 'rc-token'
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
expect(result).toEqual({
|
|
37
|
+
success: true,
|
|
38
|
+
data: {
|
|
39
|
+
openaiSessionId: 'session-123',
|
|
40
|
+
dataToShow: {
|
|
41
|
+
isCrmAuthenticated: false,
|
|
42
|
+
ringcentral: {
|
|
43
|
+
extensionId: 'ext-456',
|
|
44
|
+
name: 'Demo Extension',
|
|
45
|
+
},
|
|
46
|
+
crm: {
|
|
47
|
+
userId: null,
|
|
48
|
+
platform: null,
|
|
49
|
+
hostname: null
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
expect(jwt.decodeJwt).not.toHaveBeenCalled();
|
|
55
|
+
expect(UserModel.findByPk).not.toHaveBeenCalled();
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test('should return connected CRM session info when jwtToken resolves to a saved user', async () => {
|
|
59
|
+
jwt.decodeJwt.mockReturnValue({
|
|
60
|
+
id: 'crm-user-1',
|
|
61
|
+
platform: 'clio'
|
|
62
|
+
});
|
|
63
|
+
UserModel.findByPk.mockResolvedValue({
|
|
64
|
+
id: 'crm-user-1',
|
|
65
|
+
platform: 'clio',
|
|
66
|
+
hostname: 'app.clio.com',
|
|
67
|
+
accessToken: 'crm-access-token',
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
const result = await getSessionInfo.execute({
|
|
71
|
+
openaiSessionId: 'session-123',
|
|
72
|
+
rcExtensionId: 'ext-456',
|
|
73
|
+
rcAccessToken: 'rc-token',
|
|
74
|
+
jwtToken: 'jwt-token'
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
expect(jwt.decodeJwt).toHaveBeenCalledWith('jwt-token');
|
|
78
|
+
expect(UserModel.findByPk).toHaveBeenCalledWith('crm-user-1');
|
|
79
|
+
expect(result).toEqual({
|
|
80
|
+
success: true,
|
|
81
|
+
data: {
|
|
82
|
+
openaiSessionId: 'session-123',
|
|
83
|
+
dataToShow: {
|
|
84
|
+
isCrmAuthenticated: true,
|
|
85
|
+
ringcentral: {
|
|
86
|
+
extensionId: 'ext-456',
|
|
87
|
+
name: 'Demo Extension',
|
|
88
|
+
},
|
|
89
|
+
crm: {
|
|
90
|
+
userId: 'crm-user-1',
|
|
91
|
+
platform: 'clio',
|
|
92
|
+
hostname: 'app.clio.com'
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test('should report not authenticated when jwtToken is invalid', async () => {
|
|
100
|
+
jwt.decodeJwt.mockReturnValue(null);
|
|
101
|
+
|
|
102
|
+
const result = await getSessionInfo.execute({
|
|
103
|
+
jwtToken: 'bad-token'
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
expect(result).toEqual({
|
|
107
|
+
success: true,
|
|
108
|
+
data: {
|
|
109
|
+
openaiSessionId: null,
|
|
110
|
+
dataToShow: {
|
|
111
|
+
isCrmAuthenticated: false,
|
|
112
|
+
ringcentral: {
|
|
113
|
+
extensionId: null,
|
|
114
|
+
name: null,
|
|
115
|
+
},
|
|
116
|
+
crm: {
|
|
117
|
+
userId: null,
|
|
118
|
+
platform: null,
|
|
119
|
+
hostname: null
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
expect(UserModel.findByPk).not.toHaveBeenCalled();
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
});
|
|
@@ -1,16 +1,22 @@
|
|
|
1
1
|
const logout = require('../../../mcp/tools/logout');
|
|
2
2
|
const jwt = require('../../../lib/jwt');
|
|
3
3
|
const { UserModel } = require('../../../models/userModel');
|
|
4
|
+
const { LlmSessionModel } = require('../../../models/llmSessionModel');
|
|
5
|
+
const { CacheModel } = require('../../../models/cacheModel');
|
|
4
6
|
const connectorRegistry = require('../../../connector/registry');
|
|
5
7
|
|
|
6
8
|
// Mock dependencies
|
|
7
9
|
jest.mock('../../../lib/jwt');
|
|
8
10
|
jest.mock('../../../models/userModel');
|
|
11
|
+
jest.mock('../../../models/llmSessionModel');
|
|
12
|
+
jest.mock('../../../models/cacheModel');
|
|
9
13
|
jest.mock('../../../connector/registry');
|
|
10
14
|
|
|
11
15
|
describe('MCP Tool: logout', () => {
|
|
12
16
|
beforeEach(() => {
|
|
13
17
|
jest.clearAllMocks();
|
|
18
|
+
LlmSessionModel.destroy.mockResolvedValue(1);
|
|
19
|
+
CacheModel.destroy.mockResolvedValue(1);
|
|
14
20
|
});
|
|
15
21
|
|
|
16
22
|
describe('tool definition', () => {
|
|
@@ -65,6 +71,7 @@ describe('MCP Tool: logout', () => {
|
|
|
65
71
|
}
|
|
66
72
|
});
|
|
67
73
|
expect(jwt.decodeJwt).toHaveBeenCalledWith('mock-jwt-token');
|
|
74
|
+
expect(LlmSessionModel.destroy).toHaveBeenCalledWith({ where: { id: 'test-user-id' } });
|
|
68
75
|
expect(UserModel.findByPk).toHaveBeenCalledWith('test-user-id');
|
|
69
76
|
expect(connectorRegistry.getConnector).toHaveBeenCalledWith('testCRM');
|
|
70
77
|
expect(mockConnector.unAuthorize).toHaveBeenCalledWith({
|
|
@@ -170,6 +177,57 @@ describe('MCP Tool: logout', () => {
|
|
|
170
177
|
expect(consoleSpy).toHaveBeenCalled();
|
|
171
178
|
consoleSpy.mockRestore();
|
|
172
179
|
});
|
|
180
|
+
|
|
181
|
+
test('should clear both CRM user and RC extension session rows when rcExtensionId is provided', async () => {
|
|
182
|
+
const mockUser = {
|
|
183
|
+
id: 'test-user-id',
|
|
184
|
+
platform: 'testCRM'
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
jwt.decodeJwt.mockReturnValue({
|
|
188
|
+
id: 'test-user-id',
|
|
189
|
+
platform: 'testCRM'
|
|
190
|
+
});
|
|
191
|
+
UserModel.findByPk.mockResolvedValue(mockUser);
|
|
192
|
+
connectorRegistry.getConnector.mockReturnValue({
|
|
193
|
+
unAuthorize: jest.fn().mockResolvedValue({})
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
const result = await logout.execute({
|
|
197
|
+
jwtToken: 'mock-jwt-token',
|
|
198
|
+
rcExtensionId: 'rc-ext-123'
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
expect(result.success).toBe(true);
|
|
202
|
+
expect(LlmSessionModel.destroy).toHaveBeenNthCalledWith(1, { where: { id: 'test-user-id' } });
|
|
203
|
+
expect(LlmSessionModel.destroy).toHaveBeenNthCalledWith(2, { where: { id: 'rc-ext-123' } });
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
test('should clear the resolved rcExtension cache for the current OpenAI session on logout', async () => {
|
|
207
|
+
const mockUser = {
|
|
208
|
+
id: 'test-user-id',
|
|
209
|
+
platform: 'testCRM'
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
jwt.decodeJwt.mockReturnValue({
|
|
213
|
+
id: 'test-user-id',
|
|
214
|
+
platform: 'testCRM'
|
|
215
|
+
});
|
|
216
|
+
UserModel.findByPk.mockResolvedValue(mockUser);
|
|
217
|
+
connectorRegistry.getConnector.mockReturnValue({
|
|
218
|
+
unAuthorize: jest.fn().mockResolvedValue({})
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
const result = await logout.execute({
|
|
222
|
+
jwtToken: 'mock-jwt-token',
|
|
223
|
+
openaiSessionId: 'oa-session-123'
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
expect(result.success).toBe(true);
|
|
227
|
+
expect(CacheModel.destroy).toHaveBeenCalledWith({
|
|
228
|
+
where: { id: 'oa-session-123-rcExtensionId' }
|
|
229
|
+
});
|
|
230
|
+
});
|
|
173
231
|
});
|
|
174
232
|
});
|
|
175
233
|
|
|
@@ -47,7 +47,6 @@ describe('Managed Auth Routes', () => {
|
|
|
47
47
|
adminCore.validateRcUserToken.mockResolvedValue({
|
|
48
48
|
rcAccountId: 'validated-account-id',
|
|
49
49
|
rcExtensionId: 'validated-extension-id',
|
|
50
|
-
rcUserName: 'Validated User',
|
|
51
50
|
});
|
|
52
51
|
managedAuthCore.getManagedAuthState.mockResolvedValue({
|
|
53
52
|
hasManagedAuth: true,
|
|
@@ -95,7 +94,6 @@ describe('Managed Auth Routes', () => {
|
|
|
95
94
|
adminCore.validateRcUserToken.mockResolvedValue({
|
|
96
95
|
rcAccountId: 'validated-account-id',
|
|
97
96
|
rcExtensionId: 'validated-extension-id',
|
|
98
|
-
rcUserName: 'Validated User',
|
|
99
97
|
});
|
|
100
98
|
authCore.onApiKeyLogin.mockResolvedValue({
|
|
101
99
|
userInfo: {
|
|
@@ -125,7 +123,6 @@ describe('Managed Auth Routes', () => {
|
|
|
125
123
|
platform: 'testCRM',
|
|
126
124
|
rcAccountId: 'validated-account-id',
|
|
127
125
|
rcExtensionId: 'validated-extension-id',
|
|
128
|
-
rcUserName: 'Validated User',
|
|
129
126
|
}));
|
|
130
127
|
});
|
|
131
128
|
});
|