@optimizely-opal/opal-tool-ocp-sdk 1.0.0-beta.6 → 1.0.0-beta.7
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 +13 -3
- package/dist/auth/AuthUtils.d.ts +0 -7
- package/dist/auth/AuthUtils.d.ts.map +1 -1
- package/dist/auth/AuthUtils.js +0 -27
- package/dist/auth/AuthUtils.js.map +1 -1
- package/dist/auth/AuthUtils.test.js +0 -99
- package/dist/auth/AuthUtils.test.js.map +1 -1
- package/dist/function/GlobalToolFunction.d.ts +3 -2
- package/dist/function/GlobalToolFunction.d.ts.map +1 -1
- package/dist/function/GlobalToolFunction.js +4 -4
- package/dist/function/GlobalToolFunction.js.map +1 -1
- package/dist/function/GlobalToolFunction.test.js +16 -4
- package/dist/function/GlobalToolFunction.test.js.map +1 -1
- package/dist/function/ToolFunction.d.ts +3 -2
- package/dist/function/ToolFunction.d.ts.map +1 -1
- package/dist/function/ToolFunction.js +5 -12
- package/dist/function/ToolFunction.js.map +1 -1
- package/dist/function/ToolFunction.test.js +15 -209
- package/dist/function/ToolFunction.test.js.map +1 -1
- package/dist/logging/ToolLogger.d.ts.map +1 -1
- package/dist/logging/ToolLogger.js +1 -2
- package/dist/logging/ToolLogger.js.map +1 -1
- package/dist/logging/ToolLogger.test.js +2 -114
- package/dist/logging/ToolLogger.test.js.map +1 -1
- package/dist/service/Service.d.ts +0 -73
- package/dist/service/Service.d.ts.map +1 -1
- package/dist/service/Service.js +42 -188
- package/dist/service/Service.js.map +1 -1
- package/dist/service/Service.test.js +34 -380
- package/dist/service/Service.test.js.map +1 -1
- package/dist/types/Models.d.ts +4 -0
- package/dist/types/Models.d.ts.map +1 -1
- package/dist/types/ToolError.d.ts +0 -13
- package/dist/types/ToolError.d.ts.map +1 -1
- package/dist/types/ToolError.js +2 -30
- package/dist/types/ToolError.js.map +1 -1
- package/dist/types/ToolError.test.js +3 -27
- package/dist/types/ToolError.test.js.map +1 -1
- package/dist/validation/ParameterValidator.test.js +1 -2
- package/dist/validation/ParameterValidator.test.js.map +1 -1
- package/package.json +1 -1
- package/src/auth/AuthUtils.test.ts +1 -115
- package/src/auth/AuthUtils.ts +1 -30
- package/src/function/GlobalToolFunction.test.ts +21 -6
- package/src/function/GlobalToolFunction.ts +6 -5
- package/src/function/ToolFunction.test.ts +21 -225
- package/src/function/ToolFunction.ts +8 -13
- package/src/logging/ToolLogger.test.ts +2 -118
- package/src/logging/ToolLogger.ts +1 -2
- package/src/service/Service.test.ts +32 -474
- package/src/service/Service.ts +41 -245
- package/src/types/Models.ts +5 -0
- package/src/types/ToolError.test.ts +3 -33
- package/src/types/ToolError.ts +2 -32
- package/src/validation/ParameterValidator.test.ts +1 -4
|
@@ -53,16 +53,16 @@ jest.mock('@zaiusinc/app-sdk', () => ({
|
|
|
53
53
|
|
|
54
54
|
// Create a concrete implementation for testing
|
|
55
55
|
class TestToolFunction extends ToolFunction {
|
|
56
|
-
private mockReady: jest.MockedFunction<() => Promise<boolean>>;
|
|
56
|
+
private mockReady: jest.MockedFunction<() => Promise<{ ready: boolean; reason?: string }>>;
|
|
57
57
|
|
|
58
58
|
public constructor(request?: any) {
|
|
59
59
|
super(request || {});
|
|
60
60
|
(this as any).request = request;
|
|
61
|
-
this.mockReady = jest.fn().mockResolvedValue(true);
|
|
61
|
+
this.mockReady = jest.fn().mockResolvedValue({ ready: true });
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
// Override the ready method with mock implementation for testing
|
|
65
|
-
protected ready(): Promise<boolean> {
|
|
65
|
+
protected ready(): Promise<{ ready: boolean; reason?: string }> {
|
|
66
66
|
return this.mockReady();
|
|
67
67
|
}
|
|
68
68
|
|
|
@@ -174,7 +174,7 @@ describe('ToolFunction', () => {
|
|
|
174
174
|
const readyRequest = createReadyRequestWithAuth();
|
|
175
175
|
|
|
176
176
|
toolFunction = new TestToolFunction(readyRequest);
|
|
177
|
-
toolFunction.getMockReady().mockResolvedValue(true);
|
|
177
|
+
toolFunction.getMockReady().mockResolvedValue({ ready: true });
|
|
178
178
|
|
|
179
179
|
// Act
|
|
180
180
|
const result = await toolFunction.perform();
|
|
@@ -190,7 +190,7 @@ describe('ToolFunction', () => {
|
|
|
190
190
|
const readyRequest = createReadyRequestWithAuth();
|
|
191
191
|
|
|
192
192
|
toolFunction = new TestToolFunction(readyRequest);
|
|
193
|
-
toolFunction.getMockReady().mockResolvedValue(false);
|
|
193
|
+
toolFunction.getMockReady().mockResolvedValue({ ready: false });
|
|
194
194
|
|
|
195
195
|
// Act
|
|
196
196
|
const result = await toolFunction.perform();
|
|
@@ -201,6 +201,22 @@ describe('ToolFunction', () => {
|
|
|
201
201
|
expect(mockProcessRequest).not.toHaveBeenCalled(); // Should not call service
|
|
202
202
|
});
|
|
203
203
|
|
|
204
|
+
it('should return ready: false with reason when ready method returns false with reason', async () => {
|
|
205
|
+
// Arrange
|
|
206
|
+
const readyRequest = createReadyRequestWithAuth();
|
|
207
|
+
|
|
208
|
+
toolFunction = new TestToolFunction(readyRequest);
|
|
209
|
+
toolFunction.getMockReady().mockResolvedValue({ ready: false, reason: 'Database connection failed' });
|
|
210
|
+
|
|
211
|
+
// Act
|
|
212
|
+
const result = await toolFunction.perform();
|
|
213
|
+
|
|
214
|
+
// Assert
|
|
215
|
+
expect(toolFunction.getMockReady()).toHaveBeenCalledTimes(1);
|
|
216
|
+
expect(result).toEqual(new Response(200, { ready: false, reason: 'Database connection failed' }));
|
|
217
|
+
expect(mockProcessRequest).not.toHaveBeenCalled(); // Should not call service
|
|
218
|
+
});
|
|
219
|
+
|
|
204
220
|
it('should handle ready method throwing an error', async () => {
|
|
205
221
|
// Arrange
|
|
206
222
|
const readyRequest = createReadyRequestWithAuth();
|
|
@@ -420,224 +436,4 @@ describe('ToolFunction', () => {
|
|
|
420
436
|
expect(toolFunction.getRequest()).toBe(mockRequest);
|
|
421
437
|
});
|
|
422
438
|
});
|
|
423
|
-
|
|
424
|
-
describe('internal request authentication', () => {
|
|
425
|
-
beforeEach(() => {
|
|
426
|
-
// Reset mocks before each test
|
|
427
|
-
jest.clearAllMocks();
|
|
428
|
-
setupAuthMocks();
|
|
429
|
-
});
|
|
430
|
-
|
|
431
|
-
it('should use internal authentication for /overrides endpoint', async () => {
|
|
432
|
-
const overridesRequest = {
|
|
433
|
-
path: '/overrides',
|
|
434
|
-
method: 'DELETE',
|
|
435
|
-
bodyJSON: {},
|
|
436
|
-
headers: {
|
|
437
|
-
get: jest.fn().mockImplementation((name: string) => {
|
|
438
|
-
if (name === 'Authorization' || name === 'authorization') return 'internal-token';
|
|
439
|
-
if (name === 'x-opal-thread-id') return 'test-thread-id';
|
|
440
|
-
return null;
|
|
441
|
-
})
|
|
442
|
-
}
|
|
443
|
-
};
|
|
444
|
-
|
|
445
|
-
mockProcessRequest.mockResolvedValue(mockResponse);
|
|
446
|
-
const toolFunctionOverrides = new TestToolFunction(overridesRequest);
|
|
447
|
-
const result = await toolFunctionOverrides.perform();
|
|
448
|
-
|
|
449
|
-
expect(result).toBe(mockResponse);
|
|
450
|
-
expect(mockProcessRequest).toHaveBeenCalledWith(overridesRequest, toolFunctionOverrides);
|
|
451
|
-
});
|
|
452
|
-
|
|
453
|
-
it('should throw ToolError when internal authentication fails for /overrides endpoint', async () => {
|
|
454
|
-
const overridesRequest = {
|
|
455
|
-
path: '/overrides',
|
|
456
|
-
method: 'DELETE',
|
|
457
|
-
bodyJSON: {},
|
|
458
|
-
headers: {
|
|
459
|
-
get: jest.fn().mockImplementation((name: string) => {
|
|
460
|
-
if (name === 'x-opal-thread-id') return 'test-thread-id';
|
|
461
|
-
return null; // No Authorization header
|
|
462
|
-
})
|
|
463
|
-
}
|
|
464
|
-
};
|
|
465
|
-
|
|
466
|
-
const toolFunctionOverrides = new TestToolFunction(overridesRequest);
|
|
467
|
-
const result = await toolFunctionOverrides.perform();
|
|
468
|
-
|
|
469
|
-
expect(result.status).toBe(401);
|
|
470
|
-
expect(result.bodyJSON).toEqual({
|
|
471
|
-
title: 'Unauthorized',
|
|
472
|
-
status: 401,
|
|
473
|
-
detail: 'Internal request authentication failed',
|
|
474
|
-
instance: '/overrides'
|
|
475
|
-
});
|
|
476
|
-
expect(mockProcessRequest).not.toHaveBeenCalled();
|
|
477
|
-
});
|
|
478
|
-
|
|
479
|
-
it('should use regular authentication for non-overrides endpoints', async () => {
|
|
480
|
-
const regularRequest = {
|
|
481
|
-
...mockRequest,
|
|
482
|
-
path: '/regular-tool',
|
|
483
|
-
headers: {
|
|
484
|
-
get: jest.fn().mockImplementation((name: string) => {
|
|
485
|
-
if (name === 'x-opal-thread-id') return 'test-thread-id';
|
|
486
|
-
return null;
|
|
487
|
-
})
|
|
488
|
-
}
|
|
489
|
-
};
|
|
490
|
-
|
|
491
|
-
mockProcessRequest.mockResolvedValue(mockResponse);
|
|
492
|
-
const toolFunctionRegular = new TestToolFunction(regularRequest);
|
|
493
|
-
const result = await toolFunctionRegular.perform();
|
|
494
|
-
|
|
495
|
-
expect(result).toBe(mockResponse);
|
|
496
|
-
expect(mockProcessRequest).toHaveBeenCalledWith(regularRequest, toolFunctionRegular);
|
|
497
|
-
});
|
|
498
|
-
|
|
499
|
-
it('should handle internal authentication with valid Authorization header', async () => {
|
|
500
|
-
const overridesRequest = {
|
|
501
|
-
path: '/overrides',
|
|
502
|
-
method: 'PATCH',
|
|
503
|
-
bodyJSON: {
|
|
504
|
-
functions: [
|
|
505
|
-
{
|
|
506
|
-
name: 'test_tool',
|
|
507
|
-
description: 'Updated description'
|
|
508
|
-
}
|
|
509
|
-
]
|
|
510
|
-
},
|
|
511
|
-
headers: {
|
|
512
|
-
get: jest.fn().mockImplementation((name: string) => {
|
|
513
|
-
if (name === 'Authorization') return 'valid-internal-token';
|
|
514
|
-
if (name === 'x-opal-thread-id') return 'test-thread-id';
|
|
515
|
-
return null;
|
|
516
|
-
})
|
|
517
|
-
}
|
|
518
|
-
};
|
|
519
|
-
|
|
520
|
-
mockProcessRequest.mockResolvedValue(mockResponse);
|
|
521
|
-
const toolFunctionOverrides = new TestToolFunction(overridesRequest);
|
|
522
|
-
const result = await toolFunctionOverrides.perform();
|
|
523
|
-
|
|
524
|
-
expect(result).toBe(mockResponse);
|
|
525
|
-
expect(mockProcessRequest).toHaveBeenCalledWith(overridesRequest, toolFunctionOverrides);
|
|
526
|
-
});
|
|
527
|
-
|
|
528
|
-
it('should handle internal authentication with lowercase authorization header', async () => {
|
|
529
|
-
const overridesRequest = {
|
|
530
|
-
path: '/overrides',
|
|
531
|
-
method: 'PATCH',
|
|
532
|
-
bodyJSON: {
|
|
533
|
-
functions: [
|
|
534
|
-
{
|
|
535
|
-
name: 'test_tool',
|
|
536
|
-
description: 'Updated description'
|
|
537
|
-
}
|
|
538
|
-
]
|
|
539
|
-
},
|
|
540
|
-
headers: {
|
|
541
|
-
get: jest.fn().mockImplementation((name: string) => {
|
|
542
|
-
if (name === 'authorization') return 'valid-internal-token';
|
|
543
|
-
if (name === 'x-opal-thread-id') return 'test-thread-id';
|
|
544
|
-
return null;
|
|
545
|
-
})
|
|
546
|
-
}
|
|
547
|
-
};
|
|
548
|
-
|
|
549
|
-
mockProcessRequest.mockResolvedValue(mockResponse);
|
|
550
|
-
const toolFunctionOverrides = new TestToolFunction(overridesRequest);
|
|
551
|
-
const result = await toolFunctionOverrides.perform();
|
|
552
|
-
|
|
553
|
-
expect(result).toBe(mockResponse);
|
|
554
|
-
expect(mockProcessRequest).toHaveBeenCalledWith(overridesRequest, toolFunctionOverrides);
|
|
555
|
-
});
|
|
556
|
-
|
|
557
|
-
it('should fail internal authentication when token verification fails', async () => {
|
|
558
|
-
// Mock token verifier to return false for invalid token
|
|
559
|
-
mockTokenVerifier.verify.mockResolvedValue(false);
|
|
560
|
-
|
|
561
|
-
const overridesRequest = {
|
|
562
|
-
path: '/overrides',
|
|
563
|
-
method: 'DELETE',
|
|
564
|
-
bodyJSON: {},
|
|
565
|
-
headers: {
|
|
566
|
-
get: jest.fn().mockImplementation((name: string) => {
|
|
567
|
-
if (name === 'Authorization') return 'invalid-token';
|
|
568
|
-
if (name === 'x-opal-thread-id') return 'test-thread-id';
|
|
569
|
-
return null;
|
|
570
|
-
})
|
|
571
|
-
}
|
|
572
|
-
};
|
|
573
|
-
|
|
574
|
-
const toolFunctionOverrides = new TestToolFunction(overridesRequest);
|
|
575
|
-
const result = await toolFunctionOverrides.perform();
|
|
576
|
-
|
|
577
|
-
expect(result.status).toBe(401);
|
|
578
|
-
expect(result.bodyJSON).toEqual({
|
|
579
|
-
title: 'Unauthorized',
|
|
580
|
-
status: 401,
|
|
581
|
-
detail: 'Internal request authentication failed',
|
|
582
|
-
instance: '/overrides'
|
|
583
|
-
});
|
|
584
|
-
expect(mockProcessRequest).not.toHaveBeenCalled();
|
|
585
|
-
});
|
|
586
|
-
|
|
587
|
-
it('should fail internal authentication when token verifier throws error', async () => {
|
|
588
|
-
// Mock token verifier to throw an error
|
|
589
|
-
mockTokenVerifier.verify.mockRejectedValue(new Error('Token service unavailable'));
|
|
590
|
-
|
|
591
|
-
const overridesRequest = {
|
|
592
|
-
path: '/overrides',
|
|
593
|
-
method: 'DELETE',
|
|
594
|
-
bodyJSON: {},
|
|
595
|
-
headers: {
|
|
596
|
-
get: jest.fn().mockImplementation((name: string) => {
|
|
597
|
-
if (name === 'Authorization') return 'some-token';
|
|
598
|
-
if (name === 'x-opal-thread-id') return 'test-thread-id';
|
|
599
|
-
return null;
|
|
600
|
-
})
|
|
601
|
-
}
|
|
602
|
-
};
|
|
603
|
-
|
|
604
|
-
const toolFunctionOverrides = new TestToolFunction(overridesRequest);
|
|
605
|
-
const result = await toolFunctionOverrides.perform();
|
|
606
|
-
|
|
607
|
-
expect(result.status).toBe(401);
|
|
608
|
-
expect(result.bodyJSON).toEqual({
|
|
609
|
-
title: 'Unauthorized',
|
|
610
|
-
status: 401,
|
|
611
|
-
detail: 'Internal request authentication failed',
|
|
612
|
-
instance: '/overrides'
|
|
613
|
-
});
|
|
614
|
-
expect(mockProcessRequest).not.toHaveBeenCalled();
|
|
615
|
-
});
|
|
616
|
-
|
|
617
|
-
it('should fail internal authentication when headers object is missing', async () => {
|
|
618
|
-
const overridesRequest = {
|
|
619
|
-
path: '/overrides',
|
|
620
|
-
method: 'DELETE',
|
|
621
|
-
bodyJSON: {},
|
|
622
|
-
headers: {
|
|
623
|
-
get: jest.fn().mockImplementation((name: string) => {
|
|
624
|
-
if (name === 'x-opal-thread-id') return 'test-thread-id';
|
|
625
|
-
return null;
|
|
626
|
-
})
|
|
627
|
-
}
|
|
628
|
-
};
|
|
629
|
-
|
|
630
|
-
const toolFunctionOverrides = new TestToolFunction(overridesRequest);
|
|
631
|
-
const result = await toolFunctionOverrides.perform();
|
|
632
|
-
|
|
633
|
-
expect(result.status).toBe(401);
|
|
634
|
-
expect(result.bodyJSON).toEqual({
|
|
635
|
-
title: 'Unauthorized',
|
|
636
|
-
status: 401,
|
|
637
|
-
detail: 'Internal request authentication failed',
|
|
638
|
-
instance: '/overrides'
|
|
639
|
-
});
|
|
640
|
-
expect(mockProcessRequest).not.toHaveBeenCalled();
|
|
641
|
-
});
|
|
642
|
-
});
|
|
643
439
|
});
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { Function, Response, Headers, amendLogContext } from '@zaiusinc/app-sdk';
|
|
2
|
-
import { authenticateRegularRequest
|
|
2
|
+
import { authenticateRegularRequest } from '../auth/AuthUtils';
|
|
3
3
|
import { toolsService } from '../service/Service';
|
|
4
4
|
import { ToolLogger } from '../logging/ToolLogger';
|
|
5
5
|
import { ToolError } from '../types/ToolError';
|
|
6
|
+
import { ReadyResponse } from '../types/Models';
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* Abstract base class for tool-based function execution
|
|
@@ -14,10 +15,10 @@ export abstract class ToolFunction extends Function {
|
|
|
14
15
|
* Override this method to implement any required credentials and/or other configuration
|
|
15
16
|
* exist and are valid. Reasonable caching should be utilized to prevent excessive requests to external resources.
|
|
16
17
|
* @async
|
|
17
|
-
* @returns
|
|
18
|
+
* @returns ReadyResponse containing ready status and optional reason when not ready
|
|
18
19
|
*/
|
|
19
|
-
protected ready(): Promise<
|
|
20
|
-
return Promise.resolve(true);
|
|
20
|
+
protected ready(): Promise<ReadyResponse> {
|
|
21
|
+
return Promise.resolve({ ready: true });
|
|
21
22
|
}
|
|
22
23
|
|
|
23
24
|
/**
|
|
@@ -67,8 +68,8 @@ export abstract class ToolFunction extends Function {
|
|
|
67
68
|
}
|
|
68
69
|
|
|
69
70
|
if (this.request.path === '/ready') {
|
|
70
|
-
const
|
|
71
|
-
return new Response(200,
|
|
71
|
+
const readyResponse = await this.ready();
|
|
72
|
+
return new Response(200, readyResponse);
|
|
72
73
|
}
|
|
73
74
|
|
|
74
75
|
// Pass 'this' as context so decorated methods can use the existing instance
|
|
@@ -81,12 +82,6 @@ export abstract class ToolFunction extends Function {
|
|
|
81
82
|
* @throws {ToolError} If authentication fails
|
|
82
83
|
*/
|
|
83
84
|
private async authorizeRequest(): Promise<void> {
|
|
84
|
-
|
|
85
|
-
if (this.request.path === '/overrides') {
|
|
86
|
-
await authenticateInternalRequest(this.request);
|
|
87
|
-
} else {
|
|
88
|
-
// Use regular authentication for other endpoints (body-based token with org validation)
|
|
89
|
-
await authenticateRegularRequest(this.request);
|
|
90
|
-
}
|
|
85
|
+
await authenticateRegularRequest(this.request);
|
|
91
86
|
}
|
|
92
87
|
}
|
|
@@ -32,7 +32,6 @@ describe('ToolLogger', () => {
|
|
|
32
32
|
const createMockRequest = (overrides: any = {}): App.Request => {
|
|
33
33
|
const defaultRequest = {
|
|
34
34
|
path: '/test-tool',
|
|
35
|
-
method: 'POST',
|
|
36
35
|
bodyJSON: {
|
|
37
36
|
parameters: {
|
|
38
37
|
name: 'test',
|
|
@@ -97,7 +96,6 @@ describe('ToolLogger', () => {
|
|
|
97
96
|
const expectedLog = {
|
|
98
97
|
event: 'opal_tool_request',
|
|
99
98
|
path: '/test-tool',
|
|
100
|
-
method: 'POST',
|
|
101
99
|
parameters: {
|
|
102
100
|
name: 'test',
|
|
103
101
|
value: 'data'
|
|
@@ -120,7 +118,6 @@ describe('ToolLogger', () => {
|
|
|
120
118
|
expectJsonLog({
|
|
121
119
|
event: 'opal_tool_request',
|
|
122
120
|
path: '/test-tool',
|
|
123
|
-
method: 'POST',
|
|
124
121
|
parameters: null
|
|
125
122
|
});
|
|
126
123
|
});
|
|
@@ -138,7 +135,6 @@ describe('ToolLogger', () => {
|
|
|
138
135
|
expectJsonLog({
|
|
139
136
|
event: 'opal_tool_request',
|
|
140
137
|
path: '/test-tool',
|
|
141
|
-
method: 'POST',
|
|
142
138
|
parameters: {
|
|
143
139
|
name: 'direct',
|
|
144
140
|
action: 'test'
|
|
@@ -171,7 +167,6 @@ describe('ToolLogger', () => {
|
|
|
171
167
|
expectJsonLog({
|
|
172
168
|
event: 'opal_tool_request',
|
|
173
169
|
path: '/test-tool',
|
|
174
|
-
method: 'POST',
|
|
175
170
|
parameters: {
|
|
176
171
|
username: 'john',
|
|
177
172
|
password: '[REDACTED]',
|
|
@@ -209,7 +204,6 @@ describe('ToolLogger', () => {
|
|
|
209
204
|
expectJsonLog({
|
|
210
205
|
event: 'opal_tool_request',
|
|
211
206
|
path: '/test-tool',
|
|
212
|
-
method: 'POST',
|
|
213
207
|
parameters: {
|
|
214
208
|
PASSWORD: '[REDACTED]',
|
|
215
209
|
API_KEY: '[REDACTED]',
|
|
@@ -238,7 +232,6 @@ describe('ToolLogger', () => {
|
|
|
238
232
|
expectJsonLog({
|
|
239
233
|
event: 'opal_tool_request',
|
|
240
234
|
path: '/test-tool',
|
|
241
|
-
method: 'POST',
|
|
242
235
|
parameters: {
|
|
243
236
|
description: `${'a'.repeat(100)}... (truncated, 150 chars total)`,
|
|
244
237
|
short_field: 'normal'
|
|
@@ -262,7 +255,6 @@ describe('ToolLogger', () => {
|
|
|
262
255
|
expectJsonLog({
|
|
263
256
|
event: 'opal_tool_request',
|
|
264
257
|
path: '/test-tool',
|
|
265
|
-
method: 'POST',
|
|
266
258
|
parameters: {
|
|
267
259
|
items: [
|
|
268
260
|
...largeArray.slice(0, 10),
|
|
@@ -299,7 +291,6 @@ describe('ToolLogger', () => {
|
|
|
299
291
|
expectJsonLog({
|
|
300
292
|
event: 'opal_tool_request',
|
|
301
293
|
path: '/test-tool',
|
|
302
|
-
method: 'POST',
|
|
303
294
|
parameters: {
|
|
304
295
|
user: {
|
|
305
296
|
name: 'John',
|
|
@@ -337,7 +328,6 @@ describe('ToolLogger', () => {
|
|
|
337
328
|
expectJsonLog({
|
|
338
329
|
event: 'opal_tool_request',
|
|
339
330
|
path: '/test-tool',
|
|
340
|
-
method: 'POST',
|
|
341
331
|
parameters: {
|
|
342
332
|
nullValue: null,
|
|
343
333
|
emptyString: '',
|
|
@@ -363,7 +353,6 @@ describe('ToolLogger', () => {
|
|
|
363
353
|
expectJsonLog({
|
|
364
354
|
event: 'opal_tool_request',
|
|
365
355
|
path: '/test-tool',
|
|
366
|
-
method: 'POST',
|
|
367
356
|
parameters: {
|
|
368
357
|
credentials: '[REDACTED]',
|
|
369
358
|
public_list: ['item1', 'item2']
|
|
@@ -392,7 +381,6 @@ describe('ToolLogger', () => {
|
|
|
392
381
|
expectJsonLog({
|
|
393
382
|
event: 'opal_tool_request',
|
|
394
383
|
path: '/test-tool',
|
|
395
|
-
method: 'POST',
|
|
396
384
|
parameters: {
|
|
397
385
|
auth: '[REDACTED]',
|
|
398
386
|
public_config: {
|
|
@@ -508,9 +496,7 @@ describe('ToolLogger', () => {
|
|
|
508
496
|
// First item: deeply nested object with inner parts replaced by placeholder
|
|
509
497
|
expect(items[0]).toEqual({
|
|
510
498
|
level2: {
|
|
511
|
-
level3:
|
|
512
|
-
level4: '[MAX_DEPTH_EXCEEDED]'
|
|
513
|
-
}
|
|
499
|
+
level3: '[MAX_DEPTH_EXCEEDED]'
|
|
514
500
|
}
|
|
515
501
|
});
|
|
516
502
|
|
|
@@ -523,9 +509,7 @@ describe('ToolLogger', () => {
|
|
|
523
509
|
// Fourth item: another deeply nested object with inner parts replaced by placeholder
|
|
524
510
|
expect(items[3]).toEqual({
|
|
525
511
|
level2: {
|
|
526
|
-
level3:
|
|
527
|
-
level4: '[MAX_DEPTH_EXCEEDED]'
|
|
528
|
-
}
|
|
512
|
+
level3: '[MAX_DEPTH_EXCEEDED]'
|
|
529
513
|
}
|
|
530
514
|
});
|
|
531
515
|
});
|
|
@@ -710,7 +694,6 @@ describe('ToolLogger', () => {
|
|
|
710
694
|
expectJsonLog({
|
|
711
695
|
event: 'opal_tool_request',
|
|
712
696
|
path: '/test-tool',
|
|
713
|
-
method: 'POST',
|
|
714
697
|
parameters: {}
|
|
715
698
|
});
|
|
716
699
|
});
|
|
@@ -729,7 +712,6 @@ describe('ToolLogger', () => {
|
|
|
729
712
|
expectJsonLog({
|
|
730
713
|
event: 'opal_tool_request',
|
|
731
714
|
path: '/test-tool',
|
|
732
|
-
method: 'POST',
|
|
733
715
|
parameters: {
|
|
734
716
|
field: 'value'
|
|
735
717
|
}
|
|
@@ -756,7 +738,6 @@ describe('ToolLogger', () => {
|
|
|
756
738
|
expectJsonLog({
|
|
757
739
|
event: 'opal_tool_request',
|
|
758
740
|
path: '/test-tool',
|
|
759
|
-
method: 'POST',
|
|
760
741
|
parameters: {
|
|
761
742
|
string: 'text',
|
|
762
743
|
number: 42,
|
|
@@ -768,102 +749,5 @@ describe('ToolLogger', () => {
|
|
|
768
749
|
}
|
|
769
750
|
});
|
|
770
751
|
});
|
|
771
|
-
|
|
772
|
-
it('should handle tool override request with enhanced parameter descriptions', () => {
|
|
773
|
-
const overrideRequest = {
|
|
774
|
-
tools: [
|
|
775
|
-
{
|
|
776
|
-
name: 'calculate_experiment_runtime',
|
|
777
|
-
description: 'OVERRIDDEN: Enhanced experiment runtime calculator with advanced features',
|
|
778
|
-
parameters: [
|
|
779
|
-
{
|
|
780
|
-
name: 'BCR',
|
|
781
|
-
type: 'number',
|
|
782
|
-
description: 'OVERRIDDEN: Enhanced baseline conversion rate with validation',
|
|
783
|
-
required: true
|
|
784
|
-
},
|
|
785
|
-
{
|
|
786
|
-
name: 'MDE',
|
|
787
|
-
type: 'number',
|
|
788
|
-
description: 'OVERRIDDEN: Enhanced minimum detectable effect calculation',
|
|
789
|
-
required: true
|
|
790
|
-
},
|
|
791
|
-
{
|
|
792
|
-
name: 'sigLevel',
|
|
793
|
-
type: 'number',
|
|
794
|
-
description: 'OVERRIDDEN: Enhanced statistical significance level',
|
|
795
|
-
required: true
|
|
796
|
-
},
|
|
797
|
-
{
|
|
798
|
-
name: 'numVariations',
|
|
799
|
-
type: 'number',
|
|
800
|
-
description: 'OVERRIDDEN: Enhanced number of variations handling',
|
|
801
|
-
required: true
|
|
802
|
-
},
|
|
803
|
-
{
|
|
804
|
-
name: 'dailyVisitors',
|
|
805
|
-
type: 'number',
|
|
806
|
-
description: 'OVERRIDDEN: Enhanced daily visitor count with forecasting',
|
|
807
|
-
required: true
|
|
808
|
-
}
|
|
809
|
-
]
|
|
810
|
-
}
|
|
811
|
-
// Note: NOT including calculate_sample_size in override
|
|
812
|
-
]
|
|
813
|
-
};
|
|
814
|
-
|
|
815
|
-
const req = createMockRequest({
|
|
816
|
-
path: '/overrides',
|
|
817
|
-
bodyJSON: overrideRequest
|
|
818
|
-
});
|
|
819
|
-
|
|
820
|
-
ToolLogger.logRequest(req);
|
|
821
|
-
|
|
822
|
-
expectJsonLog({
|
|
823
|
-
event: 'opal_tool_request',
|
|
824
|
-
path: '/overrides',
|
|
825
|
-
method: 'POST',
|
|
826
|
-
parameters: {
|
|
827
|
-
tools: [
|
|
828
|
-
{
|
|
829
|
-
name: 'calculate_experiment_runtime',
|
|
830
|
-
description: 'OVERRIDDEN: Enhanced experiment runtime calculator with advanced features',
|
|
831
|
-
parameters: [
|
|
832
|
-
{
|
|
833
|
-
name: 'BCR',
|
|
834
|
-
type: 'number',
|
|
835
|
-
description: 'OVERRIDDEN: Enhanced baseline conversion rate with validation',
|
|
836
|
-
required: true
|
|
837
|
-
},
|
|
838
|
-
{
|
|
839
|
-
name: 'MDE',
|
|
840
|
-
type: 'number',
|
|
841
|
-
description: 'OVERRIDDEN: Enhanced minimum detectable effect calculation',
|
|
842
|
-
required: true
|
|
843
|
-
},
|
|
844
|
-
{
|
|
845
|
-
name: 'sigLevel',
|
|
846
|
-
type: 'number',
|
|
847
|
-
description: 'OVERRIDDEN: Enhanced statistical significance level',
|
|
848
|
-
required: true
|
|
849
|
-
},
|
|
850
|
-
{
|
|
851
|
-
name: 'numVariations',
|
|
852
|
-
type: 'number',
|
|
853
|
-
description: 'OVERRIDDEN: Enhanced number of variations handling',
|
|
854
|
-
required: true
|
|
855
|
-
},
|
|
856
|
-
{
|
|
857
|
-
name: 'dailyVisitors',
|
|
858
|
-
type: 'number',
|
|
859
|
-
description: 'OVERRIDDEN: Enhanced daily visitor count with forecasting',
|
|
860
|
-
required: true
|
|
861
|
-
}
|
|
862
|
-
]
|
|
863
|
-
}
|
|
864
|
-
]
|
|
865
|
-
}
|
|
866
|
-
});
|
|
867
|
-
});
|
|
868
752
|
});
|
|
869
753
|
});
|
|
@@ -75,7 +75,7 @@ export class ToolLogger {
|
|
|
75
75
|
|
|
76
76
|
if (Array.isArray(data)) {
|
|
77
77
|
const truncated = data.slice(0, this.MAX_ARRAY_ITEMS);
|
|
78
|
-
const result = truncated.map((item) => this.redactSensitiveData(item, maxDepth));
|
|
78
|
+
const result = truncated.map((item) => this.redactSensitiveData(item, maxDepth - 1));
|
|
79
79
|
if (data.length > this.MAX_ARRAY_ITEMS) {
|
|
80
80
|
result.push(`... (${data.length - this.MAX_ARRAY_ITEMS} more items truncated)`);
|
|
81
81
|
}
|
|
@@ -146,7 +146,6 @@ export class ToolLogger {
|
|
|
146
146
|
const requestLog = {
|
|
147
147
|
event: 'opal_tool_request',
|
|
148
148
|
path: req.path,
|
|
149
|
-
method: req.method,
|
|
150
149
|
parameters: this.createParameterSummary(params)
|
|
151
150
|
};
|
|
152
151
|
|