@optimizely-opal/opal-tool-ocp-sdk 1.0.0-beta.3 → 1.0.0-beta.5
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 +114 -0
- package/dist/auth/AuthUtils.d.ts +5 -5
- package/dist/auth/AuthUtils.d.ts.map +1 -1
- package/dist/auth/AuthUtils.js +53 -25
- package/dist/auth/AuthUtils.js.map +1 -1
- package/dist/auth/AuthUtils.test.js +62 -117
- package/dist/auth/AuthUtils.test.js.map +1 -1
- package/dist/function/GlobalToolFunction.d.ts +1 -1
- package/dist/function/GlobalToolFunction.d.ts.map +1 -1
- package/dist/function/GlobalToolFunction.js +17 -4
- package/dist/function/GlobalToolFunction.js.map +1 -1
- package/dist/function/GlobalToolFunction.test.js +54 -8
- package/dist/function/GlobalToolFunction.test.js.map +1 -1
- package/dist/function/ToolFunction.d.ts +1 -1
- package/dist/function/ToolFunction.d.ts.map +1 -1
- package/dist/function/ToolFunction.js +17 -4
- package/dist/function/ToolFunction.js.map +1 -1
- package/dist/function/ToolFunction.test.js +54 -8
- package/dist/function/ToolFunction.test.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/service/Service.d.ts +15 -2
- package/dist/service/Service.d.ts.map +1 -1
- package/dist/service/Service.js +43 -17
- package/dist/service/Service.js.map +1 -1
- package/dist/service/Service.test.js +84 -2
- package/dist/service/Service.test.js.map +1 -1
- package/dist/types/ToolError.d.ts +72 -0
- package/dist/types/ToolError.d.ts.map +1 -0
- package/dist/types/ToolError.js +107 -0
- package/dist/types/ToolError.js.map +1 -0
- package/dist/types/ToolError.test.d.ts +2 -0
- package/dist/types/ToolError.test.d.ts.map +1 -0
- package/dist/types/ToolError.test.js +185 -0
- package/dist/types/ToolError.test.js.map +1 -0
- package/dist/validation/ParameterValidator.d.ts +5 -16
- package/dist/validation/ParameterValidator.d.ts.map +1 -1
- package/dist/validation/ParameterValidator.js +10 -3
- package/dist/validation/ParameterValidator.js.map +1 -1
- package/dist/validation/ParameterValidator.test.js +187 -146
- package/dist/validation/ParameterValidator.test.js.map +1 -1
- package/package.json +1 -1
- package/src/auth/AuthUtils.test.ts +62 -157
- package/src/auth/AuthUtils.ts +66 -32
- package/src/function/GlobalToolFunction.test.ts +54 -8
- package/src/function/GlobalToolFunction.ts +26 -6
- package/src/function/ToolFunction.test.ts +54 -8
- package/src/function/ToolFunction.ts +26 -6
- package/src/index.ts +1 -0
- package/src/service/Service.test.ts +103 -2
- package/src/service/Service.ts +45 -17
- package/src/types/ToolError.test.ts +222 -0
- package/src/types/ToolError.ts +125 -0
- package/src/validation/ParameterValidator.test.ts +188 -158
- package/src/validation/ParameterValidator.ts +17 -20
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ParameterValidator } from './ParameterValidator';
|
|
2
2
|
import { Parameter, ParameterType } from '../types/Models';
|
|
3
|
+
import { ToolError } from '../types/ToolError';
|
|
3
4
|
|
|
4
5
|
describe('ParameterValidator', () => {
|
|
5
6
|
describe('validate', () => {
|
|
@@ -22,13 +23,12 @@ describe('ParameterValidator', () => {
|
|
|
22
23
|
config: { theme: 'dark', language: 'en' }
|
|
23
24
|
};
|
|
24
25
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
expect(result.errors).toHaveLength(0);
|
|
26
|
+
expect(() => {
|
|
27
|
+
ParameterValidator.validate(validParams, paramDefs, '/test-endpoint');
|
|
28
|
+
}).not.toThrow();
|
|
29
29
|
});
|
|
30
30
|
|
|
31
|
-
it('should
|
|
31
|
+
it('should throw ToolError for missing required parameters', () => {
|
|
32
32
|
const paramDefs = [
|
|
33
33
|
new Parameter('name', ParameterType.String, 'User name', true),
|
|
34
34
|
new Parameter('email', ParameterType.String, 'User email', true)
|
|
@@ -39,17 +39,30 @@ describe('ParameterValidator', () => {
|
|
|
39
39
|
// email is missing
|
|
40
40
|
};
|
|
41
41
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
42
|
+
expect(() => {
|
|
43
|
+
ParameterValidator.validate(params, paramDefs, '/test-endpoint');
|
|
44
|
+
}).toThrow(ToolError);
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
ParameterValidator.validate(params, paramDefs, '/test-endpoint');
|
|
48
|
+
} catch (error) {
|
|
49
|
+
expect(error).toBeInstanceOf(ToolError);
|
|
50
|
+
const toolError = error as ToolError;
|
|
51
|
+
expect(toolError.status).toBe(400);
|
|
52
|
+
expect(toolError.message).toBe(
|
|
53
|
+
"[400] One or more validation errors occurred.: email (Required parameter 'email' is missing)"
|
|
54
|
+
);
|
|
55
|
+
expect(toolError.title).toBe('One or more validation errors occurred.');
|
|
56
|
+
expect(toolError.detail).toBe("See 'errors' field for details.");
|
|
57
|
+
expect(toolError.errors).toHaveLength(1);
|
|
58
|
+
expect(toolError.errors).toEqual([{
|
|
59
|
+
field: 'email',
|
|
60
|
+
message: "Required parameter 'email' is missing"
|
|
61
|
+
}]);
|
|
62
|
+
}
|
|
50
63
|
});
|
|
51
64
|
|
|
52
|
-
it('should
|
|
65
|
+
it('should throw ToolError for wrong parameter types', () => {
|
|
53
66
|
const paramDefs = [
|
|
54
67
|
new Parameter('name', ParameterType.String, 'User name', true),
|
|
55
68
|
new Parameter('age', ParameterType.Integer, 'User age', true),
|
|
@@ -66,42 +79,64 @@ describe('ParameterValidator', () => {
|
|
|
66
79
|
config: 'invalid' // should be object
|
|
67
80
|
};
|
|
68
81
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
82
|
+
try {
|
|
83
|
+
ParameterValidator.validate(invalidParams, paramDefs, '/test-endpoint');
|
|
84
|
+
fail('Should have thrown ToolError');
|
|
85
|
+
} catch (error) {
|
|
86
|
+
expect(error).toBeInstanceOf(ToolError);
|
|
87
|
+
const toolError = error as ToolError;
|
|
88
|
+
expect(toolError.status).toBe(400);
|
|
89
|
+
expect(toolError.errors).toHaveLength(5);
|
|
73
90
|
|
|
74
|
-
|
|
75
|
-
|
|
91
|
+
expect(toolError.errors![0].field).toBe('name');
|
|
92
|
+
expect(toolError.errors![0].message).toContain('must be a string');
|
|
76
93
|
|
|
77
|
-
|
|
78
|
-
|
|
94
|
+
expect(toolError.errors![1].field).toBe('age');
|
|
95
|
+
expect(toolError.errors![1].message).toContain('must be an integer');
|
|
79
96
|
|
|
80
|
-
|
|
81
|
-
|
|
97
|
+
expect(toolError.errors![2].field).toBe('active');
|
|
98
|
+
expect(toolError.errors![2].message).toContain('must be a boolean');
|
|
82
99
|
|
|
83
|
-
|
|
84
|
-
|
|
100
|
+
expect(toolError.errors![3].field).toBe('tags');
|
|
101
|
+
expect(toolError.errors![3].message).toContain('must be an array');
|
|
85
102
|
|
|
86
|
-
|
|
87
|
-
|
|
103
|
+
expect(toolError.errors![4].field).toBe('config');
|
|
104
|
+
expect(toolError.errors![4].message).toContain('must be an object');
|
|
105
|
+
}
|
|
88
106
|
});
|
|
89
107
|
|
|
90
|
-
it('should
|
|
108
|
+
it('should throw ToolError when params is null with required parameters', () => {
|
|
91
109
|
const paramDefs = [
|
|
92
110
|
new Parameter('required', ParameterType.String, 'Required param', true),
|
|
93
111
|
new Parameter('optional', ParameterType.String, 'Optional param', false)
|
|
94
112
|
];
|
|
95
113
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
114
|
+
try {
|
|
115
|
+
ParameterValidator.validate(null, paramDefs, '/test-endpoint');
|
|
116
|
+
fail('Should have thrown ToolError');
|
|
117
|
+
} catch (error) {
|
|
118
|
+
expect(error).toBeInstanceOf(ToolError);
|
|
119
|
+
const toolError = error as ToolError;
|
|
120
|
+
expect(toolError.errors).toHaveLength(1);
|
|
121
|
+
expect(toolError.errors![0].field).toBe('required');
|
|
122
|
+
}
|
|
123
|
+
});
|
|
100
124
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
125
|
+
it('should throw ToolError when params is undefined with required parameters', () => {
|
|
126
|
+
const paramDefs = [
|
|
127
|
+
new Parameter('required', ParameterType.String, 'Required param', true),
|
|
128
|
+
new Parameter('optional', ParameterType.String, 'Optional param', false)
|
|
129
|
+
];
|
|
130
|
+
|
|
131
|
+
try {
|
|
132
|
+
ParameterValidator.validate(undefined, paramDefs, '/test-endpoint');
|
|
133
|
+
fail('Should have thrown ToolError');
|
|
134
|
+
} catch (error) {
|
|
135
|
+
expect(error).toBeInstanceOf(ToolError);
|
|
136
|
+
const toolError = error as ToolError;
|
|
137
|
+
expect(toolError.errors).toHaveLength(1);
|
|
138
|
+
expect(toolError.errors![0].field).toBe('required');
|
|
139
|
+
}
|
|
105
140
|
});
|
|
106
141
|
|
|
107
142
|
it('should allow optional parameters to be missing', () => {
|
|
@@ -115,10 +150,9 @@ describe('ParameterValidator', () => {
|
|
|
115
150
|
// age is optional and missing
|
|
116
151
|
};
|
|
117
152
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
expect(result.errors).toHaveLength(0);
|
|
153
|
+
expect(() => {
|
|
154
|
+
ParameterValidator.validate(params, paramDefs, '/test-endpoint');
|
|
155
|
+
}).not.toThrow();
|
|
122
156
|
});
|
|
123
157
|
|
|
124
158
|
it('should distinguish between integer and number types', () => {
|
|
@@ -132,210 +166,206 @@ describe('ParameterValidator', () => {
|
|
|
132
166
|
score: 85.5 // number is fine
|
|
133
167
|
};
|
|
134
168
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
169
|
+
try {
|
|
170
|
+
ParameterValidator.validate(params1, paramDefs, '/test-endpoint');
|
|
171
|
+
fail('Should have thrown ToolError');
|
|
172
|
+
} catch (error) {
|
|
173
|
+
expect(error).toBeInstanceOf(ToolError);
|
|
174
|
+
const toolError = error as ToolError;
|
|
175
|
+
expect(toolError.errors).toHaveLength(1);
|
|
176
|
+
expect(toolError.errors![0].field).toBe('count');
|
|
177
|
+
expect(toolError.errors![0].message).toContain('must be an integer');
|
|
178
|
+
}
|
|
140
179
|
|
|
141
180
|
const params2 = {
|
|
142
181
|
count: 25, // integer is fine
|
|
143
182
|
score: 85.5 // number is fine
|
|
144
183
|
};
|
|
145
184
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
185
|
+
expect(() => {
|
|
186
|
+
ParameterValidator.validate(params2, paramDefs, '/test-endpoint');
|
|
187
|
+
}).not.toThrow();
|
|
149
188
|
});
|
|
150
189
|
|
|
151
190
|
it('should handle array vs object distinction', () => {
|
|
152
191
|
const paramDefs = [
|
|
153
|
-
new Parameter('tags', ParameterType.List, '
|
|
154
|
-
new Parameter('config', ParameterType.Dictionary, 'Config
|
|
192
|
+
new Parameter('tags', ParameterType.List, 'Tags', true),
|
|
193
|
+
new Parameter('config', ParameterType.Dictionary, 'Config', true)
|
|
155
194
|
];
|
|
156
195
|
|
|
157
196
|
const params = {
|
|
158
|
-
tags: {
|
|
159
|
-
config: ['
|
|
197
|
+
tags: { key: 'value' }, // should be array, not object
|
|
198
|
+
config: ['item1', 'item2'] // should be object, not array
|
|
160
199
|
};
|
|
161
200
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
expect(result.errors[1].message).toContain('must be an object');
|
|
201
|
+
try {
|
|
202
|
+
ParameterValidator.validate(params, paramDefs, '/test-endpoint');
|
|
203
|
+
fail('Should have thrown ToolError');
|
|
204
|
+
} catch (error) {
|
|
205
|
+
expect(error).toBeInstanceOf(ToolError);
|
|
206
|
+
const toolError = error as ToolError;
|
|
207
|
+
expect(toolError.errors).toHaveLength(2);
|
|
208
|
+
}
|
|
171
209
|
});
|
|
172
210
|
|
|
173
211
|
it('should handle null values for optional parameters', () => {
|
|
174
212
|
const paramDefs = [
|
|
175
213
|
new Parameter('name', ParameterType.String, 'User name', true),
|
|
176
|
-
new Parameter('
|
|
214
|
+
new Parameter('nickname', ParameterType.String, 'Nickname', false)
|
|
177
215
|
];
|
|
178
216
|
|
|
179
217
|
const params = {
|
|
180
218
|
name: 'John Doe',
|
|
181
|
-
|
|
219
|
+
nickname: null // optional param can be null
|
|
182
220
|
};
|
|
183
221
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
222
|
+
expect(() => {
|
|
223
|
+
ParameterValidator.validate(params, paramDefs, '/test-endpoint');
|
|
224
|
+
}).not.toThrow();
|
|
187
225
|
});
|
|
188
226
|
|
|
189
227
|
it('should handle edge cases for number validation', () => {
|
|
190
228
|
const paramDefs = [
|
|
191
|
-
new Parameter('
|
|
229
|
+
new Parameter('value', ParameterType.Number, 'Number value', true)
|
|
192
230
|
];
|
|
193
231
|
|
|
194
|
-
//
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
expect(
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
232
|
+
// NaN should fail
|
|
233
|
+
try {
|
|
234
|
+
ParameterValidator.validate({ value: NaN }, paramDefs, '/test-endpoint');
|
|
235
|
+
fail('Should have thrown ToolError for NaN');
|
|
236
|
+
} catch (error) {
|
|
237
|
+
expect(error).toBeInstanceOf(ToolError);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Infinity should pass (it's a number)
|
|
241
|
+
expect(() => {
|
|
242
|
+
ParameterValidator.validate({ value: Infinity }, paramDefs, '/test-endpoint');
|
|
243
|
+
}).not.toThrow();
|
|
244
|
+
|
|
245
|
+
// Negative numbers should pass
|
|
246
|
+
expect(() => {
|
|
247
|
+
ParameterValidator.validate({ value: -42.5 }, paramDefs, '/test-endpoint');
|
|
248
|
+
}).not.toThrow();
|
|
211
249
|
});
|
|
212
250
|
|
|
213
251
|
it('should handle edge cases for integer validation', () => {
|
|
214
252
|
const paramDefs = [
|
|
215
|
-
new Parameter('count', ParameterType.Integer, '
|
|
253
|
+
new Parameter('count', ParameterType.Integer, 'Count', true)
|
|
216
254
|
];
|
|
217
255
|
|
|
218
|
-
//
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
expect(result2.isValid).toBe(true);
|
|
228
|
-
expect(result2.errors).toHaveLength(0);
|
|
229
|
-
|
|
230
|
-
// Test very large integers
|
|
231
|
-
const params3 = { count: Number.MAX_SAFE_INTEGER };
|
|
232
|
-
const result3 = ParameterValidator.validate(params3, paramDefs);
|
|
233
|
-
expect(result3.isValid).toBe(true);
|
|
234
|
-
expect(result3.errors).toHaveLength(0);
|
|
256
|
+
// Zero should pass
|
|
257
|
+
expect(() => {
|
|
258
|
+
ParameterValidator.validate({ count: 0 }, paramDefs, '/test-endpoint');
|
|
259
|
+
}).not.toThrow();
|
|
260
|
+
|
|
261
|
+
// Negative integer should pass
|
|
262
|
+
expect(() => {
|
|
263
|
+
ParameterValidator.validate({ count: -10 }, paramDefs, '/test-endpoint');
|
|
264
|
+
}).not.toThrow();
|
|
235
265
|
});
|
|
236
266
|
|
|
237
267
|
it('should handle empty arrays and objects', () => {
|
|
238
268
|
const paramDefs = [
|
|
239
|
-
new Parameter('tags', ParameterType.List, '
|
|
240
|
-
new Parameter('config', ParameterType.Dictionary, 'Config
|
|
269
|
+
new Parameter('tags', ParameterType.List, 'Tags', true),
|
|
270
|
+
new Parameter('config', ParameterType.Dictionary, 'Config', true)
|
|
241
271
|
];
|
|
242
272
|
|
|
243
273
|
const params = {
|
|
244
|
-
tags: [],
|
|
245
|
-
config: {}
|
|
274
|
+
tags: [],
|
|
275
|
+
config: {}
|
|
246
276
|
};
|
|
247
277
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
278
|
+
expect(() => {
|
|
279
|
+
ParameterValidator.validate(params, paramDefs, '/test-endpoint');
|
|
280
|
+
}).not.toThrow();
|
|
251
281
|
});
|
|
252
282
|
|
|
253
283
|
it('should handle special string values', () => {
|
|
254
284
|
const paramDefs = [
|
|
255
|
-
new Parameter('
|
|
285
|
+
new Parameter('value', ParameterType.String, 'String value', true)
|
|
256
286
|
];
|
|
257
287
|
|
|
258
|
-
//
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
expect(result2.isValid).toBe(true);
|
|
268
|
-
expect(result2.errors).toHaveLength(0);
|
|
288
|
+
// Empty string should pass
|
|
289
|
+
expect(() => {
|
|
290
|
+
ParameterValidator.validate({ value: '' }, paramDefs, '/test-endpoint');
|
|
291
|
+
}).not.toThrow();
|
|
292
|
+
|
|
293
|
+
// String with only whitespace should pass
|
|
294
|
+
expect(() => {
|
|
295
|
+
ParameterValidator.validate({ value: ' ' }, paramDefs, '/test-endpoint');
|
|
296
|
+
}).not.toThrow();
|
|
269
297
|
});
|
|
270
298
|
|
|
271
|
-
it('should
|
|
299
|
+
it('should collect multiple validation errors', () => {
|
|
272
300
|
const paramDefs = [
|
|
273
|
-
new Parameter('name', ParameterType.String, '
|
|
274
|
-
new Parameter('age', ParameterType.Integer, '
|
|
275
|
-
new Parameter('email', ParameterType.String, '
|
|
301
|
+
new Parameter('name', ParameterType.String, 'Name', true),
|
|
302
|
+
new Parameter('age', ParameterType.Integer, 'Age', true),
|
|
303
|
+
new Parameter('email', ParameterType.String, 'Email', true)
|
|
276
304
|
];
|
|
277
305
|
|
|
278
306
|
const params = {
|
|
279
|
-
name
|
|
280
|
-
age: '
|
|
307
|
+
// name is missing
|
|
308
|
+
age: 'invalid' // wrong type
|
|
281
309
|
// email is missing
|
|
282
310
|
};
|
|
283
311
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
312
|
+
try {
|
|
313
|
+
ParameterValidator.validate(params, paramDefs, '/test-endpoint');
|
|
314
|
+
fail('Should have thrown ToolError');
|
|
315
|
+
} catch (error) {
|
|
316
|
+
expect(error).toBeInstanceOf(ToolError);
|
|
317
|
+
const toolError = error as ToolError;
|
|
318
|
+
expect(toolError.errors).toHaveLength(3);
|
|
319
|
+
|
|
320
|
+
expect(toolError.errors!.some((e) => e.field === 'name')).toBe(true);
|
|
321
|
+
expect(toolError.errors!.some((e) => e.field === 'age')).toBe(true);
|
|
322
|
+
expect(toolError.errors!.some((e) => e.field === 'email')).toBe(true);
|
|
323
|
+
}
|
|
291
324
|
});
|
|
292
325
|
|
|
293
326
|
it('should handle tools with no parameter definitions', () => {
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
327
|
+
expect(() => {
|
|
328
|
+
ParameterValidator.validate({ anyParam: 'value' }, [], '/test-endpoint');
|
|
329
|
+
}).not.toThrow();
|
|
297
330
|
});
|
|
298
331
|
|
|
299
332
|
it('should handle extra parameters not in definition', () => {
|
|
300
333
|
const paramDefs = [
|
|
301
|
-
new Parameter('name', ParameterType.String, '
|
|
334
|
+
new Parameter('name', ParameterType.String, 'Name', true)
|
|
302
335
|
];
|
|
303
336
|
|
|
304
337
|
const params = {
|
|
305
|
-
name: 'John
|
|
306
|
-
extraParam: '
|
|
338
|
+
name: 'John',
|
|
339
|
+
extraParam: 'ignored', // not in definition
|
|
340
|
+
anotherExtra: 123
|
|
307
341
|
};
|
|
308
342
|
|
|
309
|
-
|
|
310
|
-
expect(
|
|
311
|
-
|
|
343
|
+
// Extra parameters should be ignored
|
|
344
|
+
expect(() => {
|
|
345
|
+
ParameterValidator.validate(params, paramDefs, '/test-endpoint');
|
|
346
|
+
}).not.toThrow();
|
|
312
347
|
});
|
|
313
348
|
|
|
314
349
|
it('should handle nested objects and arrays', () => {
|
|
315
350
|
const paramDefs = [
|
|
316
|
-
new Parameter('config', ParameterType.Dictionary, 'Config
|
|
317
|
-
new Parameter('
|
|
351
|
+
new Parameter('config', ParameterType.Dictionary, 'Config', true),
|
|
352
|
+
new Parameter('items', ParameterType.List, 'Items', true)
|
|
318
353
|
];
|
|
319
354
|
|
|
320
355
|
const params = {
|
|
321
356
|
config: {
|
|
322
357
|
nested: {
|
|
323
|
-
|
|
324
|
-
|
|
358
|
+
deeply: {
|
|
359
|
+
nested: 'value'
|
|
325
360
|
}
|
|
326
|
-
}
|
|
327
|
-
array: [1, 2, 3]
|
|
361
|
+
}
|
|
328
362
|
},
|
|
329
|
-
|
|
330
|
-
[1, 2, 3],
|
|
331
|
-
[4, 5, 6],
|
|
332
|
-
{ nested: 'object in array' }
|
|
333
|
-
]
|
|
363
|
+
items: [1, 2, [3, 4, [5, 6]]]
|
|
334
364
|
};
|
|
335
365
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
366
|
+
expect(() => {
|
|
367
|
+
ParameterValidator.validate(params, paramDefs, '/test-endpoint');
|
|
368
|
+
}).not.toThrow();
|
|
339
369
|
});
|
|
340
370
|
});
|
|
341
371
|
});
|
|
@@ -1,20 +1,5 @@
|
|
|
1
1
|
import { Parameter, ParameterType } from '../types/Models';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Validation error details
|
|
5
|
-
*/
|
|
6
|
-
export interface ValidationError {
|
|
7
|
-
field: string;
|
|
8
|
-
message: string;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Validation result
|
|
13
|
-
*/
|
|
14
|
-
export interface ValidationResult {
|
|
15
|
-
isValid: boolean;
|
|
16
|
-
errors: ValidationError[];
|
|
17
|
-
}
|
|
2
|
+
import { ToolError, ValidationError } from '../types/ToolError';
|
|
18
3
|
|
|
19
4
|
/**
|
|
20
5
|
* Parameter validator for tool inputs
|
|
@@ -22,14 +7,18 @@ export interface ValidationResult {
|
|
|
22
7
|
export class ParameterValidator {
|
|
23
8
|
/**
|
|
24
9
|
* Validate parameters against their definitions
|
|
10
|
+
* Throws ToolError with status 400 if validation fails
|
|
11
|
+
*
|
|
25
12
|
* @param params The actual parameters received
|
|
26
13
|
* @param parameterDefinitions The expected parameter definitions
|
|
27
|
-
* @
|
|
14
|
+
* @param _endpoint The endpoint being validated (reserved for future use)
|
|
15
|
+
* @throws {ToolError} When validation fails
|
|
28
16
|
*/
|
|
29
17
|
public static validate(
|
|
30
18
|
params: any,
|
|
31
|
-
parameterDefinitions: Parameter[]
|
|
32
|
-
|
|
19
|
+
parameterDefinitions: Parameter[],
|
|
20
|
+
_endpoint: string
|
|
21
|
+
): void {
|
|
33
22
|
const errors: ValidationError[] = [];
|
|
34
23
|
|
|
35
24
|
// Validate each defined parameter
|
|
@@ -57,7 +46,15 @@ export class ParameterValidator {
|
|
|
57
46
|
}
|
|
58
47
|
}
|
|
59
48
|
|
|
60
|
-
|
|
49
|
+
// Throw ToolError if validation failed
|
|
50
|
+
if (errors.length > 0) {
|
|
51
|
+
throw new ToolError(
|
|
52
|
+
'One or more validation errors occurred.',
|
|
53
|
+
400,
|
|
54
|
+
"See 'errors' field for details.",
|
|
55
|
+
errors
|
|
56
|
+
);
|
|
57
|
+
}
|
|
61
58
|
}
|
|
62
59
|
|
|
63
60
|
/**
|