@aloma.io/integration-sdk 3.8.55 ā 3.8.57
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/MULTI_RESOURCE_GUIDE.md +24 -21
- package/OPENAPI_TO_CONNECTOR.md +146 -16
- package/README.md +62 -10
- package/build/cli.mjs +122 -33
- package/build/internal/dispatcher/index.mjs +3 -2
- package/build/openapi-to-connector.d.mts +88 -11
- package/build/openapi-to-connector.mjs +909 -209
- package/package.json +15 -1
- package/src/cli.mts +140 -37
- package/src/internal/dispatcher/index.mts +4 -2
- package/src/openapi-to-connector.mts +1006 -217
- package/test/scenarios/README.md +148 -0
- package/test/scenarios/complex/expected/controller.mts +271 -0
- package/test/scenarios/complex/expected/orders-resource.mts +264 -0
- package/test/scenarios/complex/expected/products-resource.mts +239 -0
- package/test/scenarios/complex/specs/orders.json +362 -0
- package/test/scenarios/complex/specs/products.json +308 -0
- package/test/scenarios/simple/expected-controller.mts +60 -0
- package/test/scenarios/simple/simple-api.json +39 -0
- package/test/scenarios.test.mts +286 -0
- package/test/verify-scenarios.mjs +298 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aloma.io/integration-sdk",
|
|
3
|
-
"version": "3.8.
|
|
3
|
+
"version": "3.8.57",
|
|
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
|
},
|
|
@@ -51,5 +53,17 @@
|
|
|
51
53
|
"mocha": "^10",
|
|
52
54
|
"prettier": "^3",
|
|
53
55
|
"ts-node": "^10.9.2"
|
|
56
|
+
},
|
|
57
|
+
"resolutions": {
|
|
58
|
+
"path-to-regexp": "0.1.13"
|
|
59
|
+
},
|
|
60
|
+
"repository": {
|
|
61
|
+
"type": "git",
|
|
62
|
+
"url": "https://github.com/aloma-io/integration.git",
|
|
63
|
+
"directory": "integration-sdk"
|
|
64
|
+
},
|
|
65
|
+
"publishConfig": {
|
|
66
|
+
"access": "public",
|
|
67
|
+
"registry": "https://registry.npmjs.org/"
|
|
54
68
|
}
|
|
55
69
|
}
|
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
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
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
|
-
|
|
182
|
-
|
|
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
|
-
|
|
185
|
-
|
|
186
|
-
|
|
186
|
+
// Create the connector project structure
|
|
187
|
+
fs.mkdirSync(target);
|
|
188
|
+
console.log('Creating connector project...');
|
|
189
|
+
extract({...options, target, name});
|
|
187
190
|
|
|
188
|
-
|
|
189
|
-
|
|
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
|
-
|
|
193
|
-
|
|
194
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
217
|
-
if
|
|
218
|
-
|
|
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
|
|
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
|
-
|
|
393
|
-
console.log('
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
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('\
|
|
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
|
});
|
|
@@ -172,10 +172,12 @@ ${arg.configurableClientScope}
|
|
|
172
172
|
)
|
|
173
173
|
.slice(0, 20);
|
|
174
174
|
|
|
175
|
+
const originalQuery = [...query];
|
|
176
|
+
|
|
175
177
|
const method = resolveMethod(query);
|
|
176
|
-
if (!method && !_resolvers.__default) throw new Error(`${
|
|
178
|
+
if (!method && !_resolvers.__default) throw new Error(`${originalQuery} not found`);
|
|
177
179
|
|
|
178
|
-
return method ? method(variables) : _resolvers.__default(variables ? {...variables, __method:
|
|
180
|
+
return method ? method(variables) : _resolvers.__default(variables ? {...variables, __method: originalQuery} : variables);
|
|
179
181
|
};
|
|
180
182
|
|
|
181
183
|
const introspect = () => local._types;
|