@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.
@@ -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
- const errors = validationResult.error.errors.map((err) => `${err.path.join('.')}: ${err.message}`).join(', ');
52
- throw new Error(`Invalid OpenAPI 3.x specification: ${errors}`);
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
- lines.push(` * @param {Object} options (optional) - Request options`);
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(param.name);
246
+ pathParams.push(paramInfo);
227
247
  }
228
248
  else if (param.in === 'query') {
229
- queryParams.push(param.name);
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 paramName of pathParams) {
238
- params.push(`${paramName}: string`);
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
- // Otherwise, use options object pattern
244
- return `(options?: {${[
245
- ...pathParams.map((p) => `${p}?: string`),
246
- ...queryParams.map((p) => `${p}?: any`),
247
- hasBody ? 'body?: any' : '',
248
- 'headers?: {[key: string]: any}',
249
- ]
250
- .filter(Boolean)
251
- .join(', ')}})`;
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aloma.io/integration-sdk",
3
- "version": "3.8.53",
3
+ "version": "3.8.55",
4
4
  "description": "",
5
5
  "author": "aloma.io",
6
6
  "license": "Apache-2.0",
@@ -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
- const errors = validationResult.error.errors.map((err) => `${err.path.join('.')}: ${err.message}`).join(', ');
72
- throw new Error(`Invalid OpenAPI 3.x specification: ${errors}`);
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
- lines.push(` * @param {Object} options (optional) - Request options`);
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(param.name);
298
+ pathParams.push(paramInfo);
273
299
  } else if (param.in === 'query') {
274
- queryParams.push(param.name);
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 paramName of pathParams) {
284
- params.push(`${paramName}: string`);
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
- // Otherwise, use options object pattern
291
- return `(options?: {${[
292
- ...pathParams.map((p) => `${p}?: string`),
293
- ...queryParams.map((p) => `${p}?: any`),
294
- hasBody ? 'body?: any' : '',
295
- 'headers?: {[key: string]: any}',
296
- ]
297
- .filter(Boolean)
298
- .join(', ')}})`;
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
-