@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.
@@ -0,0 +1,282 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const ParameterValidator_1 = require("./ParameterValidator");
4
+ const Models_1 = require("../types/Models");
5
+ describe('ParameterValidator', () => {
6
+ describe('validate', () => {
7
+ it('should pass validation for valid parameters', () => {
8
+ const paramDefs = [
9
+ new Models_1.Parameter('name', Models_1.ParameterType.String, 'User name', true),
10
+ new Models_1.Parameter('age', Models_1.ParameterType.Integer, 'User age', false),
11
+ new Models_1.Parameter('score', Models_1.ParameterType.Number, 'User score', false),
12
+ new Models_1.Parameter('active', Models_1.ParameterType.Boolean, 'Is active', false),
13
+ new Models_1.Parameter('tags', Models_1.ParameterType.List, 'User tags', false),
14
+ new Models_1.Parameter('config', Models_1.ParameterType.Dictionary, 'User config', false)
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
+ const result = ParameterValidator_1.ParameterValidator.validate(validParams, paramDefs);
25
+ expect(result.isValid).toBe(true);
26
+ expect(result.errors).toHaveLength(0);
27
+ });
28
+ it('should fail validation for missing required parameters', () => {
29
+ const paramDefs = [
30
+ new Models_1.Parameter('name', Models_1.ParameterType.String, 'User name', true),
31
+ new Models_1.Parameter('email', Models_1.ParameterType.String, 'User email', true)
32
+ ];
33
+ const params = {
34
+ name: 'John Doe'
35
+ // email is missing
36
+ };
37
+ const result = ParameterValidator_1.ParameterValidator.validate(params, paramDefs);
38
+ expect(result.isValid).toBe(false);
39
+ expect(result.errors).toHaveLength(1);
40
+ expect(result.errors[0]).toEqual({
41
+ field: 'email',
42
+ message: "Required parameter 'email' is missing"
43
+ });
44
+ });
45
+ it('should fail validation for wrong parameter types', () => {
46
+ const paramDefs = [
47
+ new Models_1.Parameter('name', Models_1.ParameterType.String, 'User name', true),
48
+ new Models_1.Parameter('age', Models_1.ParameterType.Integer, 'User age', true),
49
+ new Models_1.Parameter('active', Models_1.ParameterType.Boolean, 'Is active', true),
50
+ new Models_1.Parameter('tags', Models_1.ParameterType.List, 'User tags', true),
51
+ new Models_1.Parameter('config', Models_1.ParameterType.Dictionary, 'User config', true)
52
+ ];
53
+ const invalidParams = {
54
+ name: 123, // should be string
55
+ age: '25', // should be integer
56
+ active: 'true', // should be boolean
57
+ tags: 'tag1,tag2', // should be array
58
+ config: 'invalid' // should be object
59
+ };
60
+ const result = ParameterValidator_1.ParameterValidator.validate(invalidParams, paramDefs);
61
+ expect(result.isValid).toBe(false);
62
+ expect(result.errors).toHaveLength(5);
63
+ expect(result.errors[0].field).toBe('name');
64
+ expect(result.errors[0].message).toContain('must be a string');
65
+ expect(result.errors[1].field).toBe('age');
66
+ expect(result.errors[1].message).toContain('must be an integer');
67
+ expect(result.errors[2].field).toBe('active');
68
+ expect(result.errors[2].message).toContain('must be a boolean');
69
+ expect(result.errors[3].field).toBe('tags');
70
+ expect(result.errors[3].message).toContain('must be an array');
71
+ expect(result.errors[4].field).toBe('config');
72
+ expect(result.errors[4].message).toContain('must be an object');
73
+ });
74
+ it('should handle null/undefined parameters correctly', () => {
75
+ const paramDefs = [
76
+ new Models_1.Parameter('required', Models_1.ParameterType.String, 'Required param', true),
77
+ new Models_1.Parameter('optional', Models_1.ParameterType.String, 'Optional param', false)
78
+ ];
79
+ const result1 = ParameterValidator_1.ParameterValidator.validate(null, paramDefs);
80
+ expect(result1.isValid).toBe(false);
81
+ expect(result1.errors).toHaveLength(1);
82
+ expect(result1.errors[0].field).toBe('required');
83
+ const result2 = ParameterValidator_1.ParameterValidator.validate(undefined, paramDefs);
84
+ expect(result2.isValid).toBe(false);
85
+ expect(result2.errors).toHaveLength(1);
86
+ expect(result2.errors[0].field).toBe('required');
87
+ });
88
+ it('should allow optional parameters to be missing', () => {
89
+ const paramDefs = [
90
+ new Models_1.Parameter('name', Models_1.ParameterType.String, 'User name', true),
91
+ new Models_1.Parameter('age', Models_1.ParameterType.Integer, 'User age', false)
92
+ ];
93
+ const params = {
94
+ name: 'John Doe'
95
+ // age is optional and missing
96
+ };
97
+ const result = ParameterValidator_1.ParameterValidator.validate(params, paramDefs);
98
+ expect(result.isValid).toBe(true);
99
+ expect(result.errors).toHaveLength(0);
100
+ });
101
+ it('should distinguish between integer and number types', () => {
102
+ const paramDefs = [
103
+ new Models_1.Parameter('count', Models_1.ParameterType.Integer, 'Item count', true),
104
+ new Models_1.Parameter('score', Models_1.ParameterType.Number, 'Score value', true)
105
+ ];
106
+ const params1 = {
107
+ count: 25.5, // should be integer, not float
108
+ score: 85.5 // number is fine
109
+ };
110
+ const result1 = ParameterValidator_1.ParameterValidator.validate(params1, paramDefs);
111
+ expect(result1.isValid).toBe(false);
112
+ expect(result1.errors).toHaveLength(1);
113
+ expect(result1.errors[0].field).toBe('count');
114
+ expect(result1.errors[0].message).toContain('must be an integer');
115
+ const params2 = {
116
+ count: 25, // integer is fine
117
+ score: 85.5 // number is fine
118
+ };
119
+ const result2 = ParameterValidator_1.ParameterValidator.validate(params2, paramDefs);
120
+ expect(result2.isValid).toBe(true);
121
+ expect(result2.errors).toHaveLength(0);
122
+ });
123
+ it('should handle array vs object distinction', () => {
124
+ const paramDefs = [
125
+ new Models_1.Parameter('tags', Models_1.ParameterType.List, 'Tag list', true),
126
+ new Models_1.Parameter('config', Models_1.ParameterType.Dictionary, 'Config object', true)
127
+ ];
128
+ const params = {
129
+ tags: { 0: 'tag1', 1: 'tag2' }, // object that looks like array
130
+ config: ['key1', 'key2'] // array instead of object
131
+ };
132
+ const result = ParameterValidator_1.ParameterValidator.validate(params, paramDefs);
133
+ expect(result.isValid).toBe(false);
134
+ expect(result.errors).toHaveLength(2);
135
+ expect(result.errors[0].field).toBe('tags');
136
+ expect(result.errors[0].message).toContain('must be an array');
137
+ expect(result.errors[1].field).toBe('config');
138
+ expect(result.errors[1].message).toContain('must be an object');
139
+ });
140
+ it('should handle null values for optional parameters', () => {
141
+ const paramDefs = [
142
+ new Models_1.Parameter('name', Models_1.ParameterType.String, 'User name', true),
143
+ new Models_1.Parameter('age', Models_1.ParameterType.Integer, 'User age', false)
144
+ ];
145
+ const params = {
146
+ name: 'John Doe',
147
+ age: null // null for optional parameter should be allowed
148
+ };
149
+ const result = ParameterValidator_1.ParameterValidator.validate(params, paramDefs);
150
+ expect(result.isValid).toBe(true);
151
+ expect(result.errors).toHaveLength(0);
152
+ });
153
+ it('should handle edge cases for number validation', () => {
154
+ const paramDefs = [
155
+ new Models_1.Parameter('score', Models_1.ParameterType.Number, 'Score value', true)
156
+ ];
157
+ // Test NaN
158
+ const params1 = { score: NaN };
159
+ const result1 = ParameterValidator_1.ParameterValidator.validate(params1, paramDefs);
160
+ expect(result1.isValid).toBe(false);
161
+ expect(result1.errors[0].message).toContain('must be a number');
162
+ // Test Infinity
163
+ const params2 = { score: Infinity };
164
+ const result2 = ParameterValidator_1.ParameterValidator.validate(params2, paramDefs);
165
+ expect(result2.isValid).toBe(true);
166
+ expect(result2.errors).toHaveLength(0);
167
+ // Test negative numbers
168
+ const params3 = { score: -42.5 };
169
+ const result3 = ParameterValidator_1.ParameterValidator.validate(params3, paramDefs);
170
+ expect(result3.isValid).toBe(true);
171
+ expect(result3.errors).toHaveLength(0);
172
+ });
173
+ it('should handle edge cases for integer validation', () => {
174
+ const paramDefs = [
175
+ new Models_1.Parameter('count', Models_1.ParameterType.Integer, 'Item count', true)
176
+ ];
177
+ // Test negative integers
178
+ const params1 = { count: -5 };
179
+ const result1 = ParameterValidator_1.ParameterValidator.validate(params1, paramDefs);
180
+ expect(result1.isValid).toBe(true);
181
+ expect(result1.errors).toHaveLength(0);
182
+ // Test zero
183
+ const params2 = { count: 0 };
184
+ const result2 = ParameterValidator_1.ParameterValidator.validate(params2, paramDefs);
185
+ expect(result2.isValid).toBe(true);
186
+ expect(result2.errors).toHaveLength(0);
187
+ // Test very large integers
188
+ const params3 = { count: Number.MAX_SAFE_INTEGER };
189
+ const result3 = ParameterValidator_1.ParameterValidator.validate(params3, paramDefs);
190
+ expect(result3.isValid).toBe(true);
191
+ expect(result3.errors).toHaveLength(0);
192
+ });
193
+ it('should handle empty arrays and objects', () => {
194
+ const paramDefs = [
195
+ new Models_1.Parameter('tags', Models_1.ParameterType.List, 'Tag list', true),
196
+ new Models_1.Parameter('config', Models_1.ParameterType.Dictionary, 'Config object', true)
197
+ ];
198
+ const params = {
199
+ tags: [], // empty array should be valid
200
+ config: {} // empty object should be valid
201
+ };
202
+ const result = ParameterValidator_1.ParameterValidator.validate(params, paramDefs);
203
+ expect(result.isValid).toBe(true);
204
+ expect(result.errors).toHaveLength(0);
205
+ });
206
+ it('should handle special string values', () => {
207
+ const paramDefs = [
208
+ new Models_1.Parameter('text', Models_1.ParameterType.String, 'Text value', true)
209
+ ];
210
+ // Test empty string
211
+ const params1 = { text: '' };
212
+ const result1 = ParameterValidator_1.ParameterValidator.validate(params1, paramDefs);
213
+ expect(result1.isValid).toBe(true);
214
+ expect(result1.errors).toHaveLength(0);
215
+ // Test string with special characters
216
+ const params2 = { text: 'Hello\nWorld\t!' };
217
+ const result2 = ParameterValidator_1.ParameterValidator.validate(params2, paramDefs);
218
+ expect(result2.isValid).toBe(true);
219
+ expect(result2.errors).toHaveLength(0);
220
+ });
221
+ it('should handle multiple validation errors', () => {
222
+ const paramDefs = [
223
+ new Models_1.Parameter('name', Models_1.ParameterType.String, 'User name', true),
224
+ new Models_1.Parameter('age', Models_1.ParameterType.Integer, 'User age', true),
225
+ new Models_1.Parameter('email', Models_1.ParameterType.String, 'User email', true)
226
+ ];
227
+ const params = {
228
+ name: 123, // wrong type
229
+ age: '25' // wrong type
230
+ // email is missing
231
+ };
232
+ const result = ParameterValidator_1.ParameterValidator.validate(params, paramDefs);
233
+ expect(result.isValid).toBe(false);
234
+ expect(result.errors).toHaveLength(3);
235
+ expect(result.errors.some((e) => e.field === 'name')).toBe(true);
236
+ expect(result.errors.some((e) => e.field === 'age')).toBe(true);
237
+ expect(result.errors.some((e) => e.field === 'email')).toBe(true);
238
+ });
239
+ it('should handle tools with no parameter definitions', () => {
240
+ const result = ParameterValidator_1.ParameterValidator.validate({ someParam: 'value' }, []);
241
+ expect(result.isValid).toBe(true);
242
+ expect(result.errors).toHaveLength(0);
243
+ });
244
+ it('should handle extra parameters not in definition', () => {
245
+ const paramDefs = [
246
+ new Models_1.Parameter('name', Models_1.ParameterType.String, 'User name', true)
247
+ ];
248
+ const params = {
249
+ name: 'John Doe',
250
+ extraParam: 'should be ignored'
251
+ };
252
+ const result = ParameterValidator_1.ParameterValidator.validate(params, paramDefs);
253
+ expect(result.isValid).toBe(true);
254
+ expect(result.errors).toHaveLength(0);
255
+ });
256
+ it('should handle nested objects and arrays', () => {
257
+ const paramDefs = [
258
+ new Models_1.Parameter('config', Models_1.ParameterType.Dictionary, 'Config object', true),
259
+ new Models_1.Parameter('matrix', Models_1.ParameterType.List, 'Matrix data', true)
260
+ ];
261
+ const params = {
262
+ config: {
263
+ nested: {
264
+ deep: {
265
+ value: 'test'
266
+ }
267
+ },
268
+ array: [1, 2, 3]
269
+ },
270
+ matrix: [
271
+ [1, 2, 3],
272
+ [4, 5, 6],
273
+ { nested: 'object in array' }
274
+ ]
275
+ };
276
+ const result = ParameterValidator_1.ParameterValidator.validate(params, paramDefs);
277
+ expect(result.isValid).toBe(true);
278
+ expect(result.errors).toHaveLength(0);
279
+ });
280
+ });
281
+ });
282
+ //# sourceMappingURL=ParameterValidator.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ParameterValidator.test.js","sourceRoot":"","sources":["../../src/validation/ParameterValidator.test.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optimizely-opal/opal-tool-ocp-sdk",
3
- "version": "1.0.0-beta.1",
3
+ "version": "1.0.0-beta.2",
4
4
  "description": "OCP SDK for Opal tool",
5
5
  "scripts": {
6
6
  "validate-deps": "node scripts/validate-deps.js",
@@ -11,11 +11,30 @@ jest.mock('@zaiusinc/app-sdk', () => ({
11
11
  Function: class {
12
12
  public constructor(public request: any) {}
13
13
  },
14
- Response: jest.fn().mockImplementation((status, data) => ({
14
+ Headers: class {
15
+ public constructor(headers: string[][] = []) {
16
+ this.headers = {};
17
+ headers.forEach(([name, value]) => {
18
+ this.headers[name.toLowerCase()] = value;
19
+ });
20
+ }
21
+ public headers: { [key: string]: string };
22
+ public set(name: string, value: string) {
23
+ this.headers[name.toLowerCase()] = value;
24
+ }
25
+ public get(name: string) {
26
+ return this.headers[name.toLowerCase()];
27
+ }
28
+ public has(name: string) {
29
+ return name.toLowerCase() in this.headers;
30
+ }
31
+ },
32
+ Response: jest.fn().mockImplementation((status, data, headers) => ({
15
33
  status,
16
34
  data,
17
35
  bodyJSON: data,
18
- bodyAsU8Array: new Uint8Array()
36
+ bodyAsU8Array: new Uint8Array(),
37
+ headers: headers || { get: jest.fn(), has: jest.fn(), set: jest.fn() }
19
38
  }))
20
39
  }));
21
40
 
@@ -522,15 +541,25 @@ describe('ToolsService', () => {
522
541
 
523
542
  describe('edge cases', () => {
524
543
  it('should handle request with null bodyJSON', async () => {
544
+ // Create a tool without required parameters
545
+ const toolWithoutRequiredParams = {
546
+ name: 'no_required_params_tool',
547
+ description: 'Tool without required parameters',
548
+ handler: jest.fn().mockResolvedValue({ result: 'success' }),
549
+ parameters: [], // No parameters defined
550
+ endpoint: '/no-required-params-tool'
551
+ };
552
+
525
553
  toolsService.registerTool(
526
- mockTool.name,
527
- mockTool.description,
528
- mockTool.handler,
529
- mockTool.parameters,
530
- mockTool.endpoint
554
+ toolWithoutRequiredParams.name,
555
+ toolWithoutRequiredParams.description,
556
+ toolWithoutRequiredParams.handler,
557
+ toolWithoutRequiredParams.parameters,
558
+ toolWithoutRequiredParams.endpoint
531
559
  );
532
560
 
533
561
  const requestWithNullBody = createMockRequest({
562
+ path: '/no-required-params-tool',
534
563
  bodyJSON: null,
535
564
  body: null
536
565
  });
@@ -538,19 +567,29 @@ describe('ToolsService', () => {
538
567
  const response = await toolsService.processRequest(requestWithNullBody, mockToolFunction);
539
568
 
540
569
  expect(response.status).toBe(200);
541
- expect(mockTool.handler).toHaveBeenCalledWith(mockToolFunction, null, undefined);
570
+ expect(toolWithoutRequiredParams.handler).toHaveBeenCalledWith(mockToolFunction, null, undefined);
542
571
  });
543
572
 
544
573
  it('should handle request with undefined bodyJSON', async () => {
574
+ // Create a tool without required parameters
575
+ const toolWithoutRequiredParams = {
576
+ name: 'no_required_params_tool_2',
577
+ description: 'Tool without required parameters',
578
+ handler: jest.fn().mockResolvedValue({ result: 'success' }),
579
+ parameters: [], // No parameters defined
580
+ endpoint: '/no-required-params-tool-2'
581
+ };
582
+
545
583
  toolsService.registerTool(
546
- mockTool.name,
547
- mockTool.description,
548
- mockTool.handler,
549
- mockTool.parameters,
550
- mockTool.endpoint
584
+ toolWithoutRequiredParams.name,
585
+ toolWithoutRequiredParams.description,
586
+ toolWithoutRequiredParams.handler,
587
+ toolWithoutRequiredParams.parameters,
588
+ toolWithoutRequiredParams.endpoint
551
589
  );
552
590
 
553
591
  const requestWithUndefinedBody = createMockRequest({
592
+ path: '/no-required-params-tool-2',
554
593
  bodyJSON: undefined,
555
594
  body: undefined
556
595
  });
@@ -558,7 +597,7 @@ describe('ToolsService', () => {
558
597
  const response = await toolsService.processRequest(requestWithUndefinedBody, mockToolFunction);
559
598
 
560
599
  expect(response.status).toBe(200);
561
- expect(mockTool.handler).toHaveBeenCalledWith(mockToolFunction, undefined, undefined);
600
+ expect(toolWithoutRequiredParams.handler).toHaveBeenCalledWith(mockToolFunction, undefined, undefined);
562
601
  });
563
602
 
564
603
  it('should extract auth data from bodyJSON when body exists', async () => {
@@ -657,5 +696,107 @@ describe('ToolsService', () => {
657
696
  );
658
697
  });
659
698
  });
699
+
700
+ describe('parameter validation', () => {
701
+ beforeEach(() => {
702
+ jest.clearAllMocks();
703
+ });
704
+
705
+ it('should validate parameters and return 400 for invalid types', async () => {
706
+ // Register a tool with specific parameter types
707
+ const toolWithTypedParams = {
708
+ name: 'typed_tool',
709
+ description: 'Tool with typed parameters',
710
+ handler: jest.fn().mockResolvedValue({ result: 'success' }),
711
+ parameters: [
712
+ new Parameter('name', ParameterType.String, 'User name', true),
713
+ new Parameter('age', ParameterType.Integer, 'User age', true),
714
+ new Parameter('active', ParameterType.Boolean, 'Is active', false)
715
+ ],
716
+ endpoint: '/typed-tool'
717
+ };
718
+
719
+ toolsService.registerTool(
720
+ toolWithTypedParams.name,
721
+ toolWithTypedParams.description,
722
+ toolWithTypedParams.handler,
723
+ toolWithTypedParams.parameters,
724
+ toolWithTypedParams.endpoint
725
+ );
726
+
727
+ // Send invalid parameter types
728
+ const invalidRequest = createMockRequest({
729
+ path: '/typed-tool',
730
+ bodyJSON: {
731
+ parameters: {
732
+ name: 123, // should be string
733
+ age: '25', // should be integer
734
+ active: 'true' // should be boolean
735
+ }
736
+ }
737
+ });
738
+
739
+ const response = await toolsService.processRequest(invalidRequest, mockToolFunction);
740
+
741
+ expect(response.status).toBe(400);
742
+
743
+ // Expect RFC 9457 Problem Details format
744
+ expect(response.bodyJSON).toHaveProperty('title', 'One or more validation errors occurred.');
745
+ expect(response.bodyJSON).toHaveProperty('status', 400);
746
+ expect(response.bodyJSON).toHaveProperty('detail', 'See \'errors\' field for details.');
747
+ expect(response.bodyJSON).toHaveProperty('instance', '/typed-tool');
748
+ expect(response.bodyJSON).toHaveProperty('errors');
749
+ expect(response.bodyJSON.errors).toHaveLength(3);
750
+
751
+ // Check error structure - field and message
752
+ const errors = response.bodyJSON.errors;
753
+ expect(errors[0]).toHaveProperty('field', 'name');
754
+ expect(errors[0]).toHaveProperty('message', "Parameter 'name' must be a string, but received number");
755
+
756
+ // Check that the content type is set to application/problem+json for RFC 9457 compliance
757
+ expect(response.headers).toBeDefined();
758
+ expect(response.headers.get('content-type')).toBe('application/problem+json');
759
+
760
+ // Verify the handler was not called
761
+ expect(toolWithTypedParams.handler).not.toHaveBeenCalled();
762
+ });
763
+
764
+ it('should skip validation for tools with no parameter definitions', async () => {
765
+ const toolWithoutParams = {
766
+ name: 'no_params_tool',
767
+ description: 'Tool without parameters',
768
+ handler: jest.fn().mockResolvedValue({ result: 'success' }),
769
+ parameters: [], // No parameters defined
770
+ endpoint: '/no-params-tool'
771
+ };
772
+
773
+ toolsService.registerTool(
774
+ toolWithoutParams.name,
775
+ toolWithoutParams.description,
776
+ toolWithoutParams.handler,
777
+ toolWithoutParams.parameters,
778
+ toolWithoutParams.endpoint
779
+ );
780
+
781
+ // Send request with any data (should be ignored)
782
+ const request = createMockRequest({
783
+ path: '/no-params-tool',
784
+ bodyJSON: {
785
+ parameters: {
786
+ unexpected: 'value'
787
+ }
788
+ }
789
+ });
790
+
791
+ const response = await toolsService.processRequest(request, mockToolFunction);
792
+
793
+ expect(response.status).toBe(200);
794
+ expect(toolWithoutParams.handler).toHaveBeenCalledWith(
795
+ mockToolFunction,
796
+ { unexpected: 'value' },
797
+ undefined
798
+ );
799
+ });
800
+ });
660
801
  });
661
802
  });
@@ -1,9 +1,10 @@
1
1
  /* eslint-disable max-classes-per-file */
2
2
  import { AuthRequirement, Parameter } from '../types/Models';
3
3
  import * as App from '@zaiusinc/app-sdk';
4
- import { logger } from '@zaiusinc/app-sdk';
4
+ import { logger, Headers } from '@zaiusinc/app-sdk';
5
5
  import { ToolFunction } from '../function/ToolFunction';
6
6
  import { GlobalToolFunction } from '../function/GlobalToolFunction';
7
+ import { ParameterValidator } from '../validation/ParameterValidator';
7
8
 
8
9
  /**
9
10
  * Default OptiID authentication requirement that will be enforced for all tools
@@ -173,6 +174,23 @@ export class ToolsService {
173
174
  params = req.bodyJSON;
174
175
  }
175
176
 
177
+ // Validate parameters before calling the handler (only if tool has parameter definitions)
178
+ if (func.parameters && func.parameters.length > 0) {
179
+ const validationResult = ParameterValidator.validate(params, func.parameters);
180
+ if (!validationResult.isValid) {
181
+ return new App.Response(400, {
182
+ title: 'One or more validation errors occurred.',
183
+ status: 400,
184
+ detail: "See 'errors' field for details.",
185
+ instance: func.endpoint,
186
+ errors: validationResult.errors.map((error) => ({
187
+ field: error.field,
188
+ message: error.message
189
+ }))
190
+ }, new Headers([['content-type', 'application/problem+json']]));
191
+ }
192
+ }
193
+
176
194
  // Extract auth data from body JSON
177
195
  const authData = req.bodyJSON ? req.bodyJSON.auth : undefined;
178
196