@optimizely-opal/opal-tool-ocp-sdk 0.0.0-OCP-1487.1
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 +631 -0
- package/dist/auth/AuthUtils.d.ts +31 -0
- package/dist/auth/AuthUtils.d.ts.map +1 -0
- package/dist/auth/AuthUtils.js +64 -0
- package/dist/auth/AuthUtils.js.map +1 -0
- package/dist/auth/AuthUtils.test.d.ts +2 -0
- package/dist/auth/AuthUtils.test.d.ts.map +1 -0
- package/dist/auth/AuthUtils.test.js +469 -0
- package/dist/auth/AuthUtils.test.js.map +1 -0
- package/dist/auth/TokenVerifier.d.ts +31 -0
- package/dist/auth/TokenVerifier.d.ts.map +1 -0
- package/dist/auth/TokenVerifier.js +127 -0
- package/dist/auth/TokenVerifier.js.map +1 -0
- package/dist/auth/TokenVerifier.test.d.ts +2 -0
- package/dist/auth/TokenVerifier.test.d.ts.map +1 -0
- package/dist/auth/TokenVerifier.test.js +125 -0
- package/dist/auth/TokenVerifier.test.js.map +1 -0
- package/dist/decorator/Decorator.d.ts +48 -0
- package/dist/decorator/Decorator.d.ts.map +1 -0
- package/dist/decorator/Decorator.js +53 -0
- package/dist/decorator/Decorator.js.map +1 -0
- package/dist/decorator/Decorator.test.d.ts +2 -0
- package/dist/decorator/Decorator.test.d.ts.map +1 -0
- package/dist/decorator/Decorator.test.js +528 -0
- package/dist/decorator/Decorator.test.js.map +1 -0
- package/dist/function/GlobalToolFunction.d.ts +28 -0
- package/dist/function/GlobalToolFunction.d.ts.map +1 -0
- package/dist/function/GlobalToolFunction.js +56 -0
- package/dist/function/GlobalToolFunction.js.map +1 -0
- package/dist/function/GlobalToolFunction.test.d.ts +2 -0
- package/dist/function/GlobalToolFunction.test.d.ts.map +1 -0
- package/dist/function/GlobalToolFunction.test.js +425 -0
- package/dist/function/GlobalToolFunction.test.js.map +1 -0
- package/dist/function/ToolFunction.d.ts +28 -0
- package/dist/function/ToolFunction.d.ts.map +1 -0
- package/dist/function/ToolFunction.js +60 -0
- package/dist/function/ToolFunction.js.map +1 -0
- package/dist/function/ToolFunction.test.d.ts +2 -0
- package/dist/function/ToolFunction.test.d.ts.map +1 -0
- package/dist/function/ToolFunction.test.js +314 -0
- package/dist/function/ToolFunction.test.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +26 -0
- package/dist/index.js.map +1 -0
- package/dist/service/Service.d.ts +80 -0
- package/dist/service/Service.d.ts.map +1 -0
- package/dist/service/Service.js +210 -0
- package/dist/service/Service.js.map +1 -0
- package/dist/service/Service.test.d.ts +2 -0
- package/dist/service/Service.test.d.ts.map +1 -0
- package/dist/service/Service.test.js +427 -0
- package/dist/service/Service.test.js.map +1 -0
- package/dist/types/Models.d.ts +126 -0
- package/dist/types/Models.d.ts.map +1 -0
- package/dist/types/Models.js +181 -0
- package/dist/types/Models.js.map +1 -0
- package/package.json +64 -0
- package/src/auth/AuthUtils.test.ts +586 -0
- package/src/auth/AuthUtils.ts +66 -0
- package/src/auth/TokenVerifier.test.ts +165 -0
- package/src/auth/TokenVerifier.ts +145 -0
- package/src/decorator/Decorator.test.ts +649 -0
- package/src/decorator/Decorator.ts +111 -0
- package/src/function/GlobalToolFunction.test.ts +505 -0
- package/src/function/GlobalToolFunction.ts +61 -0
- package/src/function/ToolFunction.test.ts +374 -0
- package/src/function/ToolFunction.ts +64 -0
- package/src/index.ts +5 -0
- package/src/service/Service.test.ts +661 -0
- package/src/service/Service.ts +213 -0
- package/src/types/Models.ts +163 -0
package/README.md
ADDED
|
@@ -0,0 +1,631 @@
|
|
|
1
|
+
# OPAL TOOL OCP SDK
|
|
2
|
+
|
|
3
|
+
> **Optimizely Connect Platform (OCP) SDK for OPAL Tool**
|
|
4
|
+
|
|
5
|
+
A TypeScript SDK for building Opal tools in Optimizely Connect Platform. This SDK provides decorators, abstractions, and utilities to simplify the development.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- ๐ฏ **Decorator-based Tool Registration** - Use `@tool` and `@interaction` decorators to easily register functions
|
|
10
|
+
- ๐ง **Type-safe Development** - Full TypeScript support with comprehensive type definitions
|
|
11
|
+
- ๐๏ธ **Abstract Base Classes** - Extend `ToolFunction` for standardized request processing
|
|
12
|
+
- ๐ **Authentication Support** - OptiID authentication
|
|
13
|
+
- ๐ก๏ธ **Authorization Support** - OptiID token tool authorization
|
|
14
|
+
- ๐ **Parameter Validation** - Define and validate tool parameters with types
|
|
15
|
+
- ๐งช **Comprehensive Testing** - Fully tested with Jest
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install @optimizely-opal/opal-tool-ocp-sdk
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
or
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
yarn add @optimizely-opal/opal-tool-ocp-sdk
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Quick Start
|
|
30
|
+
|
|
31
|
+
Create a tool function class by extending `ToolFunction` and registering your tools and interactions:
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
import { ToolFunction, tool, interaction, ParameterType, InteractionResult, OptiIdAuthData } from '@optimizely-opal/opal-tool-ocp-sdk';
|
|
35
|
+
|
|
36
|
+
export class MyToolFunction extends ToolFunction {
|
|
37
|
+
|
|
38
|
+
// Register a simple tool without authentication
|
|
39
|
+
@tool({
|
|
40
|
+
name: 'create_task',
|
|
41
|
+
description: 'Creates a new task in the system',
|
|
42
|
+
endpoint: '/create-task',
|
|
43
|
+
parameters: [
|
|
44
|
+
{
|
|
45
|
+
name: 'title',
|
|
46
|
+
type: ParameterType.String,
|
|
47
|
+
description: 'The task title',
|
|
48
|
+
required: true
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
name: 'priority',
|
|
52
|
+
type: ParameterType.String,
|
|
53
|
+
description: 'Task priority level',
|
|
54
|
+
required: false
|
|
55
|
+
}
|
|
56
|
+
]
|
|
57
|
+
})
|
|
58
|
+
async createTask(params: { title: string; priority?: string }, authData?: OptiIdAuthData) {
|
|
59
|
+
return {
|
|
60
|
+
id: '123',
|
|
61
|
+
title: params.title,
|
|
62
|
+
priority: params.priority || 'medium'
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Register a tool with OptiID authentication
|
|
67
|
+
@tool({
|
|
68
|
+
name: 'secure_task',
|
|
69
|
+
description: 'Creates a secure task with OptiID authentication',
|
|
70
|
+
endpoint: '/secure-task',
|
|
71
|
+
parameters: [
|
|
72
|
+
{
|
|
73
|
+
name: 'title',
|
|
74
|
+
type: ParameterType.String,
|
|
75
|
+
description: 'The task title',
|
|
76
|
+
required: true
|
|
77
|
+
}
|
|
78
|
+
],
|
|
79
|
+
authRequirements: [
|
|
80
|
+
{
|
|
81
|
+
provider: 'OptiID',
|
|
82
|
+
scopeBundle: 'tasks',
|
|
83
|
+
required: true
|
|
84
|
+
}
|
|
85
|
+
]
|
|
86
|
+
})
|
|
87
|
+
async createSecureTask(params: { title: string }, authData?: OptiIdAuthData) {
|
|
88
|
+
if (!authData) {
|
|
89
|
+
throw new Error('OptiID authentication required');
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const { customerId, instanceId, accessToken } = authData.credentials;
|
|
93
|
+
return {
|
|
94
|
+
id: '456',
|
|
95
|
+
title: params.title,
|
|
96
|
+
customerId,
|
|
97
|
+
instanceId
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Register an interaction
|
|
102
|
+
@interaction({
|
|
103
|
+
name: 'task_webhook',
|
|
104
|
+
endpoint: '/webhook/task'
|
|
105
|
+
})
|
|
106
|
+
async handleTaskWebhook(data: any): Promise<InteractionResult> {
|
|
107
|
+
return new InteractionResult(
|
|
108
|
+
`Task ${data.taskId} was updated`,
|
|
109
|
+
`https://app.example.com/tasks/${data.taskId}`
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Your function class inherits a `perform()` method from `ToolFunction` that serves as the main entry point for handling all incoming requests. When called, the SDK automatically:
|
|
116
|
+
|
|
117
|
+
- **Routes requests** to your registered tools and interactions based on endpoints
|
|
118
|
+
- **Handles authentication** and OptiID token validation before calling your methods
|
|
119
|
+
- **Provides discovery** at `/discovery` endpoint for OCP platform integration
|
|
120
|
+
- **Returns proper HTTP responses** with correct status codes and JSON formatting
|
|
121
|
+
|
|
122
|
+
## Core Concepts
|
|
123
|
+
|
|
124
|
+
### Tools
|
|
125
|
+
|
|
126
|
+
Tools are functions that can be discovered and executed through the OCP platform. They:
|
|
127
|
+
|
|
128
|
+
- Have a name, description, and endpoint
|
|
129
|
+
- Define parameters with types and validation
|
|
130
|
+
- Can require authentication
|
|
131
|
+
- Return structured responses
|
|
132
|
+
|
|
133
|
+
### Interactions
|
|
134
|
+
|
|
135
|
+
Interactions are event handlers that process incoming data (like webhooks):
|
|
136
|
+
|
|
137
|
+
- Have a name and endpoint
|
|
138
|
+
- Process unstructured data
|
|
139
|
+
- Return interaction results with messages and optional links
|
|
140
|
+
|
|
141
|
+
### Parameters
|
|
142
|
+
|
|
143
|
+
Supported parameter types:
|
|
144
|
+
|
|
145
|
+
```typescript
|
|
146
|
+
enum ParameterType {
|
|
147
|
+
String = 'string',
|
|
148
|
+
Integer = 'integer',
|
|
149
|
+
Number = 'number',
|
|
150
|
+
Boolean = 'boolean',
|
|
151
|
+
List = 'list',
|
|
152
|
+
Dictionary = 'object'
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Authentication
|
|
157
|
+
|
|
158
|
+
The SDK supports authentication and authorization mechanisms:
|
|
159
|
+
|
|
160
|
+
#### OptiID Authentication
|
|
161
|
+
|
|
162
|
+
OptiID provides user authentication with type safety. This is the only user authentication provider currently supported in Opal:
|
|
163
|
+
|
|
164
|
+
```typescript
|
|
165
|
+
interface AuthRequirementConfig {
|
|
166
|
+
provider: string; // 'OptiID'
|
|
167
|
+
scopeBundle: string; // e.g., 'calendar', 'tasks'
|
|
168
|
+
required?: boolean; // default: true
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
#### OptiID Token Authorization
|
|
173
|
+
|
|
174
|
+
The SDK automatically handles OptiID token validation for tool authorization. OptiID tokens provide both user authentication and authorization for tools, ensuring that only authenticated users with proper permissions can access your tools.
|
|
175
|
+
|
|
176
|
+
**Token Validation:**
|
|
177
|
+
- The SDK extracts and validates OptiID tokens from the request body
|
|
178
|
+
- Validation includes verifying that requests come from the same organization
|
|
179
|
+
- If validation fails, returns HTTP 403 Unauthorized before reaching your handler methods
|
|
180
|
+
- No additional configuration needed - validation is handled automatically
|
|
181
|
+
|
|
182
|
+
```typescript
|
|
183
|
+
export class MyToolFunction extends ToolFunction {
|
|
184
|
+
@tool({
|
|
185
|
+
name: 'secure_tool',
|
|
186
|
+
description: 'Tool that validates requests from Opal',
|
|
187
|
+
endpoint: '/secure-endpoint',
|
|
188
|
+
parameters: [
|
|
189
|
+
{ name: 'data', type: ParameterType.String, description: 'Data to process', required: true }
|
|
190
|
+
]
|
|
191
|
+
})
|
|
192
|
+
async secureToolHandler(
|
|
193
|
+
params: { data: string },
|
|
194
|
+
authData?: OptiIdAuthData
|
|
195
|
+
) {
|
|
196
|
+
|
|
197
|
+
// Process the request knowing it's from a trusted Opal instance
|
|
198
|
+
return {
|
|
199
|
+
status: 'success',
|
|
200
|
+
data: `Processed: ${params.data}`,
|
|
201
|
+
authorizedBy: 'Opal'
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
## API Reference
|
|
208
|
+
|
|
209
|
+
### Handler Function Signatures
|
|
210
|
+
|
|
211
|
+
All tool and interaction handler methods follow this signature pattern:
|
|
212
|
+
|
|
213
|
+
```typescript
|
|
214
|
+
async handlerMethod(
|
|
215
|
+
params: TParams, // Tool parameters or interaction data
|
|
216
|
+
authData?: OptiIdAuthData // OptiID user authentication data (if authenticated)
|
|
217
|
+
): Promise<TResult>
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
- **params**: The input parameters for tools, or interaction data for webhooks
|
|
221
|
+
- **authData**: Available when OptiID user authentication is configured and successful
|
|
222
|
+
|
|
223
|
+
### Decorators
|
|
224
|
+
|
|
225
|
+
#### `@tool(config: ToolConfig)`
|
|
226
|
+
|
|
227
|
+
Registers a method as a discoverable tool.
|
|
228
|
+
|
|
229
|
+
```typescript
|
|
230
|
+
interface ToolConfig {
|
|
231
|
+
name: string;
|
|
232
|
+
description: string;
|
|
233
|
+
parameters: ParameterConfig[];
|
|
234
|
+
authRequirements?: AuthRequirementConfig[];
|
|
235
|
+
endpoint: string;
|
|
236
|
+
}
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
#### `@interaction(config: InteractionConfig)`
|
|
240
|
+
|
|
241
|
+
Registers a method as an interaction handler.
|
|
242
|
+
|
|
243
|
+
```typescript
|
|
244
|
+
interface InteractionConfig {
|
|
245
|
+
name: string;
|
|
246
|
+
endpoint: string;
|
|
247
|
+
}
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
### Base Classes
|
|
251
|
+
|
|
252
|
+
#### `ToolFunction`
|
|
253
|
+
|
|
254
|
+
Abstract base class for OCP functions:
|
|
255
|
+
|
|
256
|
+
```typescript
|
|
257
|
+
export abstract class ToolFunction extends Function {
|
|
258
|
+
protected ready(): Promise<boolean>;
|
|
259
|
+
public async perform(): Promise<Response>;
|
|
260
|
+
}
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
Extend this class and implement your OCP function. The `perform` method automatically routes requests to registered tools.
|
|
264
|
+
|
|
265
|
+
### Models
|
|
266
|
+
|
|
267
|
+
Key model classes with generic type support:
|
|
268
|
+
|
|
269
|
+
- `Tool<TAuthData>` - Represents a registered tool with typed auth data
|
|
270
|
+
- `Interaction<TAuthData>` - Represents an interaction handler with typed auth data
|
|
271
|
+
- `Parameter` - Defines tool parameters
|
|
272
|
+
- `AuthRequirement` - Defines authentication needs
|
|
273
|
+
- `InteractionResult` - Response from interactions
|
|
274
|
+
- `OptiIdAuthData` - OptiID specific authentication data
|
|
275
|
+
|
|
276
|
+
## Discovery and Ready Endpoints
|
|
277
|
+
|
|
278
|
+
The SDK automatically provides two important endpoints:
|
|
279
|
+
|
|
280
|
+
### Discovery Endpoint (`/discovery`)
|
|
281
|
+
|
|
282
|
+
Returns all registered tools in the proper OCP format for platform integration:
|
|
283
|
+
|
|
284
|
+
```json
|
|
285
|
+
{
|
|
286
|
+
"functions": [
|
|
287
|
+
{
|
|
288
|
+
"name": "create_task",
|
|
289
|
+
"description": "Creates a new task in the system",
|
|
290
|
+
"parameters": [
|
|
291
|
+
{
|
|
292
|
+
"name": "title",
|
|
293
|
+
"type": "string",
|
|
294
|
+
"description": "The task title",
|
|
295
|
+
"required": true
|
|
296
|
+
},
|
|
297
|
+
{
|
|
298
|
+
"name": "priority",
|
|
299
|
+
"type": "string",
|
|
300
|
+
"description": "Task priority level",
|
|
301
|
+
"required": false
|
|
302
|
+
}
|
|
303
|
+
],
|
|
304
|
+
"endpoint": "/create-task",
|
|
305
|
+
"http_method": "POST"
|
|
306
|
+
},
|
|
307
|
+
{
|
|
308
|
+
"name": "secure_task",
|
|
309
|
+
"description": "Creates a secure task with OptiID authentication",
|
|
310
|
+
"parameters": [
|
|
311
|
+
{
|
|
312
|
+
"name": "title",
|
|
313
|
+
"type": "string",
|
|
314
|
+
"description": "The task title",
|
|
315
|
+
"required": true
|
|
316
|
+
}
|
|
317
|
+
],
|
|
318
|
+
"endpoint": "/secure-task",
|
|
319
|
+
"http_method": "POST",
|
|
320
|
+
"auth_requirements": [
|
|
321
|
+
{
|
|
322
|
+
"provider": "OptiID",
|
|
323
|
+
"scope_bundle": "tasks",
|
|
324
|
+
"required": true
|
|
325
|
+
}
|
|
326
|
+
]
|
|
327
|
+
}
|
|
328
|
+
]
|
|
329
|
+
}
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
### Ready Endpoint (`/ready`)
|
|
333
|
+
|
|
334
|
+
Returns the current readiness status of your function:
|
|
335
|
+
|
|
336
|
+
```json
|
|
337
|
+
{
|
|
338
|
+
"ready": true
|
|
339
|
+
}
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
This endpoint calls your function's `ready()` method and returns:
|
|
343
|
+
- `{ready: true}` when the function is ready to process requests
|
|
344
|
+
- `{ready: false}` when the function is not ready (missing configuration, external services unavailable, etc.)
|
|
345
|
+
- HTTP 200 status code regardless of ready state (the ready status is in the response body)
|
|
346
|
+
|
|
347
|
+
## Development
|
|
348
|
+
|
|
349
|
+
### Prerequisites
|
|
350
|
+
|
|
351
|
+
- Node.js >= 22.0.0
|
|
352
|
+
- TypeScript 5.x
|
|
353
|
+
|
|
354
|
+
### Building
|
|
355
|
+
|
|
356
|
+
```bash
|
|
357
|
+
yarn build
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
### Testing
|
|
361
|
+
|
|
362
|
+
```bash
|
|
363
|
+
# Run tests
|
|
364
|
+
yarn test
|
|
365
|
+
|
|
366
|
+
# Run tests in watch mode
|
|
367
|
+
yarn test:watch
|
|
368
|
+
|
|
369
|
+
# Run tests with coverage
|
|
370
|
+
yarn test:coverage
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
### Linting
|
|
374
|
+
|
|
375
|
+
```bash
|
|
376
|
+
yarn lint
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
## Examples
|
|
380
|
+
|
|
381
|
+
### Function with Authentication
|
|
382
|
+
|
|
383
|
+
```typescript
|
|
384
|
+
import { ToolFunction, tool, interaction, ParameterType, OptiIdAuthData, InteractionResult } from '@optimizely-opal/opal-ocp-sdk';
|
|
385
|
+
|
|
386
|
+
export class AuthenticatedFunction extends ToolFunction {
|
|
387
|
+
|
|
388
|
+
// OptiID authentication example
|
|
389
|
+
@tool({
|
|
390
|
+
name: 'secure_operation',
|
|
391
|
+
description: 'Performs a secure operation with OptiID',
|
|
392
|
+
endpoint: '/secure',
|
|
393
|
+
parameters: [],
|
|
394
|
+
authRequirements: [{ provider: 'OptiID', scopeBundle: 'tasks', required: true }]
|
|
395
|
+
})
|
|
396
|
+
async secureOperation(params: unknown, authData?: OptiIdAuthData) {
|
|
397
|
+
if (!authData) throw new Error('OptiID authentication required');
|
|
398
|
+
|
|
399
|
+
const { customerId, accessToken } = authData.credentials;
|
|
400
|
+
// Use OptiID credentials for API calls
|
|
401
|
+
return { success: true, customerId };
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// Interaction with authentication example
|
|
405
|
+
@interaction({
|
|
406
|
+
name: 'authenticated_webhook',
|
|
407
|
+
endpoint: '/secure-webhook'
|
|
408
|
+
})
|
|
409
|
+
async handleSecureWebhook(data: any, authData?: OptiIdAuthData): Promise<InteractionResult> {
|
|
410
|
+
if (!authData) {
|
|
411
|
+
return new InteractionResult('Authentication required for webhook processing');
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
const { customerId } = authData.credentials;
|
|
415
|
+
|
|
416
|
+
// Process webhook data with authentication context
|
|
417
|
+
return new InteractionResult(
|
|
418
|
+
`Webhook processed for customer ${customerId}: ${data.eventType}`,
|
|
419
|
+
`https://app.example.com/events/${data.eventId}`
|
|
420
|
+
);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
### Organizing Tools in Separate Files
|
|
426
|
+
|
|
427
|
+
For larger projects, you can organize your tools in separate files and import them into your main ToolFunction class:
|
|
428
|
+
|
|
429
|
+
**Project Structure:**
|
|
430
|
+
```
|
|
431
|
+
src/
|
|
432
|
+
โโโ tools/
|
|
433
|
+
โ โโโ index.ts
|
|
434
|
+
โ โโโ TaskTool.ts
|
|
435
|
+
โ โโโ NotificationTool.ts
|
|
436
|
+
โโโ MyToolFunction.ts
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
**tools/TaskTool.ts:**
|
|
440
|
+
```typescript
|
|
441
|
+
import { tool, ParameterType, OptiIdAuthData } from '@optimizely-opal/opal-ocp-sdk';
|
|
442
|
+
|
|
443
|
+
export class TaskTool {
|
|
444
|
+
@tool({
|
|
445
|
+
name: 'create_task',
|
|
446
|
+
description: 'Creates a new task in the system',
|
|
447
|
+
endpoint: '/create-task',
|
|
448
|
+
parameters: [
|
|
449
|
+
{
|
|
450
|
+
name: 'title',
|
|
451
|
+
type: ParameterType.String,
|
|
452
|
+
description: 'The task title',
|
|
453
|
+
required: true
|
|
454
|
+
},
|
|
455
|
+
{
|
|
456
|
+
name: 'priority',
|
|
457
|
+
type: ParameterType.String,
|
|
458
|
+
description: 'Task priority level',
|
|
459
|
+
required: false
|
|
460
|
+
}
|
|
461
|
+
]
|
|
462
|
+
})
|
|
463
|
+
async createTask(params: { title: string; priority?: string }, authData?: OptiIdAuthData) {
|
|
464
|
+
return {
|
|
465
|
+
id: '123',
|
|
466
|
+
title: params.title,
|
|
467
|
+
priority: params.priority || 'medium'
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
@tool({
|
|
472
|
+
name: 'delete_task',
|
|
473
|
+
description: 'Deletes a task from the system',
|
|
474
|
+
endpoint: '/delete-task',
|
|
475
|
+
parameters: [
|
|
476
|
+
{
|
|
477
|
+
name: 'taskId',
|
|
478
|
+
type: ParameterType.String,
|
|
479
|
+
description: 'The task ID to delete',
|
|
480
|
+
required: true
|
|
481
|
+
}
|
|
482
|
+
]
|
|
483
|
+
})
|
|
484
|
+
async deleteTask(params: { taskId: string }, authData?: OptiIdAuthData) {
|
|
485
|
+
return { success: true, deletedTaskId: params.taskId };
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
**tools/NotificationTool.ts:**
|
|
491
|
+
```typescript
|
|
492
|
+
import { tool, interaction, ParameterType, InteractionResult, OptiIdAuthData } from '@optimizely-opal/opal-ocp-sdk';
|
|
493
|
+
|
|
494
|
+
export class NotificationTool {
|
|
495
|
+
@tool({
|
|
496
|
+
name: 'send_notification',
|
|
497
|
+
description: 'Sends a notification to users',
|
|
498
|
+
endpoint: '/send-notification',
|
|
499
|
+
parameters: [
|
|
500
|
+
{
|
|
501
|
+
name: 'message',
|
|
502
|
+
type: ParameterType.String,
|
|
503
|
+
description: 'The notification message',
|
|
504
|
+
required: true
|
|
505
|
+
},
|
|
506
|
+
{
|
|
507
|
+
name: 'userId',
|
|
508
|
+
type: ParameterType.String,
|
|
509
|
+
description: 'Target user ID',
|
|
510
|
+
required: true
|
|
511
|
+
}
|
|
512
|
+
]
|
|
513
|
+
})
|
|
514
|
+
async sendNotification(params: { message: string; userId: string }, authData?: OptiIdAuthData) {
|
|
515
|
+
return {
|
|
516
|
+
notificationId: '456',
|
|
517
|
+
message: params.message,
|
|
518
|
+
userId: params.userId,
|
|
519
|
+
sent: true
|
|
520
|
+
};
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
@interaction({
|
|
524
|
+
name: 'notification_webhook',
|
|
525
|
+
endpoint: '/webhook/notification'
|
|
526
|
+
})
|
|
527
|
+
async handleNotificationWebhook(data: any): Promise<InteractionResult> {
|
|
528
|
+
return new InteractionResult(
|
|
529
|
+
`Notification ${data.notificationId} was delivered`,
|
|
530
|
+
`https://app.example.com/notifications/${data.notificationId}`
|
|
531
|
+
);
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
**tools/index.ts:**
|
|
537
|
+
```typescript
|
|
538
|
+
export * from './TaskTool';
|
|
539
|
+
export * from './NotificationTool';
|
|
540
|
+
```
|
|
541
|
+
|
|
542
|
+
**MyToolFunction.ts:**
|
|
543
|
+
```typescript
|
|
544
|
+
import { ToolFunction } from '@optimizely-opal/opal-ocp-sdk';
|
|
545
|
+
import * from './tools';
|
|
546
|
+
|
|
547
|
+
export class MyToolFunction extends ToolFunction {
|
|
548
|
+
}
|
|
549
|
+
```
|
|
550
|
+
|
|
551
|
+
This approach provides several benefits:
|
|
552
|
+
- **Better organization**: Each tool has its own file with related methods
|
|
553
|
+
- **Maintainability**: Easier to find and modify specific tools
|
|
554
|
+
- **Reusability**: Tools can be shared across different ToolFunction classes
|
|
555
|
+
- **Team collaboration**: Different developers can work on different tool files
|
|
556
|
+
- **Testing**: Each tool class can be unit tested independently
|
|
557
|
+
|
|
558
|
+
## Decorator Behavior and Instance Context
|
|
559
|
+
|
|
560
|
+
The `@tool` and `@interaction` decorators provide intelligent instance context management that behaves differently depending on where the decorated methods are defined:
|
|
561
|
+
|
|
562
|
+
### ToolFunction Subclass Context
|
|
563
|
+
|
|
564
|
+
When decorators are used in a class that extends `ToolFunction`, the decorators can reuse the existing ToolFunction instance when called through the `perform()` method:
|
|
565
|
+
|
|
566
|
+
```typescript
|
|
567
|
+
export class MyToolFunction extends ToolFunction {
|
|
568
|
+
private secretKey = process.env.SECRET_KEY;
|
|
569
|
+
|
|
570
|
+
@tool({
|
|
571
|
+
name: 'process_data',
|
|
572
|
+
description: 'Processes data using instance context',
|
|
573
|
+
endpoint: '/process-data',
|
|
574
|
+
parameters: [
|
|
575
|
+
{ name: 'data', type: ParameterType.String, description: 'Data to process', required: true }
|
|
576
|
+
]
|
|
577
|
+
})
|
|
578
|
+
async processData(params: { data: string }) {
|
|
579
|
+
// โ
Can access instance properties and methods
|
|
580
|
+
// โ
Can access this.request (inherited from ToolFunction)
|
|
581
|
+
// โ
Shares state with other methods in the same request
|
|
582
|
+
|
|
583
|
+
const userAgent = this.request.headers.get('user-agent');
|
|
584
|
+
return {
|
|
585
|
+
processedData: this.encryptData(params.data),
|
|
586
|
+
userAgent,
|
|
587
|
+
timestamp: Date.now()
|
|
588
|
+
};
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
private encryptData(data: string): string {
|
|
592
|
+
// Uses instance property
|
|
593
|
+
return `encrypted_${data}_${this.secretKey}`;
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
```
|
|
597
|
+
|
|
598
|
+
### Standalone Class Context
|
|
599
|
+
|
|
600
|
+
When decorators are used in classes that don't extend `ToolFunction`, the decorators create new instances for each handler call:
|
|
601
|
+
|
|
602
|
+
```typescript
|
|
603
|
+
export class StandaloneToolService {
|
|
604
|
+
private config = { apiKey: 'standalone-key' };
|
|
605
|
+
|
|
606
|
+
@tool({
|
|
607
|
+
name: 'standalone_operation',
|
|
608
|
+
description: 'Standalone operation without ToolFunction',
|
|
609
|
+
endpoint: '/standalone',
|
|
610
|
+
parameters: [
|
|
611
|
+
{ name: 'input', type: ParameterType.String, description: 'Input data', required: true }
|
|
612
|
+
]
|
|
613
|
+
})
|
|
614
|
+
async standaloneOperation(params: { input: string }) {
|
|
615
|
+
// โ
Can access instance properties and methods
|
|
616
|
+
// โ Cannot access this.request (not inherited from ToolFunction)
|
|
617
|
+
// โ No shared state with ToolFunction lifecycle
|
|
618
|
+
|
|
619
|
+
return {
|
|
620
|
+
result: `${this.helperMethod()}: ${params.input}`,
|
|
621
|
+
source: 'standalone'
|
|
622
|
+
};
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
private helperMethod() {
|
|
626
|
+
return this.config.apiKey;
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
```
|
|
630
|
+
|
|
631
|
+
This behavior ensures that your tools can be both flexible (working in any class) and powerful (leveraging ToolFunction features when available).
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { OptiIdAuthData } from '../types/Models';
|
|
2
|
+
/**
|
|
3
|
+
* Common authentication utilities for all function types
|
|
4
|
+
*/
|
|
5
|
+
export declare class AuthUtils {
|
|
6
|
+
/**
|
|
7
|
+
* Validate the OptiID access token
|
|
8
|
+
*
|
|
9
|
+
* @param accessToken - The access token to validate
|
|
10
|
+
* @returns true if the token is valid
|
|
11
|
+
*/
|
|
12
|
+
static validateAccessToken(accessToken: string | undefined): Promise<boolean>;
|
|
13
|
+
/**
|
|
14
|
+
* Extract and validate basic OptiID authentication data from request
|
|
15
|
+
*
|
|
16
|
+
* @param request - The incoming request
|
|
17
|
+
* @returns object with authData and accessToken, or null if invalid
|
|
18
|
+
*/
|
|
19
|
+
static extractAuthData(request: any): {
|
|
20
|
+
authData: OptiIdAuthData;
|
|
21
|
+
accessToken: string;
|
|
22
|
+
} | null;
|
|
23
|
+
/**
|
|
24
|
+
* Validate organization ID matches the app context
|
|
25
|
+
*
|
|
26
|
+
* @param customerId - The customer ID from the auth data
|
|
27
|
+
* @returns true if the organization ID is valid
|
|
28
|
+
*/
|
|
29
|
+
static validateOrganizationId(customerId: string | undefined): boolean;
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=AuthUtils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AuthUtils.d.ts","sourceRoot":"","sources":["../../src/auth/AuthUtils.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEjD;;GAEG;AACH,qBAAa,SAAS;IAEpB;;;;;OAKG;WACiB,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC;IAa1F;;;;;OAKG;WACW,eAAe,CAAC,OAAO,EAAE,GAAG,GAAG;QAAE,QAAQ,EAAE,cAAc,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;IAWrG;;;;;OAKG;WACW,sBAAsB,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO;CAc9E"}
|