@optimizely-opal/opal-tool-ocp-sdk 1.0.0-beta.1 → 1.0.0-beta.2
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 +42 -0
- package/dist/service/Service.d.ts.map +1 -1
- package/dist/service/Service.js +17 -0
- package/dist/service/Service.js.map +1 -1
- package/dist/service/Service.test.js +114 -6
- package/dist/service/Service.test.js.map +1 -1
- package/dist/validation/ParameterValidator.d.ts +42 -0
- package/dist/validation/ParameterValidator.d.ts.map +1 -0
- package/dist/validation/ParameterValidator.js +122 -0
- package/dist/validation/ParameterValidator.js.map +1 -0
- package/dist/validation/ParameterValidator.test.d.ts +2 -0
- package/dist/validation/ParameterValidator.test.d.ts.map +1 -0
- package/dist/validation/ParameterValidator.test.js +282 -0
- package/dist/validation/ParameterValidator.test.js.map +1 -0
- package/package.json +1 -1
- package/src/service/Service.test.ts +155 -14
- package/src/service/Service.ts +19 -1
- package/src/validation/ParameterValidator.test.ts +341 -0
- package/src/validation/ParameterValidator.ts +153 -0
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
import { ParameterValidator } from './ParameterValidator';
|
|
2
|
+
import { Parameter, ParameterType } from '../types/Models';
|
|
3
|
+
|
|
4
|
+
describe('ParameterValidator', () => {
|
|
5
|
+
describe('validate', () => {
|
|
6
|
+
it('should pass validation for valid parameters', () => {
|
|
7
|
+
const paramDefs = [
|
|
8
|
+
new Parameter('name', ParameterType.String, 'User name', true),
|
|
9
|
+
new Parameter('age', ParameterType.Integer, 'User age', false),
|
|
10
|
+
new Parameter('score', ParameterType.Number, 'User score', false),
|
|
11
|
+
new Parameter('active', ParameterType.Boolean, 'Is active', false),
|
|
12
|
+
new Parameter('tags', ParameterType.List, 'User tags', false),
|
|
13
|
+
new Parameter('config', ParameterType.Dictionary, 'User config', false)
|
|
14
|
+
];
|
|
15
|
+
|
|
16
|
+
const validParams = {
|
|
17
|
+
name: 'John Doe',
|
|
18
|
+
age: 25,
|
|
19
|
+
score: 85.5,
|
|
20
|
+
active: true,
|
|
21
|
+
tags: ['admin', 'user'],
|
|
22
|
+
config: { theme: 'dark', language: 'en' }
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const result = ParameterValidator.validate(validParams, paramDefs);
|
|
26
|
+
|
|
27
|
+
expect(result.isValid).toBe(true);
|
|
28
|
+
expect(result.errors).toHaveLength(0);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('should fail validation for missing required parameters', () => {
|
|
32
|
+
const paramDefs = [
|
|
33
|
+
new Parameter('name', ParameterType.String, 'User name', true),
|
|
34
|
+
new Parameter('email', ParameterType.String, 'User email', true)
|
|
35
|
+
];
|
|
36
|
+
|
|
37
|
+
const params = {
|
|
38
|
+
name: 'John Doe'
|
|
39
|
+
// email is missing
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const result = ParameterValidator.validate(params, paramDefs);
|
|
43
|
+
|
|
44
|
+
expect(result.isValid).toBe(false);
|
|
45
|
+
expect(result.errors).toHaveLength(1);
|
|
46
|
+
expect(result.errors[0]).toEqual({
|
|
47
|
+
field: 'email',
|
|
48
|
+
message: "Required parameter 'email' is missing"
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('should fail validation for wrong parameter types', () => {
|
|
53
|
+
const paramDefs = [
|
|
54
|
+
new Parameter('name', ParameterType.String, 'User name', true),
|
|
55
|
+
new Parameter('age', ParameterType.Integer, 'User age', true),
|
|
56
|
+
new Parameter('active', ParameterType.Boolean, 'Is active', true),
|
|
57
|
+
new Parameter('tags', ParameterType.List, 'User tags', true),
|
|
58
|
+
new Parameter('config', ParameterType.Dictionary, 'User config', true)
|
|
59
|
+
];
|
|
60
|
+
|
|
61
|
+
const invalidParams = {
|
|
62
|
+
name: 123, // should be string
|
|
63
|
+
age: '25', // should be integer
|
|
64
|
+
active: 'true', // should be boolean
|
|
65
|
+
tags: 'tag1,tag2', // should be array
|
|
66
|
+
config: 'invalid' // should be object
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const result = ParameterValidator.validate(invalidParams, paramDefs);
|
|
70
|
+
|
|
71
|
+
expect(result.isValid).toBe(false);
|
|
72
|
+
expect(result.errors).toHaveLength(5);
|
|
73
|
+
|
|
74
|
+
expect(result.errors[0].field).toBe('name');
|
|
75
|
+
expect(result.errors[0].message).toContain('must be a string');
|
|
76
|
+
|
|
77
|
+
expect(result.errors[1].field).toBe('age');
|
|
78
|
+
expect(result.errors[1].message).toContain('must be an integer');
|
|
79
|
+
|
|
80
|
+
expect(result.errors[2].field).toBe('active');
|
|
81
|
+
expect(result.errors[2].message).toContain('must be a boolean');
|
|
82
|
+
|
|
83
|
+
expect(result.errors[3].field).toBe('tags');
|
|
84
|
+
expect(result.errors[3].message).toContain('must be an array');
|
|
85
|
+
|
|
86
|
+
expect(result.errors[4].field).toBe('config');
|
|
87
|
+
expect(result.errors[4].message).toContain('must be an object');
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('should handle null/undefined parameters correctly', () => {
|
|
91
|
+
const paramDefs = [
|
|
92
|
+
new Parameter('required', ParameterType.String, 'Required param', true),
|
|
93
|
+
new Parameter('optional', ParameterType.String, 'Optional param', false)
|
|
94
|
+
];
|
|
95
|
+
|
|
96
|
+
const result1 = ParameterValidator.validate(null, paramDefs);
|
|
97
|
+
expect(result1.isValid).toBe(false);
|
|
98
|
+
expect(result1.errors).toHaveLength(1);
|
|
99
|
+
expect(result1.errors[0].field).toBe('required');
|
|
100
|
+
|
|
101
|
+
const result2 = ParameterValidator.validate(undefined, paramDefs);
|
|
102
|
+
expect(result2.isValid).toBe(false);
|
|
103
|
+
expect(result2.errors).toHaveLength(1);
|
|
104
|
+
expect(result2.errors[0].field).toBe('required');
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('should allow optional parameters to be missing', () => {
|
|
108
|
+
const paramDefs = [
|
|
109
|
+
new Parameter('name', ParameterType.String, 'User name', true),
|
|
110
|
+
new Parameter('age', ParameterType.Integer, 'User age', false)
|
|
111
|
+
];
|
|
112
|
+
|
|
113
|
+
const params = {
|
|
114
|
+
name: 'John Doe'
|
|
115
|
+
// age is optional and missing
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
const result = ParameterValidator.validate(params, paramDefs);
|
|
119
|
+
|
|
120
|
+
expect(result.isValid).toBe(true);
|
|
121
|
+
expect(result.errors).toHaveLength(0);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it('should distinguish between integer and number types', () => {
|
|
125
|
+
const paramDefs = [
|
|
126
|
+
new Parameter('count', ParameterType.Integer, 'Item count', true),
|
|
127
|
+
new Parameter('score', ParameterType.Number, 'Score value', true)
|
|
128
|
+
];
|
|
129
|
+
|
|
130
|
+
const params1 = {
|
|
131
|
+
count: 25.5, // should be integer, not float
|
|
132
|
+
score: 85.5 // number is fine
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
const result1 = ParameterValidator.validate(params1, paramDefs);
|
|
136
|
+
expect(result1.isValid).toBe(false);
|
|
137
|
+
expect(result1.errors).toHaveLength(1);
|
|
138
|
+
expect(result1.errors[0].field).toBe('count');
|
|
139
|
+
expect(result1.errors[0].message).toContain('must be an integer');
|
|
140
|
+
|
|
141
|
+
const params2 = {
|
|
142
|
+
count: 25, // integer is fine
|
|
143
|
+
score: 85.5 // number is fine
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
const result2 = ParameterValidator.validate(params2, paramDefs);
|
|
147
|
+
expect(result2.isValid).toBe(true);
|
|
148
|
+
expect(result2.errors).toHaveLength(0);
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it('should handle array vs object distinction', () => {
|
|
152
|
+
const paramDefs = [
|
|
153
|
+
new Parameter('tags', ParameterType.List, 'Tag list', true),
|
|
154
|
+
new Parameter('config', ParameterType.Dictionary, 'Config object', true)
|
|
155
|
+
];
|
|
156
|
+
|
|
157
|
+
const params = {
|
|
158
|
+
tags: { 0: 'tag1', 1: 'tag2' }, // object that looks like array
|
|
159
|
+
config: ['key1', 'key2'] // array instead of object
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
const result = ParameterValidator.validate(params, paramDefs);
|
|
163
|
+
expect(result.isValid).toBe(false);
|
|
164
|
+
expect(result.errors).toHaveLength(2);
|
|
165
|
+
|
|
166
|
+
expect(result.errors[0].field).toBe('tags');
|
|
167
|
+
expect(result.errors[0].message).toContain('must be an array');
|
|
168
|
+
|
|
169
|
+
expect(result.errors[1].field).toBe('config');
|
|
170
|
+
expect(result.errors[1].message).toContain('must be an object');
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
it('should handle null values for optional parameters', () => {
|
|
174
|
+
const paramDefs = [
|
|
175
|
+
new Parameter('name', ParameterType.String, 'User name', true),
|
|
176
|
+
new Parameter('age', ParameterType.Integer, 'User age', false)
|
|
177
|
+
];
|
|
178
|
+
|
|
179
|
+
const params = {
|
|
180
|
+
name: 'John Doe',
|
|
181
|
+
age: null // null for optional parameter should be allowed
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
const result = ParameterValidator.validate(params, paramDefs);
|
|
185
|
+
expect(result.isValid).toBe(true);
|
|
186
|
+
expect(result.errors).toHaveLength(0);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it('should handle edge cases for number validation', () => {
|
|
190
|
+
const paramDefs = [
|
|
191
|
+
new Parameter('score', ParameterType.Number, 'Score value', true)
|
|
192
|
+
];
|
|
193
|
+
|
|
194
|
+
// Test NaN
|
|
195
|
+
const params1 = { score: NaN };
|
|
196
|
+
const result1 = ParameterValidator.validate(params1, paramDefs);
|
|
197
|
+
expect(result1.isValid).toBe(false);
|
|
198
|
+
expect(result1.errors[0].message).toContain('must be a number');
|
|
199
|
+
|
|
200
|
+
// Test Infinity
|
|
201
|
+
const params2 = { score: Infinity };
|
|
202
|
+
const result2 = ParameterValidator.validate(params2, paramDefs);
|
|
203
|
+
expect(result2.isValid).toBe(true);
|
|
204
|
+
expect(result2.errors).toHaveLength(0);
|
|
205
|
+
|
|
206
|
+
// Test negative numbers
|
|
207
|
+
const params3 = { score: -42.5 };
|
|
208
|
+
const result3 = ParameterValidator.validate(params3, paramDefs);
|
|
209
|
+
expect(result3.isValid).toBe(true);
|
|
210
|
+
expect(result3.errors).toHaveLength(0);
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
it('should handle edge cases for integer validation', () => {
|
|
214
|
+
const paramDefs = [
|
|
215
|
+
new Parameter('count', ParameterType.Integer, 'Item count', true)
|
|
216
|
+
];
|
|
217
|
+
|
|
218
|
+
// Test negative integers
|
|
219
|
+
const params1 = { count: -5 };
|
|
220
|
+
const result1 = ParameterValidator.validate(params1, paramDefs);
|
|
221
|
+
expect(result1.isValid).toBe(true);
|
|
222
|
+
expect(result1.errors).toHaveLength(0);
|
|
223
|
+
|
|
224
|
+
// Test zero
|
|
225
|
+
const params2 = { count: 0 };
|
|
226
|
+
const result2 = ParameterValidator.validate(params2, paramDefs);
|
|
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);
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
it('should handle empty arrays and objects', () => {
|
|
238
|
+
const paramDefs = [
|
|
239
|
+
new Parameter('tags', ParameterType.List, 'Tag list', true),
|
|
240
|
+
new Parameter('config', ParameterType.Dictionary, 'Config object', true)
|
|
241
|
+
];
|
|
242
|
+
|
|
243
|
+
const params = {
|
|
244
|
+
tags: [], // empty array should be valid
|
|
245
|
+
config: {} // empty object should be valid
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
const result = ParameterValidator.validate(params, paramDefs);
|
|
249
|
+
expect(result.isValid).toBe(true);
|
|
250
|
+
expect(result.errors).toHaveLength(0);
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
it('should handle special string values', () => {
|
|
254
|
+
const paramDefs = [
|
|
255
|
+
new Parameter('text', ParameterType.String, 'Text value', true)
|
|
256
|
+
];
|
|
257
|
+
|
|
258
|
+
// Test empty string
|
|
259
|
+
const params1 = { text: '' };
|
|
260
|
+
const result1 = ParameterValidator.validate(params1, paramDefs);
|
|
261
|
+
expect(result1.isValid).toBe(true);
|
|
262
|
+
expect(result1.errors).toHaveLength(0);
|
|
263
|
+
|
|
264
|
+
// Test string with special characters
|
|
265
|
+
const params2 = { text: 'Hello\nWorld\t!' };
|
|
266
|
+
const result2 = ParameterValidator.validate(params2, paramDefs);
|
|
267
|
+
expect(result2.isValid).toBe(true);
|
|
268
|
+
expect(result2.errors).toHaveLength(0);
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
it('should handle multiple validation errors', () => {
|
|
272
|
+
const paramDefs = [
|
|
273
|
+
new Parameter('name', ParameterType.String, 'User name', true),
|
|
274
|
+
new Parameter('age', ParameterType.Integer, 'User age', true),
|
|
275
|
+
new Parameter('email', ParameterType.String, 'User email', true)
|
|
276
|
+
];
|
|
277
|
+
|
|
278
|
+
const params = {
|
|
279
|
+
name: 123, // wrong type
|
|
280
|
+
age: '25' // wrong type
|
|
281
|
+
// email is missing
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
const result = ParameterValidator.validate(params, paramDefs);
|
|
285
|
+
expect(result.isValid).toBe(false);
|
|
286
|
+
expect(result.errors).toHaveLength(3);
|
|
287
|
+
|
|
288
|
+
expect(result.errors.some((e) => e.field === 'name')).toBe(true);
|
|
289
|
+
expect(result.errors.some((e) => e.field === 'age')).toBe(true);
|
|
290
|
+
expect(result.errors.some((e) => e.field === 'email')).toBe(true);
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
it('should handle tools with no parameter definitions', () => {
|
|
294
|
+
const result = ParameterValidator.validate({ someParam: 'value' }, []);
|
|
295
|
+
expect(result.isValid).toBe(true);
|
|
296
|
+
expect(result.errors).toHaveLength(0);
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
it('should handle extra parameters not in definition', () => {
|
|
300
|
+
const paramDefs = [
|
|
301
|
+
new Parameter('name', ParameterType.String, 'User name', true)
|
|
302
|
+
];
|
|
303
|
+
|
|
304
|
+
const params = {
|
|
305
|
+
name: 'John Doe',
|
|
306
|
+
extraParam: 'should be ignored'
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
const result = ParameterValidator.validate(params, paramDefs);
|
|
310
|
+
expect(result.isValid).toBe(true);
|
|
311
|
+
expect(result.errors).toHaveLength(0);
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
it('should handle nested objects and arrays', () => {
|
|
315
|
+
const paramDefs = [
|
|
316
|
+
new Parameter('config', ParameterType.Dictionary, 'Config object', true),
|
|
317
|
+
new Parameter('matrix', ParameterType.List, 'Matrix data', true)
|
|
318
|
+
];
|
|
319
|
+
|
|
320
|
+
const params = {
|
|
321
|
+
config: {
|
|
322
|
+
nested: {
|
|
323
|
+
deep: {
|
|
324
|
+
value: 'test'
|
|
325
|
+
}
|
|
326
|
+
},
|
|
327
|
+
array: [1, 2, 3]
|
|
328
|
+
},
|
|
329
|
+
matrix: [
|
|
330
|
+
[1, 2, 3],
|
|
331
|
+
[4, 5, 6],
|
|
332
|
+
{ nested: 'object in array' }
|
|
333
|
+
]
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
const result = ParameterValidator.validate(params, paramDefs);
|
|
337
|
+
expect(result.isValid).toBe(true);
|
|
338
|
+
expect(result.errors).toHaveLength(0);
|
|
339
|
+
});
|
|
340
|
+
});
|
|
341
|
+
});
|
|
@@ -0,0 +1,153 @@
|
|
|
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
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Parameter validator for tool inputs
|
|
21
|
+
*/
|
|
22
|
+
export class ParameterValidator {
|
|
23
|
+
/**
|
|
24
|
+
* Validate parameters against their definitions
|
|
25
|
+
* @param params The actual parameters received
|
|
26
|
+
* @param parameterDefinitions The expected parameter definitions
|
|
27
|
+
* @returns Validation result with any errors found
|
|
28
|
+
*/
|
|
29
|
+
public static validate(
|
|
30
|
+
params: any,
|
|
31
|
+
parameterDefinitions: Parameter[]
|
|
32
|
+
): ValidationResult {
|
|
33
|
+
const errors: ValidationError[] = [];
|
|
34
|
+
|
|
35
|
+
// Validate each defined parameter
|
|
36
|
+
for (const paramDef of parameterDefinitions) {
|
|
37
|
+
const value = params ? params[paramDef.name] : undefined;
|
|
38
|
+
|
|
39
|
+
// Check if required parameter is missing
|
|
40
|
+
if (paramDef.required && (value === undefined || value === null)) {
|
|
41
|
+
errors.push({
|
|
42
|
+
field: paramDef.name,
|
|
43
|
+
message: `Required parameter '${paramDef.name}' is missing`
|
|
44
|
+
});
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Skip validation if parameter is optional and not provided
|
|
49
|
+
if (!paramDef.required && (value === undefined || value === null)) {
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Validate parameter type
|
|
54
|
+
const typeError = this.validateParameterType(paramDef.name, value, paramDef.type);
|
|
55
|
+
if (typeError) {
|
|
56
|
+
errors.push(typeError);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return { isValid: errors.length === 0, errors };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Validate a single parameter's type
|
|
65
|
+
* @param paramName Parameter name
|
|
66
|
+
* @param value Parameter value
|
|
67
|
+
* @param expectedType Expected parameter type
|
|
68
|
+
* @returns Validation error if invalid, null if valid
|
|
69
|
+
*/
|
|
70
|
+
private static validateParameterType(
|
|
71
|
+
paramName: string,
|
|
72
|
+
value: any,
|
|
73
|
+
expectedType: ParameterType
|
|
74
|
+
): ValidationError | null {
|
|
75
|
+
const actualType = this.getActualType(value);
|
|
76
|
+
|
|
77
|
+
switch (expectedType) {
|
|
78
|
+
case ParameterType.String:
|
|
79
|
+
if (typeof value !== 'string') {
|
|
80
|
+
return {
|
|
81
|
+
field: paramName,
|
|
82
|
+
message: `Parameter '${paramName}' must be a string, but received ${actualType}`
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
break;
|
|
86
|
+
|
|
87
|
+
case ParameterType.Integer:
|
|
88
|
+
if (!Number.isInteger(value)) {
|
|
89
|
+
return {
|
|
90
|
+
field: paramName,
|
|
91
|
+
message: `Parameter '${paramName}' must be an integer, but received ${actualType}`
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
break;
|
|
95
|
+
|
|
96
|
+
case ParameterType.Number:
|
|
97
|
+
if (typeof value !== 'number' || isNaN(value)) {
|
|
98
|
+
return {
|
|
99
|
+
field: paramName,
|
|
100
|
+
message: `Parameter '${paramName}' must be a number, but received ${actualType}`
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
break;
|
|
104
|
+
|
|
105
|
+
case ParameterType.Boolean:
|
|
106
|
+
if (typeof value !== 'boolean') {
|
|
107
|
+
return {
|
|
108
|
+
field: paramName,
|
|
109
|
+
message: `Parameter '${paramName}' must be a boolean, but received ${actualType}`
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
break;
|
|
113
|
+
|
|
114
|
+
case ParameterType.List:
|
|
115
|
+
if (!Array.isArray(value)) {
|
|
116
|
+
return {
|
|
117
|
+
field: paramName,
|
|
118
|
+
message: `Parameter '${paramName}' must be an array, but received ${actualType}`
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
break;
|
|
122
|
+
|
|
123
|
+
case ParameterType.Dictionary:
|
|
124
|
+
if (typeof value !== 'object' || Array.isArray(value) || value === null) {
|
|
125
|
+
return {
|
|
126
|
+
field: paramName,
|
|
127
|
+
message: `Parameter '${paramName}' must be an object, but received ${actualType}`
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
break;
|
|
131
|
+
|
|
132
|
+
default:
|
|
133
|
+
return {
|
|
134
|
+
field: paramName,
|
|
135
|
+
message: `Parameter '${paramName}' has unknown expected type: ${String(expectedType)}`
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Get a human-readable description of the actual type
|
|
144
|
+
* @param value The value to check
|
|
145
|
+
* @returns String description of the type
|
|
146
|
+
*/
|
|
147
|
+
private static getActualType(value: any): string {
|
|
148
|
+
if (value === null) return 'null';
|
|
149
|
+
if (value === undefined) return 'undefined';
|
|
150
|
+
if (Array.isArray(value)) return 'array';
|
|
151
|
+
return typeof value;
|
|
152
|
+
}
|
|
153
|
+
}
|