@aloma.io/integration-sdk 3.8.54 → 3.8.56

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.
Files changed (35) hide show
  1. package/MULTI_RESOURCE_GUIDE.md +24 -21
  2. package/OPENAPI_TO_CONNECTOR.md +146 -16
  3. package/README.md +62 -10
  4. package/build/cli.mjs +122 -33
  5. package/build/openapi-to-connector.d.mts +92 -11
  6. package/build/openapi-to-connector.mjs +968 -168
  7. package/package.json +3 -1
  8. package/src/cli.mts +140 -37
  9. package/src/openapi-to-connector.mts +1092 -176
  10. package/test/scenarios/README.md +148 -0
  11. package/test/scenarios/complex/expected/controller.mts +271 -0
  12. package/test/scenarios/complex/expected/orders-resource.mts +264 -0
  13. package/test/scenarios/complex/expected/products-resource.mts +239 -0
  14. package/test/scenarios/complex/specs/orders.json +362 -0
  15. package/test/scenarios/complex/specs/products.json +308 -0
  16. package/test/scenarios/simple/expected-controller.mts +60 -0
  17. package/test/scenarios/simple/simple-api.json +39 -0
  18. package/test/scenarios.test.mts +286 -0
  19. package/test/verify-scenarios.mjs +298 -0
  20. package/examples/api-without-servers.json +0 -32
  21. package/examples/companies-resource-class.mts +0 -310
  22. package/examples/companies-resource.mts +0 -310
  23. package/examples/complete-example.sh +0 -116
  24. package/examples/create-hubspot-connector.sh +0 -33
  25. package/examples/generate-connector.sh +0 -35
  26. package/examples/generated-controller.mts +0 -81
  27. package/examples/hubspot-companies.json +0 -1889
  28. package/examples/hubspot-contacts.json +0 -1919
  29. package/examples/hubspot-controller-individual-params.mts +0 -323
  30. package/examples/hubspot-controller-with-implementation.mts +0 -315
  31. package/examples/hubspot-controller.mts +0 -192
  32. package/examples/hubspot-lists.json +0 -5525
  33. package/examples/main-controller-with-resources.mts +0 -35
  34. package/examples/stripe.json +0 -182829
  35. package/examples/utility-click.json +0 -8992
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aloma.io/integration-sdk",
3
- "version": "3.8.54",
3
+ "version": "3.8.56",
4
4
  "description": "",
5
5
  "author": "aloma.io",
6
6
  "license": "Apache-2.0",
@@ -12,6 +12,8 @@
12
12
  "dev": "./node_modules/typescript/bin/tsc --watch",
13
13
  "build": "./node_modules/typescript/bin/tsc",
14
14
  "test": "./node_modules/mocha/bin/_mocha --recursive test/ --loader ts-node/esm",
15
+ "test:scenarios": "node test/verify-scenarios.mjs",
16
+ "test:all": "npm run test:scenarios",
15
17
  "format": "yarn prettier --write src/",
16
18
  "openapi-to-connector": "node ./build/openapi-to-connector.mjs"
17
19
  },
package/src/cli.mts CHANGED
@@ -144,23 +144,17 @@ program
144
144
  'Generate as a resource class with the specified class name (e.g., CompaniesResource)'
145
145
  )
146
146
  .option('--multi-resource', 'Generate multiple resource files + main controller (requires multiple --spec files)')
147
+ .option('--controller-only', 'Generate only the controller file, do not create full project structure')
147
148
  .option('--no-build', 'Skip installing dependencies and building the project')
148
149
  .action(async (name, options) => {
149
150
  name = name.replace(/[\/\.]/gi, '');
150
151
  if (!name) throw new Error('name is empty');
151
152
 
152
- const target = `${process.cwd()}/${name}`;
153
-
154
153
  try {
155
154
  // Read and parse the OpenAPI spec
156
155
  const specContent = fs.readFileSync(options.spec, 'utf-8');
157
156
  const spec = OpenAPIToConnector.parseSpec(specContent);
158
157
 
159
- // Create the connector project structure
160
- fs.mkdirSync(target);
161
- console.log('Creating connector project...');
162
- extract({...options, target, name});
163
-
164
158
  // Generate the controller from OpenAPI spec
165
159
  const generator = new OpenAPIToConnector(spec, name);
166
160
  let controllerCode: string;
@@ -173,30 +167,51 @@ program
173
167
  controllerCode = generator.generateController();
174
168
  }
175
169
 
176
- // Write the generated controller
177
- const controllerPath = `${target}/${options.out}`;
178
- fs.mkdirSync(path.dirname(controllerPath), {recursive: true});
179
- fs.writeFileSync(controllerPath, controllerCode);
170
+ if (options.controllerOnly) {
171
+ // Controller-only mode: just generate the controller file
172
+ const controllerPath = path.resolve(options.out);
173
+ fs.mkdirSync(path.dirname(controllerPath), {recursive: true});
174
+ fs.writeFileSync(controllerPath, controllerCode);
180
175
 
181
- console.log('Generating keys...');
182
- await generateKeys({target});
176
+ console.log(`\nāœ… Success! Generated controller from OpenAPI specification
177
+ šŸ“ Connector name: ${name}
178
+ šŸ“Š Found ${generator.getOperationsCount()} operations
179
+ šŸ“„ Controller file: ${controllerPath}
180
+
181
+ Controller file generated successfully! You can now use it in your existing project.`);
182
+ } else {
183
+ // Full project mode: create complete project structure
184
+ const target = `${process.cwd()}/${name}`;
183
185
 
184
- if (options.build !== false) {
185
- console.log('Installing dependencies...');
186
- await exec(`cd ${target}; yarn --ignore-engines`);
186
+ // Create the connector project structure
187
+ fs.mkdirSync(target);
188
+ console.log('Creating connector project...');
189
+ extract({...options, target, name});
187
190
 
188
- console.log('Building...');
189
- await exec(`cd ${target}; yarn build`);
190
- }
191
+ // Write the generated controller
192
+ const controllerPath = `${target}/${options.out}`;
193
+ fs.mkdirSync(path.dirname(controllerPath), {recursive: true});
194
+ fs.writeFileSync(controllerPath, controllerCode);
191
195
 
192
- const nextSteps =
193
- options.build !== false
194
- ? `Next steps:
196
+ console.log('Generating keys...');
197
+ await generateKeys({target});
198
+
199
+ if (options.build !== false) {
200
+ console.log('Installing dependencies...');
201
+ await exec(`cd ${target}; yarn --ignore-engines`);
202
+
203
+ console.log('Building...');
204
+ await exec(`cd ${target}; yarn build`);
205
+ }
206
+
207
+ const nextSteps =
208
+ options.build !== false
209
+ ? `Next steps:
195
210
  1.) Add the connector to a workspace
196
211
  2.) Edit ./${name}/.env and insert the registration token
197
212
  3.) Implement the actual API calls in each method in ${options.out}
198
213
  4.) Start the connector with cd ./${name}/; yarn start`
199
- : `Next steps:
214
+ : `Next steps:
200
215
  1.) Install dependencies: cd ./${name}/ && yarn --ignore-engines
201
216
  2.) Implement the actual API calls in each method in ${options.out}
202
217
  3.) Build the project: yarn build
@@ -204,19 +219,24 @@ program
204
219
  5.) Edit ./${name}/.env and insert the registration token
205
220
  6.) Start the connector: yarn start`;
206
221
 
207
- console.log(`
208
- āœ… Success! Generated connector from OpenAPI specification
222
+ console.log(`\nāœ… Success! Generated connector from OpenAPI specification
209
223
  šŸ“ Connector name: ${name}
210
224
  šŸ“Š Found ${generator.getOperationsCount()} operations
211
225
  šŸ“„ Controller generated: ${options.out}
212
226
 
213
227
  ${nextSteps}`);
228
+ }
214
229
  } catch (error) {
215
230
  console.error('āŒ Error:', error instanceof Error ? error.message : 'Unknown error');
216
- // Clean up on error
217
- if (fs.existsSync(target)) {
218
- fs.rmSync(target, {recursive: true, force: true});
231
+
232
+ // Clean up on error (only if we created a project directory)
233
+ if (!options.controllerOnly) {
234
+ const target = `${process.cwd()}/${name}`;
235
+ if (fs.existsSync(target)) {
236
+ fs.rmSync(target, {recursive: true, force: true});
237
+ }
219
238
  }
239
+
220
240
  process.exit(1);
221
241
  }
222
242
  });
@@ -283,6 +303,7 @@ program
283
303
 
284
304
  // Generate each resource
285
305
  const resources: Array<{className: string; fileName: string}> = [];
306
+ const parsedResourceSpecs: Array<{fileName: string; spec: any}> = [];
286
307
  let baseUrl = options.baseUrl;
287
308
 
288
309
  for (const {className, specFile} of resourceSpecs) {
@@ -308,13 +329,14 @@ program
308
329
  fs.writeFileSync(resourcePath, resourceCode);
309
330
 
310
331
  resources.push({className, fileName});
332
+ parsedResourceSpecs.push({fileName, spec});
311
333
  }
312
334
 
313
335
  // Generate the main controller
314
336
  console.log('Generating main controller...');
315
337
  const firstSpec = OpenAPIToConnector.parseSpec(fs.readFileSync(resourceSpecs[0].specFile, 'utf-8'));
316
338
  const mainGenerator = new OpenAPIToConnector(firstSpec, name);
317
- const mainControllerCode = mainGenerator.generateMainController(resources);
339
+ const mainControllerCode = mainGenerator.generateMainController(resources, parsedResourceSpecs);
318
340
 
319
341
  // Write the main controller
320
342
  const controllerPath = `${target}/src/controller/index.mts`;
@@ -372,6 +394,13 @@ program
372
394
  throw new Error(`Project path does not exist: ${target}`);
373
395
  }
374
396
 
397
+ const controllerPath = `${target}/src/controller/index.mts`;
398
+ if (!fs.existsSync(controllerPath)) {
399
+ throw new Error(
400
+ `Controller file not found: ${controllerPath}. This might not be a multi-resource connector project.`
401
+ );
402
+ }
403
+
375
404
  try {
376
405
  console.log(`Adding ${options.className} resource to existing project...`);
377
406
 
@@ -379,7 +408,7 @@ program
379
408
  const specContent = fs.readFileSync(options.spec, 'utf-8');
380
409
  const spec = OpenAPIToConnector.parseSpec(specContent);
381
410
 
382
- // Generate the resource class
411
+ // Generate the resource functions file (new function-based pattern)
383
412
  const generator = new OpenAPIToConnector(spec, 'Resource');
384
413
  const resourceCode = generator.generateResourceClass(options.className);
385
414
 
@@ -389,18 +418,92 @@ program
389
418
  fs.mkdirSync(path.dirname(resourcePath), {recursive: true});
390
419
  fs.writeFileSync(resourcePath, resourceCode);
391
420
 
392
- console.log(`āœ… Resource ${options.className} added successfully at ${resourcePath}`);
393
- console.log('\nāš ļø You need to manually update the main controller to include this new resource:');
394
- console.log(`1.) Add import: import ${options.className} from '../resources/${fileName}.mjs';`);
395
- console.log(`2.) Add property: ${fileName}!: ${options.className};`);
396
- console.log(`3.) Add initialization in start(): this.${fileName} = new ${options.className}(this);`);
421
+ // Update the main controller to include the new resource
422
+ console.log('Updating main controller...');
423
+ let controllerContent = fs.readFileSync(controllerPath, 'utf-8');
424
+
425
+ // Add import
426
+ const importStatement = `import * as ${fileName}Functions from '../resources/${fileName}.mjs';`;
427
+ if (!controllerContent.includes(importStatement)) {
428
+ // Find the last import and add after it
429
+ const importRegex = /^import.*from.*?;$/gm;
430
+ const imports = controllerContent.match(importRegex);
431
+ if (imports && imports.length > 0) {
432
+ const lastImport = imports[imports.length - 1];
433
+ controllerContent = controllerContent.replace(lastImport, `${lastImport}\n${importStatement}`);
434
+ } else {
435
+ // If no imports found, add at the beginning
436
+ controllerContent = `${importStatement}\n\n${controllerContent}`;
437
+ }
438
+ }
439
+
440
+ // Add property declaration
441
+ const propertyDeclaration = ` ${fileName}: any = {};`;
442
+ if (!controllerContent.includes(propertyDeclaration)) {
443
+ // Find the existing properties and add the new one
444
+ const propertyRegex = /^ \w+: any = \{\};$/gm;
445
+ const properties = controllerContent.match(propertyRegex);
446
+ if (properties && properties.length > 0) {
447
+ const lastProperty = properties[properties.length - 1];
448
+ controllerContent = controllerContent.replace(lastProperty, `${lastProperty}\n${propertyDeclaration}`);
449
+ } else {
450
+ // Add after class declaration
451
+ controllerContent = controllerContent.replace(
452
+ /^export default class Controller extends AbstractController \{$/gm,
453
+ `export default class Controller extends AbstractController {\n${propertyDeclaration}`
454
+ );
455
+ }
456
+ }
457
+
458
+ // Add binding in start() method
459
+ const bindingStatement = ` this.bindResourceFunctions('${fileName}', ${fileName}Functions);`;
460
+ if (!controllerContent.includes(bindingStatement)) {
461
+ // Find the existing bindings and add the new one
462
+ const bindingRegex = /^ this\.bindResourceFunctions\(.*?\);$/gm;
463
+ const bindings = controllerContent.match(bindingRegex);
464
+ if (bindings && bindings.length > 0) {
465
+ const lastBinding = bindings[bindings.length - 1];
466
+ controllerContent = controllerContent.replace(lastBinding, `${lastBinding}\n${bindingStatement}`);
467
+ }
468
+ }
469
+
470
+ // Write the updated controller
471
+ fs.writeFileSync(controllerPath, controllerContent);
472
+
473
+ // Generate exposed methods for the new resource
474
+ console.log('Generating exposed methods for the new resource...');
475
+ const resources = [{className: options.className, fileName}];
476
+ const resourceSpecs = [{fileName, spec}];
477
+
478
+ // Create a temporary generator to generate just the exposed methods for this resource
479
+ const tempGenerator = new OpenAPIToConnector(spec, 'temp');
480
+ const exposedMethods = tempGenerator.generateExposedResourceMethods(resources, resourceSpecs);
481
+
482
+ // Add the exposed methods to the controller before the closing brace
483
+ if (exposedMethods.trim()) {
484
+ controllerContent = fs.readFileSync(controllerPath, 'utf-8');
485
+ // Find the last method and add the new exposed methods
486
+ const lastBrace = controllerContent.lastIndexOf('}');
487
+ if (lastBrace !== -1) {
488
+ const beforeBrace = controllerContent.substring(0, lastBrace);
489
+ const afterBrace = controllerContent.substring(lastBrace);
490
+ const updatedContent = `${beforeBrace}\n${exposedMethods}\n${afterBrace}`;
491
+ fs.writeFileSync(controllerPath, updatedContent);
492
+ }
493
+ }
494
+
495
+ console.log(`āœ… Resource ${options.className} added successfully!`);
496
+ console.log(`šŸ“„ Resource functions: ${resourcePath}`);
497
+ console.log(`šŸŽ›ļø Controller updated: ${controllerPath}`);
498
+ console.log(`\nšŸŽ‰ The new resource is fully integrated and ready to use!`);
397
499
 
398
500
  if (options.build !== false) {
399
- console.log('\nBuilding project...');
501
+ console.log('\nšŸ”Ø Building project...');
400
502
  await exec(`cd "${target}"; yarn build`);
503
+ console.log('āœ… Build complete!');
401
504
  }
402
505
  } catch (error) {
403
- console.error('Error adding resource:', (error as Error).message);
506
+ console.error('āŒ Error adding resource:', (error as Error).message);
404
507
  process.exit(1);
405
508
  }
406
509
  });