@optimizely-opal/opal-tool-ocp-sdk 0.0.0-dev.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 +437 -0
- package/dist/decorator/Decorator.d.ts +46 -0
- package/dist/decorator/Decorator.d.ts.map +1 -0
- package/dist/decorator/Decorator.js +31 -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 +418 -0
- package/dist/decorator/Decorator.test.js.map +1 -0
- package/dist/function/ToolFunction.d.ts +15 -0
- package/dist/function/ToolFunction.d.ts.map +1 -0
- package/dist/function/ToolFunction.js +25 -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 +189 -0
- package/dist/function/ToolFunction.test.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +25 -0
- package/dist/index.js.map +1 -0
- package/dist/service/Service.d.ts +78 -0
- package/dist/service/Service.d.ts.map +1 -0
- package/dist/service/Service.js +204 -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 +341 -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 +58 -0
- package/src/decorator/Decorator.test.ts +523 -0
- package/src/decorator/Decorator.ts +83 -0
- package/src/function/ToolFunction.test.ts +224 -0
- package/src/function/ToolFunction.ts +25 -0
- package/src/index.ts +4 -0
- package/src/service/Service.test.ts +550 -0
- package/src/service/Service.ts +182 -0
- package/src/types/Models.ts +163 -0
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/* eslint-disable max-classes-per-file */
|
|
2
|
+
import { AuthRequirement, Parameter } from '../types/Models';
|
|
3
|
+
import * as App from '@zaiusinc/app-sdk';
|
|
4
|
+
import { logger } from '@zaiusinc/app-sdk';
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Result type for interaction handlers
|
|
10
|
+
*/
|
|
11
|
+
export class InteractionResult {
|
|
12
|
+
public constructor(
|
|
13
|
+
public message: string,
|
|
14
|
+
public link?: string
|
|
15
|
+
) {}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Interaction definition for an Opal interaction
|
|
20
|
+
*/
|
|
21
|
+
export class Interaction<TAuthData> {
|
|
22
|
+
public constructor(
|
|
23
|
+
public name: string,
|
|
24
|
+
public endpoint: string,
|
|
25
|
+
public handler: (data: unknown, authData?: TAuthData) => Promise<InteractionResult>
|
|
26
|
+
) {}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Tool definition for an Opal tool
|
|
31
|
+
*/
|
|
32
|
+
export class Tool<TAuthData> {
|
|
33
|
+
/**
|
|
34
|
+
* HTTP method for the endpoint (default: POST)
|
|
35
|
+
*/
|
|
36
|
+
public httpMethod: string = 'POST';
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Create a new function definition
|
|
40
|
+
* @param name Function name
|
|
41
|
+
* @param description Function description
|
|
42
|
+
* @param parameters Function parameters
|
|
43
|
+
* @param endpoint API endpoint
|
|
44
|
+
* @param handler Function implementing the tool
|
|
45
|
+
* @param authRequirements Authentication requirements (optional)
|
|
46
|
+
*/
|
|
47
|
+
public constructor(
|
|
48
|
+
public name: string,
|
|
49
|
+
public description: string,
|
|
50
|
+
public parameters: Parameter[],
|
|
51
|
+
public endpoint: string,
|
|
52
|
+
public handler: (params: unknown, authData?: TAuthData) => Promise<unknown>,
|
|
53
|
+
public authRequirements?: AuthRequirement[]
|
|
54
|
+
) {}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Convert to JSON for the discovery endpoint
|
|
58
|
+
*/
|
|
59
|
+
public toJSON(): Record<string, unknown> {
|
|
60
|
+
const result: Record<string, unknown> = {
|
|
61
|
+
name: this.name,
|
|
62
|
+
description: this.description,
|
|
63
|
+
parameters: this.parameters.map((p) => p.toJSON()),
|
|
64
|
+
endpoint: this.endpoint,
|
|
65
|
+
http_method: this.httpMethod
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
if (this.authRequirements && this.authRequirements.length > 0) {
|
|
69
|
+
result.auth_requirements = this.authRequirements.map((auth) => auth.toJSON());
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return result;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export class ToolsService {
|
|
77
|
+
private functions: Map<string, Tool<any>> = new Map();
|
|
78
|
+
private interactions: Map<string, Interaction<any>> = new Map();
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Extract Bearer token from Authorization header
|
|
82
|
+
* @param headers Request headers (Map-like object or Headers object with get method)
|
|
83
|
+
* @returns Bearer token string or undefined
|
|
84
|
+
*/
|
|
85
|
+
public extractBearerToken(headers: App.Headers): string | undefined {
|
|
86
|
+
let bearerToken: string | undefined;
|
|
87
|
+
|
|
88
|
+
const authHeader = headers ? headers.get('authorization') : undefined;
|
|
89
|
+
if (authHeader && authHeader.startsWith('Bearer ')) {
|
|
90
|
+
bearerToken = authHeader.substring(7).trim();
|
|
91
|
+
}
|
|
92
|
+
return bearerToken;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Register a tool function with generic auth data
|
|
97
|
+
* @param name Tool name
|
|
98
|
+
* @param description Tool description
|
|
99
|
+
* @param handler Function implementing the tool
|
|
100
|
+
* @param parameters List of parameters for the tool
|
|
101
|
+
* @param endpoint API endpoint for the tool
|
|
102
|
+
* @param authRequirements Authentication requirements (optional)
|
|
103
|
+
*/
|
|
104
|
+
public registerTool<TAuthData>(
|
|
105
|
+
name: string,
|
|
106
|
+
description: string,
|
|
107
|
+
handler: (params: unknown, authData?: TAuthData) => Promise<unknown>,
|
|
108
|
+
parameters: Parameter[],
|
|
109
|
+
endpoint: string,
|
|
110
|
+
authRequirements?: AuthRequirement[]
|
|
111
|
+
): void {
|
|
112
|
+
const func = new Tool<TAuthData>(name, description, parameters, endpoint, handler, authRequirements);
|
|
113
|
+
this.functions.set(endpoint, func);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Register a tool interaction with generic auth data
|
|
118
|
+
* @param name Tool name
|
|
119
|
+
* @param handler Function implementing the tool
|
|
120
|
+
* @param endpoint API endpoint for the tool
|
|
121
|
+
*/
|
|
122
|
+
public registerInteraction<TAuthData>(
|
|
123
|
+
name: string,
|
|
124
|
+
handler: (data: unknown, authData?: TAuthData) => Promise<InteractionResult>,
|
|
125
|
+
endpoint: string
|
|
126
|
+
): void {
|
|
127
|
+
const func = new Interaction<TAuthData>(name, endpoint, handler);
|
|
128
|
+
this.interactions.set(endpoint, func);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
public async processRequest(req: App.Request): Promise<App.Response> {
|
|
132
|
+
if (req.path === '/discovery') {
|
|
133
|
+
return new App.Response(200, { functions: Array.from(this.functions.values()).map((f) => f.toJSON()) });
|
|
134
|
+
} else {
|
|
135
|
+
const func = this.functions.get(req.path);
|
|
136
|
+
if (func) {
|
|
137
|
+
try {
|
|
138
|
+
let params;
|
|
139
|
+
if (req.bodyJSON && req.bodyJSON.parameters) {
|
|
140
|
+
params = req.bodyJSON.parameters;
|
|
141
|
+
} else {
|
|
142
|
+
params = req.bodyJSON;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Extract auth data from body JSON
|
|
146
|
+
const authData = req.bodyJSON ? req.bodyJSON.auth : undefined;
|
|
147
|
+
|
|
148
|
+
const result = await func.handler(params, authData);
|
|
149
|
+
return new App.Response(200, result);
|
|
150
|
+
} catch (error: any) {
|
|
151
|
+
logger.error(`Error in function ${func.name}:`, error);
|
|
152
|
+
return new App.Response(500, { error: error.message || 'Unknown error' });
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const interaction = this.interactions.get(req.path);
|
|
157
|
+
if (interaction) {
|
|
158
|
+
try {
|
|
159
|
+
let params;
|
|
160
|
+
if (req.bodyJSON && req.bodyJSON.data) {
|
|
161
|
+
params = req.bodyJSON.data;
|
|
162
|
+
} else {
|
|
163
|
+
params = req.bodyJSON;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Extract auth data from body JSON
|
|
167
|
+
const authData = req.bodyJSON ? req.bodyJSON.auth : undefined;
|
|
168
|
+
|
|
169
|
+
const result = await interaction.handler(params, authData);
|
|
170
|
+
return new App.Response(200, result);
|
|
171
|
+
} catch (error: any) {
|
|
172
|
+
logger.error(`Error in function ${interaction.name}:`, error);
|
|
173
|
+
return new App.Response(500, { error: error.message || 'Unknown error' });
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return new App.Response(404, 'Function not found');
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export const toolsService = new ToolsService();
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/* eslint-disable max-classes-per-file */
|
|
2
|
+
/**
|
|
3
|
+
* Types of parameters supported by Opal tools
|
|
4
|
+
*/
|
|
5
|
+
export enum ParameterType {
|
|
6
|
+
String = 'string',
|
|
7
|
+
Integer = 'integer',
|
|
8
|
+
Number = 'number',
|
|
9
|
+
Boolean = 'boolean',
|
|
10
|
+
List = 'list',
|
|
11
|
+
Dictionary = 'object'
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Parameter definition for an Opal tool
|
|
16
|
+
*/
|
|
17
|
+
export class Parameter {
|
|
18
|
+
/**
|
|
19
|
+
* Create a new parameter definition
|
|
20
|
+
* @param name Parameter name
|
|
21
|
+
* @param type Parameter type
|
|
22
|
+
* @param description Parameter description
|
|
23
|
+
* @param required Whether the parameter is required
|
|
24
|
+
*/
|
|
25
|
+
public constructor(
|
|
26
|
+
public name: string,
|
|
27
|
+
public type: ParameterType,
|
|
28
|
+
public description: string,
|
|
29
|
+
public required: boolean
|
|
30
|
+
) {}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Convert to JSON for the discovery endpoint
|
|
34
|
+
*/
|
|
35
|
+
public toJSON() {
|
|
36
|
+
return {
|
|
37
|
+
name: this.name,
|
|
38
|
+
type: this.type,
|
|
39
|
+
description: this.description,
|
|
40
|
+
required: this.required
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Credentials structure
|
|
47
|
+
*/
|
|
48
|
+
export class OptiIdAuthDataCredentials {
|
|
49
|
+
|
|
50
|
+
public constructor(
|
|
51
|
+
public customerId: string,
|
|
52
|
+
public instanceId: string,
|
|
53
|
+
public accessToken: string,
|
|
54
|
+
public productSku: string
|
|
55
|
+
) {}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Auth data structure
|
|
61
|
+
*/
|
|
62
|
+
export class OptiIdAuthData {
|
|
63
|
+
|
|
64
|
+
public constructor(
|
|
65
|
+
public provider: string,
|
|
66
|
+
public credentials: OptiIdAuthDataCredentials
|
|
67
|
+
) {}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Authentication requirements for an Opal tool
|
|
72
|
+
*/
|
|
73
|
+
export class AuthRequirement {
|
|
74
|
+
/**
|
|
75
|
+
* Create a new authentication requirement
|
|
76
|
+
* @param provider Auth provider (e.g., "optiId")
|
|
77
|
+
* @param scopeBundle Scope bundle (e.g., "calendar", "drive")
|
|
78
|
+
* @param required Whether authentication is required
|
|
79
|
+
*/
|
|
80
|
+
public constructor(
|
|
81
|
+
public provider: string,
|
|
82
|
+
public scopeBundle: string,
|
|
83
|
+
public required: boolean = true
|
|
84
|
+
) {}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Convert to JSON for the discovery endpoint
|
|
88
|
+
*/
|
|
89
|
+
public toJSON() {
|
|
90
|
+
return {
|
|
91
|
+
provider: this.provider,
|
|
92
|
+
scope_bundle: this.scopeBundle,
|
|
93
|
+
required: this.required
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export class IslandField {
|
|
99
|
+
|
|
100
|
+
public constructor(
|
|
101
|
+
public name: string,
|
|
102
|
+
public label: string,
|
|
103
|
+
public type: 'string' | 'boolean' | 'json',
|
|
104
|
+
public value: string | boolean | object = '',
|
|
105
|
+
public hidden: boolean = false,
|
|
106
|
+
public options: string[] = []
|
|
107
|
+
) {}
|
|
108
|
+
|
|
109
|
+
public toJSON() {
|
|
110
|
+
return {
|
|
111
|
+
name: this.name,
|
|
112
|
+
label: this.label,
|
|
113
|
+
type: this.type,
|
|
114
|
+
hidden: this.hidden,
|
|
115
|
+
options: this.options,
|
|
116
|
+
value: this.value
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export class IslandAction {
|
|
122
|
+
|
|
123
|
+
public constructor(
|
|
124
|
+
public name: string,
|
|
125
|
+
public label: string,
|
|
126
|
+
public type: string,
|
|
127
|
+
public endpoint: string,
|
|
128
|
+
public operation: string = 'create'
|
|
129
|
+
) {}
|
|
130
|
+
|
|
131
|
+
public toJSON() {
|
|
132
|
+
return {
|
|
133
|
+
name: this.name,
|
|
134
|
+
label: this.label,
|
|
135
|
+
type: this.type,
|
|
136
|
+
endpoint: this.endpoint,
|
|
137
|
+
operation: this.operation
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export class IslandConfig {
|
|
143
|
+
|
|
144
|
+
public constructor(
|
|
145
|
+
public fields: IslandField[],
|
|
146
|
+
public actions: IslandAction[]
|
|
147
|
+
) {}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export class IslandResponse {
|
|
151
|
+
|
|
152
|
+
public constructor(
|
|
153
|
+
public type: 'island',
|
|
154
|
+
public config: {
|
|
155
|
+
islands: IslandConfig[];
|
|
156
|
+
}
|
|
157
|
+
) {}
|
|
158
|
+
|
|
159
|
+
public static create(islands: IslandConfig[]): IslandResponse {
|
|
160
|
+
return new IslandResponse('island', { islands });
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|