@aloma.io/integration-sdk 3.8.53 → 3.8.55
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/build/openapi-to-connector.d.mts +4 -0
- package/build/openapi-to-connector.mjs +131 -18
- package/package.json +1 -1
- package/src/openapi-to-connector.mts +159 -20
- package/examples/api-without-servers.json +0 -32
- package/examples/companies-resource-class.mts +0 -310
- package/examples/companies-resource.mts +0 -310
- package/examples/complete-example.sh +0 -116
- package/examples/create-hubspot-connector.sh +0 -33
- package/examples/generate-connector.sh +0 -35
- package/examples/generated-controller.mts +0 -81
- package/examples/hubspot-companies.json +0 -1889
- package/examples/hubspot-contacts.json +0 -1919
- package/examples/hubspot-controller-individual-params.mts +0 -323
- package/examples/hubspot-controller-with-implementation.mts +0 -315
- package/examples/hubspot-controller.mts +0 -192
- package/examples/hubspot-lists.json +0 -5525
- package/examples/main-controller-with-resources.mts +0 -35
- package/examples/stripe.json +0 -182829
- package/examples/utility-click.json +0 -8992
@@ -32,6 +32,10 @@ export declare class OpenAPIToConnector {
|
|
32
32
|
* Generate method signature with options object
|
33
33
|
*/
|
34
34
|
private generateMethodSignature;
|
35
|
+
/**
|
36
|
+
* Get TypeScript type for a parameter based on its schema
|
37
|
+
*/
|
38
|
+
private getParameterType;
|
35
39
|
/**
|
36
40
|
* Generate method implementation code
|
37
41
|
*/
|
@@ -45,11 +45,21 @@ export class OpenAPIToConnector {
|
|
45
45
|
throw new Error(`Failed to parse OpenAPI spec: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
46
46
|
}
|
47
47
|
}
|
48
|
-
// Validate against OpenAPI 3.x schema
|
48
|
+
// Validate against OpenAPI 3.x schema with lenient validation
|
49
49
|
const validationResult = OpenAPISchema.safeParse(parsed);
|
50
50
|
if (!validationResult.success) {
|
51
|
-
|
52
|
-
|
51
|
+
// Check if the errors are just about missing 'type' fields in schemas
|
52
|
+
const criticalErrors = validationResult.error.errors.filter((err) => {
|
53
|
+
const path = err.path.join('.');
|
54
|
+
// Allow missing 'type' in schema definitions as many OpenAPI specs don't include it
|
55
|
+
return !path.includes('components.schemas') || !err.message.includes('Required');
|
56
|
+
});
|
57
|
+
if (criticalErrors.length > 0) {
|
58
|
+
const errors = criticalErrors.map((err) => `${err.path.join('.')}: ${err.message}`).join(', ');
|
59
|
+
throw new Error(`Invalid OpenAPI 3.x specification: ${errors}`);
|
60
|
+
}
|
61
|
+
// Log a warning about lenient validation
|
62
|
+
console.warn('⚠️ OpenAPI spec has some validation warnings but proceeding with lenient validation...');
|
53
63
|
}
|
54
64
|
return parsed;
|
55
65
|
}
|
@@ -176,7 +186,12 @@ export class OpenAPIToConnector {
|
|
176
186
|
else {
|
177
187
|
// Options object documentation
|
178
188
|
lines.push(' *');
|
179
|
-
|
189
|
+
// Check if there are any required parameters
|
190
|
+
const hasRequiredParams = pathParams.some(p => p.required) ||
|
191
|
+
queryParams.some(p => p.required) ||
|
192
|
+
(operation.requestBody && operation.requestBody.required);
|
193
|
+
const optionsRequired = hasRequiredParams ? '(required)' : '(optional)';
|
194
|
+
lines.push(` * @param {Object} options ${optionsRequired} - Request options`);
|
180
195
|
// Document path parameters
|
181
196
|
for (const param of pathParams) {
|
182
197
|
const paramType = param.schema?.type || 'string';
|
@@ -218,15 +233,20 @@ export class OpenAPIToConnector {
|
|
218
233
|
const pathParams = [];
|
219
234
|
const queryParams = [];
|
220
235
|
const hasBody = !!operation.requestBody;
|
221
|
-
// Identify path and query parameters
|
236
|
+
// Identify path and query parameters with their types and required status
|
222
237
|
if (operation.parameters) {
|
223
238
|
for (const param of operation.parameters) {
|
224
239
|
if (typeof param === 'object' && 'name' in param && 'in' in param) {
|
240
|
+
const paramInfo = {
|
241
|
+
name: param.name,
|
242
|
+
required: param.required || false,
|
243
|
+
type: this.getParameterType(param)
|
244
|
+
};
|
225
245
|
if (param.in === 'path') {
|
226
|
-
pathParams.push(
|
246
|
+
pathParams.push(paramInfo);
|
227
247
|
}
|
228
248
|
else if (param.in === 'query') {
|
229
|
-
queryParams.push(
|
249
|
+
queryParams.push(paramInfo);
|
230
250
|
}
|
231
251
|
}
|
232
252
|
}
|
@@ -234,21 +254,114 @@ export class OpenAPIToConnector {
|
|
234
254
|
// If there are no query params, no body, and only path params, use simple signature
|
235
255
|
if (queryParams.length === 0 && !hasBody && pathParams.length <= 1) {
|
236
256
|
const params = [];
|
237
|
-
for (const
|
238
|
-
params.push(`${
|
257
|
+
for (const paramInfo of pathParams) {
|
258
|
+
params.push(`${paramInfo.name}: ${paramInfo.type}`);
|
239
259
|
}
|
240
260
|
params.push(`options?: {headers?: {[key: string]: any}}`);
|
241
261
|
return `(${params.join(', ')})`;
|
242
262
|
}
|
243
|
-
//
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
263
|
+
// Check if there are any required parameters
|
264
|
+
const hasRequiredParams = pathParams.some(p => p.required) ||
|
265
|
+
queryParams.some(p => p.required) ||
|
266
|
+
(hasBody && operation.requestBody?.required);
|
267
|
+
// Build detailed options object with proper types
|
268
|
+
// Group nested properties into objects (e.g., PrimaryContact.FirstName -> PrimaryContact: {FirstName: string})
|
269
|
+
const nestedObjects = new Map();
|
270
|
+
const flatProps = [];
|
271
|
+
// Process all parameters (path + query)
|
272
|
+
const allParams = [...pathParams, ...queryParams];
|
273
|
+
for (const paramInfo of allParams) {
|
274
|
+
if (paramInfo.name.includes('.')) {
|
275
|
+
// This is a nested property like PrimaryContact.FirstName
|
276
|
+
const parts = paramInfo.name.split('.');
|
277
|
+
const objectName = parts[0];
|
278
|
+
const propertyName = parts.slice(1).join('.');
|
279
|
+
if (!nestedObjects.has(objectName)) {
|
280
|
+
nestedObjects.set(objectName, []);
|
281
|
+
}
|
282
|
+
nestedObjects.get(objectName).push({
|
283
|
+
name: propertyName,
|
284
|
+
type: paramInfo.type,
|
285
|
+
required: paramInfo.required
|
286
|
+
});
|
287
|
+
}
|
288
|
+
else {
|
289
|
+
// This is a flat property
|
290
|
+
flatProps.push({
|
291
|
+
name: paramInfo.name,
|
292
|
+
type: paramInfo.type,
|
293
|
+
required: paramInfo.required
|
294
|
+
});
|
295
|
+
}
|
296
|
+
}
|
297
|
+
// Build the options properties array
|
298
|
+
const optionProps = [];
|
299
|
+
// Add flat properties
|
300
|
+
for (const prop of flatProps) {
|
301
|
+
const optional = prop.required ? '' : '?';
|
302
|
+
optionProps.push(`${prop.name}${optional}: ${prop.type}`);
|
303
|
+
}
|
304
|
+
// Add nested objects
|
305
|
+
for (const [objectName, properties] of nestedObjects) {
|
306
|
+
const nestedProps = properties.map(p => {
|
307
|
+
const optional = p.required ? '' : '?';
|
308
|
+
return `${p.name}${optional}: ${p.type}`;
|
309
|
+
}).join(', ');
|
310
|
+
// Check if all properties are optional
|
311
|
+
const allOptional = properties.every(p => !p.required);
|
312
|
+
const optional = allOptional ? '?' : '';
|
313
|
+
optionProps.push(`${objectName}${optional}: {${nestedProps}}`);
|
314
|
+
}
|
315
|
+
// Add request body
|
316
|
+
if (hasBody) {
|
317
|
+
optionProps.push('body?: any');
|
318
|
+
}
|
319
|
+
// Add custom headers
|
320
|
+
optionProps.push('headers?: {[key: string]: any}');
|
321
|
+
// If there are too many parameters, use simplified signature to avoid parsing issues
|
322
|
+
// Also check if any parameter name is too long (over 100 chars) which can cause issues
|
323
|
+
const hasLongParamNames = optionProps.some(prop => prop.length > 100);
|
324
|
+
if (optionProps.length > 15 || hasLongParamNames) {
|
325
|
+
const required = hasRequiredParams ? '' : '?';
|
326
|
+
return `(options${required}: {[key: string]: any})`;
|
327
|
+
}
|
328
|
+
const required = hasRequiredParams ? '' : '?';
|
329
|
+
return `(options${required}: {${optionProps.join(', ')}})`;
|
330
|
+
}
|
331
|
+
/**
|
332
|
+
* Get TypeScript type for a parameter based on its schema
|
333
|
+
*/
|
334
|
+
getParameterType(param) {
|
335
|
+
if (param.schema) {
|
336
|
+
const schema = param.schema;
|
337
|
+
// Handle different schema types
|
338
|
+
if (schema.type) {
|
339
|
+
switch (schema.type) {
|
340
|
+
case 'string':
|
341
|
+
return 'string';
|
342
|
+
case 'integer':
|
343
|
+
case 'number':
|
344
|
+
return 'number';
|
345
|
+
case 'boolean':
|
346
|
+
return 'boolean';
|
347
|
+
case 'array':
|
348
|
+
return 'any[]';
|
349
|
+
case 'object':
|
350
|
+
return 'any';
|
351
|
+
default:
|
352
|
+
return 'any';
|
353
|
+
}
|
354
|
+
}
|
355
|
+
// Handle enum
|
356
|
+
if (schema.enum) {
|
357
|
+
return 'string';
|
358
|
+
}
|
359
|
+
// Handle $ref
|
360
|
+
if (schema.$ref) {
|
361
|
+
return 'any';
|
362
|
+
}
|
363
|
+
}
|
364
|
+
return 'any';
|
252
365
|
}
|
253
366
|
/**
|
254
367
|
* Generate method implementation code
|
package/package.json
CHANGED
@@ -65,11 +65,23 @@ export class OpenAPIToConnector {
|
|
65
65
|
}
|
66
66
|
}
|
67
67
|
|
68
|
-
// Validate against OpenAPI 3.x schema
|
68
|
+
// Validate against OpenAPI 3.x schema with lenient validation
|
69
69
|
const validationResult = OpenAPISchema.safeParse(parsed);
|
70
70
|
if (!validationResult.success) {
|
71
|
-
|
72
|
-
|
71
|
+
// Check if the errors are just about missing 'type' fields in schemas
|
72
|
+
const criticalErrors = validationResult.error.errors.filter((err) => {
|
73
|
+
const path = err.path.join('.');
|
74
|
+
// Allow missing 'type' in schema definitions as many OpenAPI specs don't include it
|
75
|
+
return !path.includes('components.schemas') || !err.message.includes('Required');
|
76
|
+
});
|
77
|
+
|
78
|
+
if (criticalErrors.length > 0) {
|
79
|
+
const errors = criticalErrors.map((err) => `${err.path.join('.')}: ${err.message}`).join(', ');
|
80
|
+
throw new Error(`Invalid OpenAPI 3.x specification: ${errors}`);
|
81
|
+
}
|
82
|
+
|
83
|
+
// Log a warning about lenient validation
|
84
|
+
console.warn('⚠️ OpenAPI spec has some validation warnings but proceeding with lenient validation...');
|
73
85
|
}
|
74
86
|
|
75
87
|
return parsed as OpenAPIV3.Document;
|
@@ -213,7 +225,15 @@ export class OpenAPIToConnector {
|
|
213
225
|
} else {
|
214
226
|
// Options object documentation
|
215
227
|
lines.push(' *');
|
216
|
-
|
228
|
+
|
229
|
+
// Check if there are any required parameters
|
230
|
+
const hasRequiredParams =
|
231
|
+
pathParams.some((p) => p.required) ||
|
232
|
+
queryParams.some((p) => p.required) ||
|
233
|
+
(operation.requestBody && operation.requestBody.required);
|
234
|
+
|
235
|
+
const optionsRequired = hasRequiredParams ? '(required)' : '(optional)';
|
236
|
+
lines.push(` * @param {Object} options ${optionsRequired} - Request options`);
|
217
237
|
|
218
238
|
// Document path parameters
|
219
239
|
for (const param of pathParams) {
|
@@ -260,18 +280,24 @@ export class OpenAPIToConnector {
|
|
260
280
|
* Generate method signature with options object
|
261
281
|
*/
|
262
282
|
private generateMethodSignature(operation: OperationInfo): string {
|
263
|
-
const pathParams: string
|
264
|
-
const queryParams: string
|
283
|
+
const pathParams: Array<{name: string; required: boolean; type: string}> = [];
|
284
|
+
const queryParams: Array<{name: string; required: boolean; type: string}> = [];
|
265
285
|
const hasBody = !!operation.requestBody;
|
266
286
|
|
267
|
-
// Identify path and query parameters
|
287
|
+
// Identify path and query parameters with their types and required status
|
268
288
|
if (operation.parameters) {
|
269
289
|
for (const param of operation.parameters) {
|
270
290
|
if (typeof param === 'object' && 'name' in param && 'in' in param) {
|
291
|
+
const paramInfo = {
|
292
|
+
name: param.name,
|
293
|
+
required: param.required || false,
|
294
|
+
type: this.getParameterType(param),
|
295
|
+
};
|
296
|
+
|
271
297
|
if (param.in === 'path') {
|
272
|
-
pathParams.push(
|
298
|
+
pathParams.push(paramInfo);
|
273
299
|
} else if (param.in === 'query') {
|
274
|
-
queryParams.push(
|
300
|
+
queryParams.push(paramInfo);
|
275
301
|
}
|
276
302
|
}
|
277
303
|
}
|
@@ -280,22 +306,135 @@ export class OpenAPIToConnector {
|
|
280
306
|
// If there are no query params, no body, and only path params, use simple signature
|
281
307
|
if (queryParams.length === 0 && !hasBody && pathParams.length <= 1) {
|
282
308
|
const params: string[] = [];
|
283
|
-
for (const
|
284
|
-
params.push(`${
|
309
|
+
for (const paramInfo of pathParams) {
|
310
|
+
params.push(`${paramInfo.name}: ${paramInfo.type}`);
|
285
311
|
}
|
286
312
|
params.push(`options?: {headers?: {[key: string]: any}}`);
|
287
313
|
return `(${params.join(', ')})`;
|
288
314
|
}
|
289
315
|
|
290
|
-
//
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
hasBody
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
316
|
+
// Check if there are any required parameters
|
317
|
+
const hasRequiredParams =
|
318
|
+
pathParams.some((p) => p.required) ||
|
319
|
+
queryParams.some((p) => p.required) ||
|
320
|
+
(hasBody && operation.requestBody?.required);
|
321
|
+
|
322
|
+
// Build detailed options object with proper types
|
323
|
+
// Group nested properties into objects (e.g., PrimaryContact.FirstName -> PrimaryContact: {FirstName: string})
|
324
|
+
const nestedObjects: Map<string, Array<{name: string; type: string; required: boolean}>> = new Map();
|
325
|
+
const flatProps: Array<{name: string; type: string; required: boolean}> = [];
|
326
|
+
|
327
|
+
// Process all parameters (path + query)
|
328
|
+
const allParams = [...pathParams, ...queryParams];
|
329
|
+
|
330
|
+
for (const paramInfo of allParams) {
|
331
|
+
if (paramInfo.name.includes('.')) {
|
332
|
+
// This is a nested property like PrimaryContact.FirstName
|
333
|
+
const parts = paramInfo.name.split('.');
|
334
|
+
const objectName = parts[0];
|
335
|
+
const propertyName = parts.slice(1).join('.');
|
336
|
+
|
337
|
+
if (!nestedObjects.has(objectName)) {
|
338
|
+
nestedObjects.set(objectName, []);
|
339
|
+
}
|
340
|
+
nestedObjects.get(objectName)!.push({
|
341
|
+
name: propertyName,
|
342
|
+
type: paramInfo.type,
|
343
|
+
required: paramInfo.required,
|
344
|
+
});
|
345
|
+
} else {
|
346
|
+
// This is a flat property
|
347
|
+
flatProps.push({
|
348
|
+
name: paramInfo.name,
|
349
|
+
type: paramInfo.type,
|
350
|
+
required: paramInfo.required,
|
351
|
+
});
|
352
|
+
}
|
353
|
+
}
|
354
|
+
|
355
|
+
// Build the options properties array
|
356
|
+
const optionProps: string[] = [];
|
357
|
+
|
358
|
+
// Add flat properties
|
359
|
+
for (const prop of flatProps) {
|
360
|
+
const optional = prop.required ? '' : '?';
|
361
|
+
optionProps.push(`${prop.name}${optional}: ${prop.type}`);
|
362
|
+
}
|
363
|
+
|
364
|
+
// Add nested objects
|
365
|
+
for (const [objectName, properties] of nestedObjects) {
|
366
|
+
const nestedProps = properties
|
367
|
+
.map((p) => {
|
368
|
+
const optional = p.required ? '' : '?';
|
369
|
+
return `${p.name}${optional}: ${p.type}`;
|
370
|
+
})
|
371
|
+
.join(', ');
|
372
|
+
|
373
|
+
// Check if all properties are optional
|
374
|
+
const allOptional = properties.every((p) => !p.required);
|
375
|
+
const optional = allOptional ? '?' : '';
|
376
|
+
|
377
|
+
optionProps.push(`${objectName}${optional}: {${nestedProps}}`);
|
378
|
+
}
|
379
|
+
|
380
|
+
// Add request body
|
381
|
+
if (hasBody) {
|
382
|
+
optionProps.push('body?: any');
|
383
|
+
}
|
384
|
+
|
385
|
+
// Add custom headers
|
386
|
+
optionProps.push('headers?: {[key: string]: any}');
|
387
|
+
|
388
|
+
// If there are too many parameters, use simplified signature to avoid parsing issues
|
389
|
+
// Also check if any parameter name is too long (over 100 chars) which can cause issues
|
390
|
+
const hasLongParamNames = optionProps.some((prop) => prop.length > 100);
|
391
|
+
if (optionProps.length > 15 || hasLongParamNames) {
|
392
|
+
const required = hasRequiredParams ? '' : '?';
|
393
|
+
return `(options${required}: {[key: string]: any})`;
|
394
|
+
}
|
395
|
+
|
396
|
+
const required = hasRequiredParams ? '' : '?';
|
397
|
+
return `(options${required}: {${optionProps.join(', ')}})`;
|
398
|
+
}
|
399
|
+
|
400
|
+
/**
|
401
|
+
* Get TypeScript type for a parameter based on its schema
|
402
|
+
*/
|
403
|
+
private getParameterType(param: any): string {
|
404
|
+
if (param.schema) {
|
405
|
+
const schema = param.schema;
|
406
|
+
|
407
|
+
// Handle different schema types
|
408
|
+
if (schema.type) {
|
409
|
+
switch (schema.type) {
|
410
|
+
case 'string':
|
411
|
+
return 'string';
|
412
|
+
case 'integer':
|
413
|
+
case 'number':
|
414
|
+
return 'number';
|
415
|
+
case 'boolean':
|
416
|
+
return 'boolean';
|
417
|
+
case 'array':
|
418
|
+
return 'any[]';
|
419
|
+
case 'object':
|
420
|
+
return 'any';
|
421
|
+
default:
|
422
|
+
return 'any';
|
423
|
+
}
|
424
|
+
}
|
425
|
+
|
426
|
+
// Handle enum
|
427
|
+
if (schema.enum) {
|
428
|
+
return 'string';
|
429
|
+
}
|
430
|
+
|
431
|
+
// Handle $ref
|
432
|
+
if (schema.$ref) {
|
433
|
+
return 'any';
|
434
|
+
}
|
435
|
+
}
|
436
|
+
|
437
|
+
return 'any';
|
299
438
|
}
|
300
439
|
|
301
440
|
/**
|
@@ -1,32 +0,0 @@
|
|
1
|
-
{
|
2
|
-
"openapi": "3.0.0",
|
3
|
-
"info": {
|
4
|
-
"title": "Test API",
|
5
|
-
"version": "1.0.0",
|
6
|
-
"description": "API without servers definition"
|
7
|
-
},
|
8
|
-
"paths": {
|
9
|
-
"/users": {
|
10
|
-
"get": {
|
11
|
-
"summary": "List users",
|
12
|
-
"operationId": "listUsers",
|
13
|
-
"parameters": [
|
14
|
-
{
|
15
|
-
"name": "limit",
|
16
|
-
"in": "query",
|
17
|
-
"schema": {
|
18
|
-
"type": "integer"
|
19
|
-
},
|
20
|
-
"description": "Maximum number of users"
|
21
|
-
}
|
22
|
-
],
|
23
|
-
"responses": {
|
24
|
-
"200": {
|
25
|
-
"description": "Success"
|
26
|
-
}
|
27
|
-
}
|
28
|
-
}
|
29
|
-
}
|
30
|
-
}
|
31
|
-
}
|
32
|
-
|