@optimizely-opal/opal-tool-ocp-sdk 0.0.0-beta.5 → 0.0.0-beta.6

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.
@@ -26,11 +26,13 @@ jest.mock('@zaiusinc/app-sdk', () => ({
26
26
  bodyJSON: data,
27
27
  bodyAsU8Array: new Uint8Array()
28
28
  })),
29
+ amendLogContext: jest.fn(),
29
30
  }));
30
31
 
31
32
  // Create a concrete implementation for testing
32
33
  class TestToolFunction extends ToolFunction {
33
34
  private mockValidateBearerToken: jest.MockedFunction<(token: string) => boolean>;
35
+ private mockReady: jest.MockedFunction<() => Promise<boolean>>;
34
36
 
35
37
  public constructor(request?: any) {
36
38
  super(request || {}); // Pass the request parameter properly
@@ -38,6 +40,7 @@ class TestToolFunction extends ToolFunction {
38
40
  (this as any).request = request;
39
41
 
40
42
  this.mockValidateBearerToken = jest.fn().mockReturnValue(true);
43
+ this.mockReady = jest.fn().mockResolvedValue(true);
41
44
  }
42
45
 
43
46
  // Override the concrete method with mock implementation for testing
@@ -45,6 +48,11 @@ class TestToolFunction extends ToolFunction {
45
48
  return this.mockValidateBearerToken(bearerToken);
46
49
  }
47
50
 
51
+ // Override the ready method with mock implementation for testing
52
+ protected ready(): Promise<boolean> {
53
+ return this.mockReady();
54
+ }
55
+
48
56
  // Expose request and validation mock for testing
49
57
  public getRequest() {
50
58
  return (this as any).request;
@@ -53,6 +61,10 @@ class TestToolFunction extends ToolFunction {
53
61
  public getMockValidateBearerToken() {
54
62
  return this.mockValidateBearerToken;
55
63
  }
64
+
65
+ public getMockReady() {
66
+ return this.mockReady;
67
+ }
56
68
  }
57
69
 
58
70
  describe('ToolFunction', () => {
@@ -81,6 +93,137 @@ describe('ToolFunction', () => {
81
93
  toolFunction = new TestToolFunction(mockRequest);
82
94
  });
83
95
 
96
+ describe('/ready endpoint', () => {
97
+ it('should return ready: true when ready method returns true', async () => {
98
+ // Arrange
99
+ const readyRequest = {
100
+ headers: new Map(),
101
+ method: 'GET',
102
+ path: '/ready'
103
+ };
104
+ toolFunction = new TestToolFunction(readyRequest);
105
+ toolFunction.getMockReady().mockResolvedValue(true);
106
+
107
+ // Act
108
+ const result = await toolFunction.perform();
109
+
110
+ // Assert
111
+ expect(toolFunction.getMockReady()).toHaveBeenCalledTimes(1);
112
+ expect(result).toEqual(new Response(200, { ready: true }));
113
+ expect(mockProcessRequest).not.toHaveBeenCalled(); // Should not call service
114
+ });
115
+
116
+ it('should return ready: false when ready method returns false', async () => {
117
+ // Arrange
118
+ const readyRequest = {
119
+ headers: new Map(),
120
+ method: 'GET',
121
+ path: '/ready'
122
+ };
123
+ toolFunction = new TestToolFunction(readyRequest);
124
+ toolFunction.getMockReady().mockResolvedValue(false);
125
+
126
+ // Act
127
+ const result = await toolFunction.perform();
128
+
129
+ // Assert
130
+ expect(toolFunction.getMockReady()).toHaveBeenCalledTimes(1);
131
+ expect(result).toEqual(new Response(200, { ready: false }));
132
+ expect(mockProcessRequest).not.toHaveBeenCalled(); // Should not call service
133
+ });
134
+
135
+ it('should handle ready method throwing an error', async () => {
136
+ // Arrange
137
+ const readyRequest = {
138
+ headers: new Map(),
139
+ method: 'GET',
140
+ path: '/ready'
141
+ };
142
+ toolFunction = new TestToolFunction(readyRequest);
143
+ toolFunction.getMockReady().mockRejectedValue(new Error('Ready check failed'));
144
+
145
+ // Act & Assert
146
+ await expect(toolFunction.perform()).rejects.toThrow('Ready check failed');
147
+ expect(toolFunction.getMockReady()).toHaveBeenCalledTimes(1);
148
+ expect(mockProcessRequest).not.toHaveBeenCalled(); // Should not call service
149
+ });
150
+
151
+ it('should handle /ready endpoint after bearer token validation passes', async () => {
152
+ // Arrange
153
+ const readyRequest = {
154
+ headers: new Map([['authorization', 'Bearer valid-token']]),
155
+ method: 'GET',
156
+ path: '/ready'
157
+ };
158
+ toolFunction = new TestToolFunction(readyRequest);
159
+ toolFunction.getMockReady().mockResolvedValue(true);
160
+ mockExtractBearerToken.mockReturnValue('valid-token');
161
+ toolFunction.getMockValidateBearerToken().mockReturnValue(true); // Make sure auth passes so we reach /ready
162
+
163
+ // Act
164
+ const result = await toolFunction.perform();
165
+
166
+ // Assert
167
+ expect(toolFunction.getMockReady()).toHaveBeenCalledTimes(1);
168
+ expect(result).toEqual(new Response(200, { ready: true }));
169
+ expect(mockExtractBearerToken).toHaveBeenCalledWith(readyRequest.headers); // Auth is checked first
170
+ expect(toolFunction.getMockValidateBearerToken()).toHaveBeenCalledWith('valid-token'); // Auth validation happens
171
+ expect(mockProcessRequest).not.toHaveBeenCalled(); // Should not call service
172
+ });
173
+
174
+ it('should return 403 when bearer token validation fails even for /ready endpoint', async () => {
175
+ // Arrange
176
+ const readyRequest = {
177
+ headers: new Map([['authorization', 'Bearer invalid-token']]),
178
+ method: 'GET',
179
+ path: '/ready'
180
+ };
181
+ toolFunction = new TestToolFunction(readyRequest);
182
+ toolFunction.getMockReady().mockResolvedValue(true);
183
+ mockExtractBearerToken.mockReturnValue('invalid-token');
184
+ toolFunction.getMockValidateBearerToken().mockReturnValue(false); // Auth fails
185
+
186
+ // Act
187
+ const result = await toolFunction.perform();
188
+
189
+ // Assert
190
+ expect(result).toEqual(new Response(403, { error: 'Forbidden' }));
191
+ expect(mockExtractBearerToken).toHaveBeenCalledWith(readyRequest.headers);
192
+ expect(toolFunction.getMockValidateBearerToken()).toHaveBeenCalledWith('invalid-token');
193
+ expect(toolFunction.getMockReady()).not.toHaveBeenCalled(); // Should not reach ready check
194
+ expect(mockProcessRequest).not.toHaveBeenCalled(); // Should not call service
195
+ });
196
+
197
+ it('should use default ready implementation when not overridden', async () => {
198
+ // Create a class that doesn't override ready method
199
+ class DefaultReadyToolFunction extends ToolFunction {
200
+ public constructor(request?: any) {
201
+ super(request || {});
202
+ (this as any).request = request;
203
+ }
204
+
205
+ public getRequest() {
206
+ return (this as any).request;
207
+ }
208
+ }
209
+
210
+ // Arrange
211
+ const readyRequest = {
212
+ headers: new Map(),
213
+ method: 'GET',
214
+ path: '/ready'
215
+ };
216
+ const defaultToolFunction = new DefaultReadyToolFunction(readyRequest);
217
+
218
+ // Act
219
+ const result = await defaultToolFunction.perform();
220
+
221
+ // Assert - Default implementation should return true
222
+ expect(result).toEqual(new Response(200, { ready: true }));
223
+ expect(mockProcessRequest).not.toHaveBeenCalled(); // Should not call service
224
+ });
225
+ });
226
+
84
227
  describe('bearer token validation', () => {
85
228
  it('should extract bearer token from headers and validate it', async () => {
86
229
  // Arrange
@@ -95,7 +238,7 @@ describe('ToolFunction', () => {
95
238
  // Assert
96
239
  expect(mockExtractBearerToken).toHaveBeenCalledWith(mockRequest.headers);
97
240
  expect(toolFunction.getMockValidateBearerToken()).toHaveBeenCalledWith(bearerToken);
98
- expect(mockProcessRequest).toHaveBeenCalledWith(mockRequest, expect.any(Function), toolFunction);
241
+ expect(mockProcessRequest).toHaveBeenCalledWith(mockRequest, toolFunction);
99
242
  expect(result).toBe(mockResponse);
100
243
  });
101
244
 
@@ -129,7 +272,7 @@ describe('ToolFunction', () => {
129
272
  // Assert
130
273
  expect(mockExtractBearerToken).toHaveBeenCalledWith(mockRequest.headers);
131
274
  expect(toolFunction.getMockValidateBearerToken()).toHaveBeenCalledWith(complexToken);
132
- expect(mockProcessRequest).toHaveBeenCalledWith(mockRequest, expect.any(Function), toolFunction);
275
+ expect(mockProcessRequest).toHaveBeenCalledWith(mockRequest, toolFunction);
133
276
  expect(result).toBe(mockResponse);
134
277
  });
135
278
 
@@ -189,7 +332,7 @@ describe('ToolFunction', () => {
189
332
  // Assert
190
333
  expect(mockExtractBearerToken).toHaveBeenCalledWith(mockRequest.headers);
191
334
  expect(toolFunction.getMockValidateBearerToken()).not.toHaveBeenCalled();
192
- expect(mockProcessRequest).toHaveBeenCalledWith(mockRequest, expect.any(Function), toolFunction);
335
+ expect(mockProcessRequest).toHaveBeenCalledWith(mockRequest, toolFunction);
193
336
  expect(result).toBe(mockResponse);
194
337
  });
195
338
 
@@ -204,7 +347,7 @@ describe('ToolFunction', () => {
204
347
  // Assert
205
348
  expect(mockExtractBearerToken).toHaveBeenCalledWith(mockRequest.headers);
206
349
  expect(toolFunction.getMockValidateBearerToken()).not.toHaveBeenCalled();
207
- expect(mockProcessRequest).toHaveBeenCalledWith(mockRequest, expect.any(Function), toolFunction);
350
+ expect(mockProcessRequest).toHaveBeenCalledWith(mockRequest, toolFunction);
208
351
  expect(result).toBe(mockResponse);
209
352
  });
210
353
 
@@ -232,7 +375,7 @@ describe('ToolFunction', () => {
232
375
 
233
376
  // Assert - Default implementation should return true and allow request to proceed
234
377
  expect(mockExtractBearerToken).toHaveBeenCalledWith(mockRequest.headers);
235
- expect(mockProcessRequest).toHaveBeenCalledWith(mockRequest, expect.any(Function), defaultToolFunction);
378
+ expect(mockProcessRequest).toHaveBeenCalledWith(mockRequest, defaultToolFunction);
236
379
  expect(result).toBe(mockResponse);
237
380
  });
238
381
  });
@@ -1,4 +1,4 @@
1
- import { Function, Response } from '@zaiusinc/app-sdk';
1
+ import { Function, Response, amendLogContext } from '@zaiusinc/app-sdk';
2
2
  import { toolsService } from '../service/Service';
3
3
 
4
4
  /**
@@ -23,13 +23,18 @@ export abstract class ToolFunction extends Function {
23
23
  * @returns Response as the HTTP response
24
24
  */
25
25
  public async perform(): Promise<Response> {
26
+ amendLogContext({ opalThreadId: this.request.headers.get('x-opal-thread-id') || '' });
26
27
  const bearerToken = toolsService.extractBearerToken(this.request.headers);
27
28
  if (bearerToken && !this.validateBearerToken(bearerToken)) {
28
29
  return new Response(403, { error: 'Forbidden' });
29
30
  }
30
31
 
32
+ if (this.request.path === '/ready') {
33
+ const isReady = await this.ready();
34
+ return new Response(200, { ready: isReady });
35
+ }
31
36
  // Pass 'this' as context so decorated methods can use the existing instance
32
- return toolsService.processRequest(this.request, this.ready.bind(this), this);
37
+ return toolsService.processRequest(this.request, this);
33
38
  }
34
39
 
35
40
  /**
@@ -22,7 +22,7 @@ jest.mock('@zaiusinc/app-sdk', () => ({
22
22
  describe('ToolsService', () => {
23
23
  let mockTool: Tool<unknown>;
24
24
  let mockInteraction: Interaction<unknown>;
25
- let mockReadyCheck: jest.MockedFunction<() => Promise<boolean>>;
25
+ let mockToolFunction: ToolFunction;
26
26
 
27
27
  beforeEach(() => {
28
28
  // Clear registered functions and interactions before each test
@@ -32,8 +32,13 @@ describe('ToolsService', () => {
32
32
  // Reset all mocks
33
33
  jest.clearAllMocks();
34
34
 
35
- // Create mock ready check function
36
- mockReadyCheck = jest.fn().mockResolvedValue(true);
35
+ // Create mock ToolFunction
36
+ mockToolFunction = {
37
+ ready: jest.fn().mockResolvedValue(true),
38
+ perform: jest.fn(),
39
+ validateBearerToken: jest.fn().mockReturnValue(true),
40
+ request: {} as any
41
+ } as any;
37
42
 
38
43
  // Create mock tool handler
39
44
  const mockToolHandler = jest.fn().mockResolvedValue({ result: 'success' });
@@ -98,7 +103,7 @@ describe('ToolsService', () => {
98
103
  );
99
104
 
100
105
  const discoveryRequest = createMockRequest({ path: '/discovery' });
101
- const response = await toolsService.processRequest(discoveryRequest, mockReadyCheck);
106
+ const response = await toolsService.processRequest(discoveryRequest, mockToolFunction);
102
107
 
103
108
  expect(response.status).toBe(200);
104
109
  expect(response).toHaveProperty('bodyJSON');
@@ -123,7 +128,7 @@ describe('ToolsService', () => {
123
128
 
124
129
  it('should return empty functions array when no tools are registered', async () => {
125
130
  const discoveryRequest = createMockRequest({ path: '/discovery' });
126
- const response = await toolsService.processRequest(discoveryRequest, mockReadyCheck);
131
+ const response = await toolsService.processRequest(discoveryRequest, mockToolFunction);
127
132
 
128
133
  expect(response.status).toBe(200);
129
134
  expect(response.bodyAsU8Array).toBeDefined();
@@ -157,7 +162,7 @@ describe('ToolsService', () => {
157
162
  );
158
163
 
159
164
  const discoveryRequest = createMockRequest({ path: '/discovery' });
160
- const response = await toolsService.processRequest(discoveryRequest, mockReadyCheck);
165
+ const response = await toolsService.processRequest(discoveryRequest, mockToolFunction);
161
166
 
162
167
  expect(response.status).toBe(200);
163
168
 
@@ -190,37 +195,6 @@ describe('ToolsService', () => {
190
195
  });
191
196
  });
192
197
 
193
- describe('/ready endpoint', () => {
194
- it('should return ready: true when readyCheck returns true', async () => {
195
- const readyRequest = createMockRequest({ path: '/ready' });
196
- const mockReadyCheckTrue = jest.fn().mockResolvedValue(true);
197
- const response = await toolsService.processRequest(readyRequest, mockReadyCheckTrue);
198
-
199
- expect(response.status).toBe(200);
200
- expect(response.bodyJSON).toEqual({ ready: true });
201
- expect(mockReadyCheckTrue).toHaveBeenCalledTimes(1);
202
- });
203
-
204
- it('should return ready: false when readyCheck returns false', async () => {
205
- const readyRequest = createMockRequest({ path: '/ready' });
206
- const mockReadyCheckFalse = jest.fn().mockResolvedValue(false);
207
- const response = await toolsService.processRequest(readyRequest, mockReadyCheckFalse);
208
-
209
- expect(response.status).toBe(200);
210
- expect(response.bodyJSON).toEqual({ ready: false });
211
- expect(mockReadyCheckFalse).toHaveBeenCalledTimes(1);
212
- });
213
-
214
- it('should handle readyCheck throwing an error', async () => {
215
- const readyRequest = createMockRequest({ path: '/ready' });
216
- const mockReadyCheckError = jest.fn().mockRejectedValue(new Error('Ready check failed'));
217
-
218
- await expect(toolsService.processRequest(readyRequest, mockReadyCheckError)).
219
- rejects.toThrow('Ready check failed');
220
- expect(mockReadyCheckError).toHaveBeenCalledTimes(1);
221
- });
222
- });
223
-
224
198
  describe('tool execution', () => {
225
199
  beforeEach(() => {
226
200
  toolsService.registerTool(
@@ -234,11 +208,11 @@ describe('ToolsService', () => {
234
208
 
235
209
  it('should execute tool successfully with parameters', async () => {
236
210
  const mockRequest = createMockRequest();
237
- const response = await toolsService.processRequest(mockRequest, mockReadyCheck);
211
+ const response = await toolsService.processRequest(mockRequest, mockToolFunction);
238
212
 
239
213
  expect(response.status).toBe(200);
240
214
  expect(mockTool.handler).toHaveBeenCalledWith(
241
- undefined, // functionContext
215
+ mockToolFunction, // functionContext
242
216
  { param1: 'test-value' },
243
217
  undefined
244
218
  );
@@ -248,11 +222,15 @@ describe('ToolsService', () => {
248
222
  // Create a mock ToolFunction instance
249
223
  const mockToolFunctionInstance = {
250
224
  someProperty: 'test-value',
251
- someMethod: jest.fn()
252
- };
225
+ someMethod: jest.fn(),
226
+ ready: jest.fn().mockResolvedValue(true),
227
+ perform: jest.fn(),
228
+ validateBearerToken: jest.fn().mockReturnValue(true),
229
+ request: {} as any
230
+ } as any;
253
231
 
254
232
  const mockRequest = createMockRequest();
255
- const response = await toolsService.processRequest(mockRequest, mockReadyCheck, mockToolFunctionInstance);
233
+ const response = await toolsService.processRequest(mockRequest, mockToolFunctionInstance);
256
234
 
257
235
  expect(response.status).toBe(200);
258
236
  expect(mockTool.handler).toHaveBeenCalledWith(
@@ -310,7 +288,7 @@ describe('ToolsService', () => {
310
288
  bodyJSON: { action: 'test' }
311
289
  });
312
290
 
313
- const response = await toolsService.processRequest(testRequest, mockReadyCheck, mockToolFunctionInstance);
291
+ const response = await toolsService.processRequest(testRequest, mockToolFunctionInstance);
314
292
 
315
293
  expect(response.status).toBe(200);
316
294
  expect((response as any).data).toBeDefined();
@@ -342,11 +320,11 @@ describe('ToolsService', () => {
342
320
  })
343
321
  });
344
322
 
345
- const response = await toolsService.processRequest(requestWithAuth, mockReadyCheck);
323
+ const response = await toolsService.processRequest(requestWithAuth, mockToolFunction);
346
324
 
347
325
  expect(response.status).toBe(200);
348
326
  expect(mockTool.handler).toHaveBeenCalledWith(
349
- undefined, // functionContext
327
+ mockToolFunction, // functionContext
350
328
  { param1: 'test-value' },
351
329
  authData
352
330
  );
@@ -358,11 +336,11 @@ describe('ToolsService', () => {
358
336
  body: JSON.stringify({ param1: 'test-value' })
359
337
  });
360
338
 
361
- const response = await toolsService.processRequest(requestWithoutWrapper, mockReadyCheck);
339
+ const response = await toolsService.processRequest(requestWithoutWrapper, mockToolFunction);
362
340
 
363
341
  expect(response.status).toBe(200);
364
342
  expect(mockTool.handler).toHaveBeenCalledWith(
365
- undefined, // functionContext
343
+ mockToolFunction, // functionContext
366
344
  { param1: 'test-value' },
367
345
  undefined
368
346
  );
@@ -373,7 +351,7 @@ describe('ToolsService', () => {
373
351
  jest.mocked(mockTool.handler).mockRejectedValueOnce(new Error(errorMessage));
374
352
 
375
353
  const mockRequest = createMockRequest();
376
- const response = await toolsService.processRequest(mockRequest, mockReadyCheck);
354
+ const response = await toolsService.processRequest(mockRequest, mockToolFunction);
377
355
 
378
356
  expect(response.status).toBe(500);
379
357
  expect(logger.error).toHaveBeenCalledWith(
@@ -386,7 +364,7 @@ describe('ToolsService', () => {
386
364
  jest.mocked(mockTool.handler).mockRejectedValueOnce({});
387
365
 
388
366
  const mockRequest = createMockRequest();
389
- const response = await toolsService.processRequest(mockRequest, mockReadyCheck);
367
+ const response = await toolsService.processRequest(mockRequest, mockToolFunction);
390
368
 
391
369
  expect(response.status).toBe(500);
392
370
  });
@@ -408,10 +386,10 @@ describe('ToolsService', () => {
408
386
  body: JSON.stringify({ data: { param1: 'test-value' } })
409
387
  });
410
388
 
411
- const response = await toolsService.processRequest(interactionRequest, mockReadyCheck);
389
+ const response = await toolsService.processRequest(interactionRequest, mockToolFunction);
412
390
 
413
391
  expect(response.status).toBe(200);
414
- expect(mockInteraction.handler).toHaveBeenCalledWith(undefined, { param1: 'test-value' }, undefined);
392
+ expect(mockInteraction.handler).toHaveBeenCalledWith(mockToolFunction, { param1: 'test-value' }, undefined);
415
393
  });
416
394
 
417
395
  it('should handle interaction request body without data wrapper', async () => {
@@ -421,10 +399,10 @@ describe('ToolsService', () => {
421
399
  body: JSON.stringify({ param1: 'test-value' })
422
400
  });
423
401
 
424
- const response = await toolsService.processRequest(interactionRequest, mockReadyCheck);
402
+ const response = await toolsService.processRequest(interactionRequest, mockToolFunction);
425
403
 
426
404
  expect(response.status).toBe(200);
427
- expect(mockInteraction.handler).toHaveBeenCalledWith(undefined, { param1: 'test-value' }, undefined);
405
+ expect(mockInteraction.handler).toHaveBeenCalledWith(mockToolFunction, { param1: 'test-value' }, undefined);
428
406
  });
429
407
 
430
408
  it('should execute interaction with OptiID auth data when provided', async () => {
@@ -445,11 +423,11 @@ describe('ToolsService', () => {
445
423
  })
446
424
  });
447
425
 
448
- const response = await toolsService.processRequest(interactionRequest, mockReadyCheck);
426
+ const response = await toolsService.processRequest(interactionRequest, mockToolFunction);
449
427
 
450
428
  expect(response.status).toBe(200);
451
429
  expect(mockInteraction.handler).toHaveBeenCalledWith(
452
- undefined, // functionContext
430
+ mockToolFunction, // functionContext
453
431
  { param1: 'test-value' },
454
432
  authData
455
433
  );
@@ -473,11 +451,11 @@ describe('ToolsService', () => {
473
451
  })
474
452
  });
475
453
 
476
- const response = await toolsService.processRequest(interactionRequest, mockReadyCheck);
454
+ const response = await toolsService.processRequest(interactionRequest, mockToolFunction);
477
455
 
478
456
  expect(response.status).toBe(200);
479
457
  expect(mockInteraction.handler).toHaveBeenCalledWith(
480
- undefined, // functionContext
458
+ mockToolFunction, // functionContext
481
459
  {
482
460
  param1: 'test-value',
483
461
  auth: authData
@@ -495,7 +473,7 @@ describe('ToolsService', () => {
495
473
  bodyJSON: { data: { param1: 'test-value' } }
496
474
  });
497
475
 
498
- const response = await toolsService.processRequest(interactionRequest, mockReadyCheck);
476
+ const response = await toolsService.processRequest(interactionRequest, mockToolFunction);
499
477
 
500
478
  expect(response.status).toBe(500);
501
479
  expect(logger.error).toHaveBeenCalledWith(
@@ -508,7 +486,7 @@ describe('ToolsService', () => {
508
486
  describe('error cases', () => {
509
487
  it('should return 404 when no matching tool or interaction is found', async () => {
510
488
  const unknownRequest = createMockRequest({ path: '/unknown-endpoint' });
511
- const response = await toolsService.processRequest(unknownRequest, mockReadyCheck);
489
+ const response = await toolsService.processRequest(unknownRequest, mockToolFunction);
512
490
 
513
491
  expect(response.status).toBe(404);
514
492
  });
@@ -531,7 +509,7 @@ describe('ToolsService', () => {
531
509
  path: '/optid-auth-tool'
532
510
  });
533
511
 
534
- const response = await toolsService.processRequest(authRequest, mockReadyCheck);
512
+ const response = await toolsService.processRequest(authRequest, mockToolFunction);
535
513
 
536
514
  expect(response.status).toBe(200);
537
515
  });
@@ -552,10 +530,10 @@ describe('ToolsService', () => {
552
530
  body: null
553
531
  });
554
532
 
555
- const response = await toolsService.processRequest(requestWithNullBody, mockReadyCheck);
533
+ const response = await toolsService.processRequest(requestWithNullBody, mockToolFunction);
556
534
 
557
535
  expect(response.status).toBe(200);
558
- expect(mockTool.handler).toHaveBeenCalledWith(undefined, null, undefined);
536
+ expect(mockTool.handler).toHaveBeenCalledWith(mockToolFunction, null, undefined);
559
537
  });
560
538
 
561
539
  it('should handle request with undefined bodyJSON', async () => {
@@ -572,10 +550,10 @@ describe('ToolsService', () => {
572
550
  body: undefined
573
551
  });
574
552
 
575
- const response = await toolsService.processRequest(requestWithUndefinedBody, mockReadyCheck);
553
+ const response = await toolsService.processRequest(requestWithUndefinedBody, mockToolFunction);
576
554
 
577
555
  expect(response.status).toBe(200);
578
- expect(mockTool.handler).toHaveBeenCalledWith(undefined, undefined, undefined);
556
+ expect(mockTool.handler).toHaveBeenCalledWith(mockToolFunction, undefined, undefined);
579
557
  });
580
558
 
581
559
  it('should extract auth data from bodyJSON when body exists', async () => {
@@ -603,11 +581,11 @@ describe('ToolsService', () => {
603
581
  })
604
582
  });
605
583
 
606
- const response = await toolsService.processRequest(requestWithAuth, mockReadyCheck);
584
+ const response = await toolsService.processRequest(requestWithAuth, mockToolFunction);
607
585
 
608
586
  expect(response.status).toBe(200);
609
587
  expect(mockTool.handler).toHaveBeenCalledWith(
610
- undefined, // functionContext
588
+ mockToolFunction, // functionContext
611
589
  { param1: 'test-value' },
612
590
  authData
613
591
  );
@@ -632,11 +610,11 @@ describe('ToolsService', () => {
632
610
  })
633
611
  });
634
612
 
635
- const response = await toolsService.processRequest(requestWithoutAuth, mockReadyCheck);
613
+ const response = await toolsService.processRequest(requestWithoutAuth, mockToolFunction);
636
614
 
637
615
  expect(response.status).toBe(200);
638
616
  expect(mockTool.handler).toHaveBeenCalledWith(
639
- undefined, // functionContext
617
+ mockToolFunction, // functionContext
640
618
  { param1: 'test-value' },
641
619
  undefined
642
620
  );
@@ -664,11 +642,11 @@ describe('ToolsService', () => {
664
642
  body: ''
665
643
  });
666
644
 
667
- const response = await toolsService.processRequest(requestWithAuthButNoBody, mockReadyCheck);
645
+ const response = await toolsService.processRequest(requestWithAuthButNoBody, mockToolFunction);
668
646
 
669
647
  expect(response.status).toBe(200);
670
648
  expect(mockTool.handler).toHaveBeenCalledWith(
671
- undefined, // functionContext
649
+ mockToolFunction, // functionContext
672
650
  { param1: 'test-value' },
673
651
  authData
674
652
  );
@@ -2,6 +2,7 @@
2
2
  import { AuthRequirement, Parameter } from '../types/Models';
3
3
  import * as App from '@zaiusinc/app-sdk';
4
4
  import { logger } from '@zaiusinc/app-sdk';
5
+ import { ToolFunction } from '../function/ToolFunction';
5
6
 
6
7
 
7
8
 
@@ -22,7 +23,7 @@ export class Interaction<TAuthData> {
22
23
  public constructor(
23
24
  public name: string,
24
25
  public endpoint: string,
25
- public handler: (functionContext: any, data: unknown, authData?: TAuthData) => Promise<InteractionResult>
26
+ public handler: (functionContext: ToolFunction, data: unknown, authData?: TAuthData) => Promise<InteractionResult>
26
27
  ) {}
27
28
  }
28
29
 
@@ -49,7 +50,7 @@ export class Tool<TAuthData> {
49
50
  public description: string,
50
51
  public parameters: Parameter[],
51
52
  public endpoint: string,
52
- public handler: (functionContext: any, params: unknown, authData?: TAuthData) => Promise<unknown>,
53
+ public handler: (functionContext: ToolFunction, params: unknown, authData?: TAuthData) => Promise<unknown>,
53
54
  public authRequirements?: AuthRequirement[]
54
55
  ) {}
55
56
 
@@ -104,7 +105,7 @@ export class ToolsService {
104
105
  public registerTool<TAuthData>(
105
106
  name: string,
106
107
  description: string,
107
- handler: (functionContext: any, params: unknown, authData?: TAuthData) => Promise<unknown>,
108
+ handler: (functionContext: ToolFunction, params: unknown, authData?: TAuthData) => Promise<unknown>,
108
109
  parameters: Parameter[],
109
110
  endpoint: string,
110
111
  authRequirements?: AuthRequirement[]
@@ -121,7 +122,7 @@ export class ToolsService {
121
122
  */
122
123
  public registerInteraction<TAuthData>(
123
124
  name: string,
124
- handler: (functionContext: any, data: unknown, authData?: TAuthData) => Promise<InteractionResult>,
125
+ handler: (functionContext: ToolFunction, data: unknown, authData?: TAuthData) => Promise<InteractionResult>,
125
126
  endpoint: string
126
127
  ): void {
127
128
  const func = new Interaction<TAuthData>(name, endpoint, handler);
@@ -129,13 +130,9 @@ export class ToolsService {
129
130
  }
130
131
 
131
132
  public async processRequest(req: App.Request,
132
- readyCheck: () => Promise<boolean>,
133
- functionContext?: any): Promise<App.Response> {
133
+ functionContext: ToolFunction): Promise<App.Response> {
134
134
  if (req.path === '/discovery') {
135
135
  return new App.Response(200, { functions: Array.from(this.functions.values()).map((f) => f.toJSON()) });
136
- } else if (req.path === '/ready') {
137
- const isReady = await readyCheck();
138
- return new App.Response(200, { ready: isReady });
139
136
  } else {
140
137
  const func = this.functions.get(req.path);
141
138
  if (func) {