@app-connect/core 1.7.18 → 1.7.19
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/connector/proxy/index.js +2 -1
- package/handlers/log.js +181 -10
- package/handlers/plugin.js +27 -0
- package/handlers/user.js +31 -2
- package/index.js +99 -22
- package/lib/authSession.js +21 -12
- package/lib/callLogComposer.js +1 -1
- package/lib/debugTracer.js +20 -2
- package/lib/util.js +21 -4
- package/mcp/README.md +392 -0
- package/mcp/mcpHandler.js +293 -82
- package/mcp/tools/checkAuthStatus.js +27 -34
- package/mcp/tools/createCallLog.js +13 -9
- package/mcp/tools/createContact.js +2 -6
- package/mcp/tools/doAuth.js +27 -157
- package/mcp/tools/findContactByName.js +6 -9
- package/mcp/tools/findContactByPhone.js +2 -6
- package/mcp/tools/getGoogleFilePicker.js +5 -9
- package/mcp/tools/getHelp.js +2 -3
- package/mcp/tools/getPublicConnectors.js +41 -28
- package/mcp/tools/index.js +11 -36
- package/mcp/tools/logout.js +5 -10
- package/mcp/tools/rcGetCallLogs.js +3 -20
- package/mcp/ui/App/App.tsx +361 -0
- package/mcp/ui/App/components/AuthInfoForm.tsx +113 -0
- package/mcp/ui/App/components/AuthSuccess.tsx +22 -0
- package/mcp/ui/App/components/ConnectorList.tsx +82 -0
- package/mcp/ui/App/components/DebugPanel.tsx +43 -0
- package/mcp/ui/App/components/OAuthConnect.tsx +270 -0
- package/mcp/ui/App/lib/callTool.ts +130 -0
- package/mcp/ui/App/lib/debugLog.ts +41 -0
- package/mcp/ui/App/lib/developerPortal.ts +111 -0
- package/mcp/ui/App/main.css +6 -0
- package/mcp/ui/App/root.tsx +13 -0
- package/mcp/ui/dist/index.html +53 -0
- package/mcp/ui/index.html +13 -0
- package/mcp/ui/package-lock.json +6356 -0
- package/mcp/ui/package.json +25 -0
- package/mcp/ui/tsconfig.json +26 -0
- package/mcp/ui/vite.config.ts +16 -0
- package/models/llmSessionModel.js +14 -0
- package/package.json +72 -72
- package/releaseNotes.json +12 -0
- package/test/handlers/plugin.test.js +287 -0
- package/test/lib/util.test.js +379 -1
- package/test/mcp/tools/createCallLog.test.js +3 -3
- package/test/mcp/tools/doAuth.test.js +40 -303
- package/test/mcp/tools/findContactByName.test.js +3 -3
- package/test/mcp/tools/findContactByPhone.test.js +3 -3
- package/test/mcp/tools/getGoogleFilePicker.test.js +7 -7
- package/test/mcp/tools/getPublicConnectors.test.js +49 -70
- package/test/mcp/tools/logout.test.js +2 -2
- package/mcp/SupportedPlatforms.md +0 -12
- package/mcp/tools/collectAuthInfo.js +0 -91
- package/mcp/tools/setConnector.js +0 -69
- package/test/mcp/tools/collectAuthInfo.test.js +0 -234
- package/test/mcp/tools/setConnector.test.js +0 -177
|
@@ -21,8 +21,8 @@ describe('MCP Tool: logout', () => {
|
|
|
21
21
|
expect(logout.definition.inputSchema).toBeDefined();
|
|
22
22
|
});
|
|
23
23
|
|
|
24
|
-
test('should have jwtToken
|
|
25
|
-
expect(logout.definition.inputSchema.properties).
|
|
24
|
+
test('should have empty properties (jwtToken is server-injected, not in schema)', () => {
|
|
25
|
+
expect(logout.definition.inputSchema.properties).toEqual({});
|
|
26
26
|
});
|
|
27
27
|
});
|
|
28
28
|
|
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* MCP Tool: User Authentication
|
|
3
|
-
*
|
|
4
|
-
* This tool authenticates the user with the CRM platform.
|
|
5
|
-
* It uses the platform-specific connector to authenticate the user.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
const { isManifestValid } = require('../lib/validator');
|
|
9
|
-
|
|
10
|
-
const toolDefinition = {
|
|
11
|
-
name: 'collectAuthInfo',
|
|
12
|
-
description: '(This step is skipped if auth type is "apiKey" or environment type is "fixed", this is a MUST if environment type is "dynamic" or "selectable") Auth flow step.3. Get information that is required for authentication. Next step is calling step.4 "doAuth" tool.',
|
|
13
|
-
inputSchema: {
|
|
14
|
-
type: 'object',
|
|
15
|
-
properties: {
|
|
16
|
-
connectorManifest: {
|
|
17
|
-
type: 'object',
|
|
18
|
-
description: 'connectorManifest variable from above conversation. Must be the full manifest object, not just serverUrl'
|
|
19
|
-
},
|
|
20
|
-
hostname: {
|
|
21
|
-
type: 'string',
|
|
22
|
-
description: 'For "dynamic" type environment. User is to login to CRM account then copy and paste the hostname over here.'
|
|
23
|
-
},
|
|
24
|
-
selection: {
|
|
25
|
-
type: 'string',
|
|
26
|
-
description: 'For "selectable" type environment. User is to select a name (NOT value) of the options from the selectable list'
|
|
27
|
-
},
|
|
28
|
-
connectorName: {
|
|
29
|
-
type: 'string',
|
|
30
|
-
description: 'connectorName variable from above conversation.'
|
|
31
|
-
}
|
|
32
|
-
},
|
|
33
|
-
required: ['connectorManifest', 'connectorName']
|
|
34
|
-
},
|
|
35
|
-
annotations: {
|
|
36
|
-
readOnlyHint: true,
|
|
37
|
-
openWorldHint: false,
|
|
38
|
-
destructiveHint: false
|
|
39
|
-
}
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Execute the collectAuthInfo tool
|
|
44
|
-
* @param {Object} args - The tool arguments
|
|
45
|
-
* @param {string} args.connectorManifest - Connector manifest from conversation or memory.
|
|
46
|
-
* @param {string} args.hostname - For "dynamic" type environment. User is to login to CRM account then copy and paste the hostname over here.
|
|
47
|
-
* @param {string} args.selection - For "selectable" type environment. User is to select a name (NOT value) of the options from the selectable list
|
|
48
|
-
* @param {string} args.connectorName - Connector name from conversation or memory.
|
|
49
|
-
* @returns {Object} Result object with hostname or selection
|
|
50
|
-
*/
|
|
51
|
-
async function execute(args) {
|
|
52
|
-
try {
|
|
53
|
-
const { connectorManifest, hostname, selection, connectorName } = args;
|
|
54
|
-
const { isValid, errors } = isManifestValid({ connectorManifest, connectorName });
|
|
55
|
-
if (!isValid) {
|
|
56
|
-
return {
|
|
57
|
-
success: false,
|
|
58
|
-
error: "Invalid connector manifest",
|
|
59
|
-
errorDetails: errors.join(', '),
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
let result = '';
|
|
63
|
-
switch (connectorManifest.platforms[connectorName].environment.type) {
|
|
64
|
-
case 'selectable':
|
|
65
|
-
result = connectorManifest.platforms[connectorName].environment.selections.find(s => s.name === selection).const;
|
|
66
|
-
break;
|
|
67
|
-
case 'dynamic':
|
|
68
|
-
result = hostname;
|
|
69
|
-
break;
|
|
70
|
-
}
|
|
71
|
-
const url = new URL(result);
|
|
72
|
-
return {
|
|
73
|
-
success: true,
|
|
74
|
-
data: {
|
|
75
|
-
hostname: url.hostname,
|
|
76
|
-
// Add explicit instruction
|
|
77
|
-
message: "IMPORTANT: Use hostname in the next few authentication steps.",
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
catch (error) {
|
|
82
|
-
return {
|
|
83
|
-
success: false,
|
|
84
|
-
error: error.message || 'Unknown error occurred',
|
|
85
|
-
errorDetails: error.stack
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
exports.definition = toolDefinition;
|
|
91
|
-
exports.execute = execute;
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
const developerPortal = require('../../connector/developerPortal');
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* MCP Tool: Set Connector
|
|
5
|
-
*
|
|
6
|
-
* This tool helps the user set the connector.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
const toolDefinition = {
|
|
10
|
-
name: 'setConnector',
|
|
11
|
-
description: 'Auth flow step.2. Save connectorManifest to memory if successful.',
|
|
12
|
-
inputSchema: {
|
|
13
|
-
type: 'object',
|
|
14
|
-
properties: {
|
|
15
|
-
connectorDisplayName: {
|
|
16
|
-
type: 'string',
|
|
17
|
-
description: 'Connector displayname to set'
|
|
18
|
-
}
|
|
19
|
-
},
|
|
20
|
-
required: ['connectorDisplayName']
|
|
21
|
-
},
|
|
22
|
-
annotations: {
|
|
23
|
-
readOnlyHint: true,
|
|
24
|
-
openWorldHint: false,
|
|
25
|
-
destructiveHint: false
|
|
26
|
-
}
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Execute the setConnector tool
|
|
31
|
-
* @param {Object} args - The tool arguments
|
|
32
|
-
* @param {string} args.connectorDisplayName - Connector display name to set
|
|
33
|
-
* @returns {Object} Result object with connector information
|
|
34
|
-
*/
|
|
35
|
-
async function execute(args) {
|
|
36
|
-
try {
|
|
37
|
-
const { connectorDisplayName } = args;
|
|
38
|
-
const { connectors: publicConnectorList } = await developerPortal.getPublicConnectorList();
|
|
39
|
-
const { privateConnectors } = await developerPortal.getPrivateConnectorList();
|
|
40
|
-
const connectorList = [...publicConnectorList, ...privateConnectors];
|
|
41
|
-
const connector = connectorList.find(c => c.displayName === connectorDisplayName);
|
|
42
|
-
const connectorName = connector.name;
|
|
43
|
-
const connectorManifest = await developerPortal.getConnectorManifest({ connectorId: connector.id, isPrivate: connector.status === 'private' });
|
|
44
|
-
if (!connectorManifest) {
|
|
45
|
-
throw new Error(`Connector manifest not found: ${connectorDisplayName}`);
|
|
46
|
-
}
|
|
47
|
-
return {
|
|
48
|
-
success: true,
|
|
49
|
-
data: {
|
|
50
|
-
connectorManifest,
|
|
51
|
-
connectorDisplayName,
|
|
52
|
-
connectorName,
|
|
53
|
-
// Add explicit instruction
|
|
54
|
-
message: "IMPORTANT: Use connectorManifest, connectorDisplayName, and connectorName in the next few authentication steps. Call 'collectAuthInfo' tool if the connector is oauth, unless connectorManifest.platform[0].environment.type == 'fixed'.",
|
|
55
|
-
|
|
56
|
-
}
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
catch (error) {
|
|
60
|
-
return {
|
|
61
|
-
success: false,
|
|
62
|
-
error: error.message || 'Unknown error occurred',
|
|
63
|
-
errorDetails: error.stack
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
exports.definition = toolDefinition;
|
|
69
|
-
exports.execute = execute;
|
|
@@ -1,234 +0,0 @@
|
|
|
1
|
-
const collectAuthInfo = require('../../../mcp/tools/collectAuthInfo');
|
|
2
|
-
|
|
3
|
-
describe('MCP Tool: collectAuthInfo', () => {
|
|
4
|
-
describe('tool definition', () => {
|
|
5
|
-
test('should have correct tool definition', () => {
|
|
6
|
-
expect(collectAuthInfo.definition).toBeDefined();
|
|
7
|
-
expect(collectAuthInfo.definition.name).toBe('collectAuthInfo');
|
|
8
|
-
expect(collectAuthInfo.definition.description).toContain('Auth flow step.3');
|
|
9
|
-
expect(collectAuthInfo.definition.inputSchema).toBeDefined();
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
test('should require connectorManifest and connectorName parameters', () => {
|
|
13
|
-
expect(collectAuthInfo.definition.inputSchema.required).toContain('connectorManifest');
|
|
14
|
-
expect(collectAuthInfo.definition.inputSchema.required).toContain('connectorName');
|
|
15
|
-
});
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
describe('execute', () => {
|
|
19
|
-
test('should handle selectable environment type', async () => {
|
|
20
|
-
// Arrange
|
|
21
|
-
const mockManifest = {
|
|
22
|
-
platforms: {
|
|
23
|
-
salesforce: {
|
|
24
|
-
name: 'salesforce',
|
|
25
|
-
auth: {
|
|
26
|
-
type: 'oauth',
|
|
27
|
-
oauth: {
|
|
28
|
-
authUrl: 'https://login.salesforce.com/services/oauth2/authorize',
|
|
29
|
-
clientId: 'test-client-id'
|
|
30
|
-
}
|
|
31
|
-
},
|
|
32
|
-
environment: {
|
|
33
|
-
type: 'selectable',
|
|
34
|
-
selections: [
|
|
35
|
-
{ name: 'Production', const: 'https://login.salesforce.com' },
|
|
36
|
-
{ name: 'Sandbox', const: 'https://test.salesforce.com' }
|
|
37
|
-
]
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
// Act
|
|
44
|
-
const result = await collectAuthInfo.execute({
|
|
45
|
-
connectorManifest: mockManifest,
|
|
46
|
-
connectorName: 'salesforce',
|
|
47
|
-
selection: 'Production'
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
// Assert
|
|
51
|
-
expect(result).toEqual({
|
|
52
|
-
success: true,
|
|
53
|
-
data: {
|
|
54
|
-
hostname: 'login.salesforce.com',
|
|
55
|
-
message: expect.stringContaining('IMPORTANT')
|
|
56
|
-
}
|
|
57
|
-
});
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
test('should handle dynamic environment type', async () => {
|
|
61
|
-
// Arrange
|
|
62
|
-
const mockManifest = {
|
|
63
|
-
platforms: {
|
|
64
|
-
netsuite: {
|
|
65
|
-
name: 'netsuite',
|
|
66
|
-
auth: {
|
|
67
|
-
type: 'oauth',
|
|
68
|
-
oauth: {
|
|
69
|
-
authUrl: 'https://system.netsuite.com/app/login/oauth2/authorize.nl',
|
|
70
|
-
clientId: 'test-client-id'
|
|
71
|
-
}
|
|
72
|
-
},
|
|
73
|
-
environment: {
|
|
74
|
-
type: 'dynamic'
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
// Act
|
|
81
|
-
const result = await collectAuthInfo.execute({
|
|
82
|
-
connectorManifest: mockManifest,
|
|
83
|
-
connectorName: 'netsuite',
|
|
84
|
-
hostname: 'https://1234567.app.netsuite.com'
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
// Assert
|
|
88
|
-
expect(result).toEqual({
|
|
89
|
-
success: true,
|
|
90
|
-
data: {
|
|
91
|
-
hostname: '1234567.app.netsuite.com',
|
|
92
|
-
message: expect.stringContaining('IMPORTANT')
|
|
93
|
-
}
|
|
94
|
-
});
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
test('should handle sandbox selection', async () => {
|
|
98
|
-
// Arrange
|
|
99
|
-
const mockManifest = {
|
|
100
|
-
platforms: {
|
|
101
|
-
salesforce: {
|
|
102
|
-
name: 'salesforce',
|
|
103
|
-
auth: {
|
|
104
|
-
type: 'oauth',
|
|
105
|
-
oauth: {
|
|
106
|
-
authUrl: 'https://login.salesforce.com/services/oauth2/authorize',
|
|
107
|
-
clientId: 'test-client-id'
|
|
108
|
-
}
|
|
109
|
-
},
|
|
110
|
-
environment: {
|
|
111
|
-
type: 'selectable',
|
|
112
|
-
selections: [
|
|
113
|
-
{ name: 'Production', const: 'https://login.salesforce.com' },
|
|
114
|
-
{ name: 'Sandbox', const: 'https://test.salesforce.com' }
|
|
115
|
-
]
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
};
|
|
120
|
-
|
|
121
|
-
// Act
|
|
122
|
-
const result = await collectAuthInfo.execute({
|
|
123
|
-
connectorManifest: mockManifest,
|
|
124
|
-
connectorName: 'salesforce',
|
|
125
|
-
selection: 'Sandbox'
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
// Assert
|
|
129
|
-
expect(result.success).toBe(true);
|
|
130
|
-
expect(result.data.hostname).toBe('test.salesforce.com');
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
test('should handle invalid hostname URL', async () => {
|
|
134
|
-
// Arrange
|
|
135
|
-
const mockManifest = {
|
|
136
|
-
platforms: {
|
|
137
|
-
netsuite: {
|
|
138
|
-
name: 'netsuite',
|
|
139
|
-
auth: {
|
|
140
|
-
type: 'oauth',
|
|
141
|
-
oauth: {
|
|
142
|
-
authUrl: 'https://system.netsuite.com/app/login/oauth2/authorize.nl',
|
|
143
|
-
clientId: 'test-client-id'
|
|
144
|
-
}
|
|
145
|
-
},
|
|
146
|
-
environment: {
|
|
147
|
-
type: 'dynamic'
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
};
|
|
152
|
-
|
|
153
|
-
// Act
|
|
154
|
-
const result = await collectAuthInfo.execute({
|
|
155
|
-
connectorManifest: mockManifest,
|
|
156
|
-
connectorName: 'netsuite',
|
|
157
|
-
hostname: 'invalid-url'
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
// Assert
|
|
161
|
-
expect(result.success).toBe(false);
|
|
162
|
-
expect(result.error).toBeDefined();
|
|
163
|
-
expect(result.errorDetails).toBeDefined();
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
test('should handle missing selection for selectable type', async () => {
|
|
167
|
-
// Arrange
|
|
168
|
-
const mockManifest = {
|
|
169
|
-
platforms: {
|
|
170
|
-
salesforce: {
|
|
171
|
-
name: 'salesforce',
|
|
172
|
-
auth: {
|
|
173
|
-
type: 'oauth',
|
|
174
|
-
oauth: {
|
|
175
|
-
authUrl: 'https://login.salesforce.com/services/oauth2/authorize',
|
|
176
|
-
clientId: 'test-client-id'
|
|
177
|
-
}
|
|
178
|
-
},
|
|
179
|
-
environment: {
|
|
180
|
-
type: 'selectable',
|
|
181
|
-
selections: [
|
|
182
|
-
{ name: 'Production', const: 'https://login.salesforce.com' }
|
|
183
|
-
]
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
};
|
|
188
|
-
|
|
189
|
-
// Act
|
|
190
|
-
const result = await collectAuthInfo.execute({
|
|
191
|
-
connectorManifest: mockManifest,
|
|
192
|
-
connectorName: 'salesforce',
|
|
193
|
-
selection: 'NonExistent'
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
// Assert
|
|
197
|
-
expect(result.success).toBe(false);
|
|
198
|
-
expect(result.error).toBeDefined();
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
test('should handle missing hostname for dynamic type', async () => {
|
|
202
|
-
// Arrange
|
|
203
|
-
const mockManifest = {
|
|
204
|
-
platforms: {
|
|
205
|
-
netsuite: {
|
|
206
|
-
name: 'netsuite',
|
|
207
|
-
auth: {
|
|
208
|
-
type: 'oauth',
|
|
209
|
-
oauth: {
|
|
210
|
-
authUrl: 'https://system.netsuite.com/app/login/oauth2/authorize.nl',
|
|
211
|
-
clientId: 'test-client-id'
|
|
212
|
-
}
|
|
213
|
-
},
|
|
214
|
-
environment: {
|
|
215
|
-
type: 'dynamic'
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
};
|
|
220
|
-
|
|
221
|
-
// Act
|
|
222
|
-
const result = await collectAuthInfo.execute({
|
|
223
|
-
connectorManifest: mockManifest,
|
|
224
|
-
connectorName: 'netsuite'
|
|
225
|
-
// hostname is missing
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
// Assert
|
|
229
|
-
expect(result.success).toBe(false);
|
|
230
|
-
expect(result.error).toBeDefined();
|
|
231
|
-
});
|
|
232
|
-
});
|
|
233
|
-
});
|
|
234
|
-
|
|
@@ -1,177 +0,0 @@
|
|
|
1
|
-
const setConnector = require('../../../mcp/tools/setConnector');
|
|
2
|
-
const developerPortal = require('../../../connector/developerPortal');
|
|
3
|
-
|
|
4
|
-
// Mock the developerPortal module
|
|
5
|
-
jest.mock('../../../connector/developerPortal');
|
|
6
|
-
|
|
7
|
-
describe('MCP Tool: setConnector', () => {
|
|
8
|
-
beforeEach(() => {
|
|
9
|
-
jest.clearAllMocks();
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
describe('tool definition', () => {
|
|
13
|
-
test('should have correct tool definition', () => {
|
|
14
|
-
expect(setConnector.definition).toBeDefined();
|
|
15
|
-
expect(setConnector.definition.name).toBe('setConnector');
|
|
16
|
-
expect(setConnector.definition.description).toContain('Auth flow step.2');
|
|
17
|
-
expect(setConnector.definition.inputSchema).toBeDefined();
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
test('should require connectorDisplayName parameter', () => {
|
|
21
|
-
expect(setConnector.definition.inputSchema.required).toContain('connectorDisplayName');
|
|
22
|
-
});
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
describe('execute', () => {
|
|
26
|
-
test('should set connector successfully', async () => {
|
|
27
|
-
// Arrange
|
|
28
|
-
const mockPublicConnectors = [
|
|
29
|
-
{ id: '1', name: 'salesforce', displayName: 'Salesforce', status: 'public' }
|
|
30
|
-
];
|
|
31
|
-
const mockPrivateConnectors = [];
|
|
32
|
-
const mockManifest = {
|
|
33
|
-
platforms: {
|
|
34
|
-
salesforce: {
|
|
35
|
-
name: 'salesforce',
|
|
36
|
-
auth: { type: 'oauth' },
|
|
37
|
-
environment: { type: 'fixed' }
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
developerPortal.getPublicConnectorList.mockResolvedValue({
|
|
43
|
-
connectors: mockPublicConnectors
|
|
44
|
-
});
|
|
45
|
-
developerPortal.getPrivateConnectorList.mockResolvedValue({
|
|
46
|
-
privateConnectors: mockPrivateConnectors
|
|
47
|
-
});
|
|
48
|
-
developerPortal.getConnectorManifest.mockResolvedValue(mockManifest);
|
|
49
|
-
|
|
50
|
-
// Act
|
|
51
|
-
const result = await setConnector.execute({
|
|
52
|
-
connectorDisplayName: 'Salesforce'
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
// Assert
|
|
56
|
-
expect(result).toEqual({
|
|
57
|
-
success: true,
|
|
58
|
-
data: {
|
|
59
|
-
connectorManifest: mockManifest,
|
|
60
|
-
connectorDisplayName: 'Salesforce',
|
|
61
|
-
connectorName: 'salesforce',
|
|
62
|
-
message: expect.stringContaining('IMPORTANT')
|
|
63
|
-
}
|
|
64
|
-
});
|
|
65
|
-
expect(developerPortal.getConnectorManifest).toHaveBeenCalledWith({
|
|
66
|
-
connectorId: '1',
|
|
67
|
-
isPrivate: false
|
|
68
|
-
});
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
test('should handle private connector', async () => {
|
|
72
|
-
// Arrange
|
|
73
|
-
const mockPublicConnectors = [];
|
|
74
|
-
const mockPrivateConnectors = [
|
|
75
|
-
{ id: '2', name: 'custom-crm', displayName: 'Custom CRM', status: 'private' }
|
|
76
|
-
];
|
|
77
|
-
const mockManifest = {
|
|
78
|
-
platforms: {
|
|
79
|
-
'custom-crm': {
|
|
80
|
-
name: 'custom-crm',
|
|
81
|
-
auth: { type: 'apiKey' }
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
developerPortal.getPublicConnectorList.mockResolvedValue({
|
|
87
|
-
connectors: mockPublicConnectors
|
|
88
|
-
});
|
|
89
|
-
developerPortal.getPrivateConnectorList.mockResolvedValue({
|
|
90
|
-
privateConnectors: mockPrivateConnectors
|
|
91
|
-
});
|
|
92
|
-
developerPortal.getConnectorManifest.mockResolvedValue(mockManifest);
|
|
93
|
-
|
|
94
|
-
// Act
|
|
95
|
-
const result = await setConnector.execute({
|
|
96
|
-
connectorDisplayName: 'Custom CRM'
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
// Assert
|
|
100
|
-
expect(result.success).toBe(true);
|
|
101
|
-
expect(result.data.connectorName).toBe('custom-crm');
|
|
102
|
-
expect(developerPortal.getConnectorManifest).toHaveBeenCalledWith({
|
|
103
|
-
connectorId: '2',
|
|
104
|
-
isPrivate: true
|
|
105
|
-
});
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
test('should return error when connector manifest not found', async () => {
|
|
109
|
-
// Arrange
|
|
110
|
-
const mockPublicConnectors = [
|
|
111
|
-
{ id: '1', name: 'salesforce', displayName: 'Salesforce', status: 'public' }
|
|
112
|
-
];
|
|
113
|
-
const mockPrivateConnectors = [];
|
|
114
|
-
|
|
115
|
-
developerPortal.getPublicConnectorList.mockResolvedValue({
|
|
116
|
-
connectors: mockPublicConnectors
|
|
117
|
-
});
|
|
118
|
-
developerPortal.getPrivateConnectorList.mockResolvedValue({
|
|
119
|
-
privateConnectors: mockPrivateConnectors
|
|
120
|
-
});
|
|
121
|
-
developerPortal.getConnectorManifest.mockResolvedValue(null);
|
|
122
|
-
|
|
123
|
-
// Act
|
|
124
|
-
const result = await setConnector.execute({
|
|
125
|
-
connectorDisplayName: 'Salesforce'
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
// Assert
|
|
129
|
-
expect(result.success).toBe(false);
|
|
130
|
-
expect(result.error).toContain('Connector manifest not found');
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
test('should return error when connector not found in list', async () => {
|
|
134
|
-
// Arrange
|
|
135
|
-
const mockPublicConnectors = [
|
|
136
|
-
{ id: '1', name: 'salesforce', displayName: 'Salesforce', status: 'public' }
|
|
137
|
-
];
|
|
138
|
-
const mockPrivateConnectors = [];
|
|
139
|
-
|
|
140
|
-
developerPortal.getPublicConnectorList.mockResolvedValue({
|
|
141
|
-
connectors: mockPublicConnectors
|
|
142
|
-
});
|
|
143
|
-
developerPortal.getPrivateConnectorList.mockResolvedValue({
|
|
144
|
-
privateConnectors: mockPrivateConnectors
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
// Act
|
|
148
|
-
const result = await setConnector.execute({
|
|
149
|
-
connectorDisplayName: 'NonExistentCRM'
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
// Assert
|
|
153
|
-
expect(result.success).toBe(false);
|
|
154
|
-
expect(result.error).toBeDefined();
|
|
155
|
-
expect(result.errorDetails).toBeDefined();
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
test('should handle API errors gracefully', async () => {
|
|
159
|
-
// Arrange
|
|
160
|
-
const errorMessage = 'API connection failed';
|
|
161
|
-
developerPortal.getPublicConnectorList.mockRejectedValue(
|
|
162
|
-
new Error(errorMessage)
|
|
163
|
-
);
|
|
164
|
-
|
|
165
|
-
// Act
|
|
166
|
-
const result = await setConnector.execute({
|
|
167
|
-
connectorDisplayName: 'Salesforce'
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
// Assert
|
|
171
|
-
expect(result.success).toBe(false);
|
|
172
|
-
expect(result.error).toBe(errorMessage);
|
|
173
|
-
expect(result.errorDetails).toBeDefined();
|
|
174
|
-
});
|
|
175
|
-
});
|
|
176
|
-
});
|
|
177
|
-
|