@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
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
import { OpenAPIToConnector } from '../build/openapi-to-connector.mjs';
|
|
5
|
+
|
|
6
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
7
|
+
const __dirname = path.dirname(__filename);
|
|
8
|
+
|
|
9
|
+
// Colors for output
|
|
10
|
+
const colors = {
|
|
11
|
+
green: '\x1b[32m',
|
|
12
|
+
red: '\x1b[31m',
|
|
13
|
+
yellow: '\x1b[33m',
|
|
14
|
+
reset: '\x1b[0m',
|
|
15
|
+
cyan: '\x1b[36m'
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
let passed = 0;
|
|
19
|
+
let failed = 0;
|
|
20
|
+
|
|
21
|
+
function test(name, fn) {
|
|
22
|
+
try {
|
|
23
|
+
console.log(`${colors.cyan}Running: ${name}${colors.reset}`);
|
|
24
|
+
fn();
|
|
25
|
+
console.log(`${colors.green}✓ PASS: ${name}${colors.reset}\n`);
|
|
26
|
+
passed++;
|
|
27
|
+
} catch (error) {
|
|
28
|
+
console.log(`${colors.red}✗ FAIL: ${name}${colors.reset}`);
|
|
29
|
+
console.log(`${colors.red} Error: ${error.message}${colors.reset}\n`);
|
|
30
|
+
failed++;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function expect(actual) {
|
|
35
|
+
return {
|
|
36
|
+
toEqual: (expected) => {
|
|
37
|
+
if (actual !== expected) {
|
|
38
|
+
throw new Error(`Expected:\n${expected.slice(0, 200)}...\n\nActual:\n${actual.slice(0, 200)}...`);
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
toInclude: (expected) => {
|
|
42
|
+
if (!actual.includes(expected)) {
|
|
43
|
+
throw new Error(`Expected output to include: "${expected}"\n\nActual output snippet: "${actual.slice(0, 500)}..."`);
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
toNotInclude: (expected) => {
|
|
47
|
+
if (actual.includes(expected)) {
|
|
48
|
+
throw new Error(`Expected output to NOT include: "${expected}"\n\nBut it was found in the output.`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Helper function to normalize output
|
|
55
|
+
const normalizeOutput = (output) => output
|
|
56
|
+
.replace(/\r\n/g, '\n')
|
|
57
|
+
.trim();
|
|
58
|
+
|
|
59
|
+
console.log(`${colors.yellow}🧪 Running Scenario Fixture Tests${colors.reset}\n`);
|
|
60
|
+
|
|
61
|
+
// Test 1: Simple Scenario
|
|
62
|
+
test('Simple Scenario - should generate expected output', () => {
|
|
63
|
+
// Read input spec
|
|
64
|
+
const specPath = path.join(__dirname, 'scenarios/simple/simple-api.json');
|
|
65
|
+
const specContent = fs.readFileSync(specPath, 'utf-8');
|
|
66
|
+
const spec = OpenAPIToConnector.parseSpec(specContent);
|
|
67
|
+
|
|
68
|
+
// Read expected output
|
|
69
|
+
const expectedPath = path.join(__dirname, 'scenarios/simple/expected-controller.mts');
|
|
70
|
+
const expectedOutput = fs.readFileSync(expectedPath, 'utf-8');
|
|
71
|
+
|
|
72
|
+
// Generate actual output
|
|
73
|
+
const generator = new OpenAPIToConnector(spec, 'simple-test');
|
|
74
|
+
const actualOutput = generator.generateController();
|
|
75
|
+
|
|
76
|
+
// Compare outputs
|
|
77
|
+
expect(normalizeOutput(actualOutput)).toEqual(normalizeOutput(expectedOutput));
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// Test 2: Simple Scenario - Methods without options
|
|
81
|
+
test('Simple Scenario - methods without options should be clean', () => {
|
|
82
|
+
const specPath = path.join(__dirname, 'scenarios/simple/simple-api.json');
|
|
83
|
+
const specContent = fs.readFileSync(specPath, 'utf-8');
|
|
84
|
+
const spec = OpenAPIToConnector.parseSpec(specContent);
|
|
85
|
+
|
|
86
|
+
const generator = new OpenAPIToConnector(spec, 'simple-test');
|
|
87
|
+
const actualOutput = generator.generateController();
|
|
88
|
+
|
|
89
|
+
expect(actualOutput).toInclude('async getProducts() {');
|
|
90
|
+
expect(actualOutput).toInclude('async createProduct(options:');
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
// Test 3: Complex Scenario - Main Controller
|
|
94
|
+
test('Complex Scenario - main controller should generate expected output', () => {
|
|
95
|
+
const productsSpecPath = path.join(__dirname, 'scenarios/complex/specs/products.json');
|
|
96
|
+
const ordersSpecPath = path.join(__dirname, 'scenarios/complex/specs/orders.json');
|
|
97
|
+
const productsSpecContent = fs.readFileSync(productsSpecPath, 'utf-8');
|
|
98
|
+
const ordersSpecContent = fs.readFileSync(ordersSpecPath, 'utf-8');
|
|
99
|
+
|
|
100
|
+
const productsSpec = OpenAPIToConnector.parseSpec(productsSpecContent);
|
|
101
|
+
const ordersSpec = OpenAPIToConnector.parseSpec(ordersSpecContent);
|
|
102
|
+
|
|
103
|
+
const expectedPath = path.join(__dirname, 'scenarios/complex/expected/controller.mts');
|
|
104
|
+
const expectedOutput = fs.readFileSync(expectedPath, 'utf-8');
|
|
105
|
+
|
|
106
|
+
const resources = [
|
|
107
|
+
{ className: 'ProductsResource', fileName: 'products' },
|
|
108
|
+
{ className: 'OrdersResource', fileName: 'orders' }
|
|
109
|
+
];
|
|
110
|
+
const resourceSpecs = [
|
|
111
|
+
{ fileName: 'products', spec: productsSpec },
|
|
112
|
+
{ fileName: 'orders', spec: ordersSpec }
|
|
113
|
+
];
|
|
114
|
+
|
|
115
|
+
const mainGenerator = new OpenAPIToConnector(productsSpec, 'test-shop-temp');
|
|
116
|
+
const actualOutput = mainGenerator.generateMainController(resources, resourceSpecs);
|
|
117
|
+
|
|
118
|
+
expect(normalizeOutput(actualOutput)).toEqual(normalizeOutput(expectedOutput));
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
// Test 4: Complex Scenario - Products Resource
|
|
122
|
+
test('Complex Scenario - products resource should generate expected output', () => {
|
|
123
|
+
const specPath = path.join(__dirname, 'scenarios/complex/specs/products.json');
|
|
124
|
+
const specContent = fs.readFileSync(specPath, 'utf-8');
|
|
125
|
+
const spec = OpenAPIToConnector.parseSpec(specContent);
|
|
126
|
+
|
|
127
|
+
const expectedPath = path.join(__dirname, 'scenarios/complex/expected/products-resource.mts');
|
|
128
|
+
const expectedOutput = fs.readFileSync(expectedPath, 'utf-8');
|
|
129
|
+
|
|
130
|
+
const generator = new OpenAPIToConnector(spec, 'test-shop');
|
|
131
|
+
const actualOutput = generator.generateResourceClass('ProductsResource');
|
|
132
|
+
|
|
133
|
+
expect(normalizeOutput(actualOutput)).toEqual(normalizeOutput(expectedOutput));
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
// Test 5: Complex Scenario - Orders Resource
|
|
137
|
+
test('Complex Scenario - orders resource should generate expected output', () => {
|
|
138
|
+
const specPath = path.join(__dirname, 'scenarios/complex/specs/orders.json');
|
|
139
|
+
const specContent = fs.readFileSync(specPath, 'utf-8');
|
|
140
|
+
const spec = OpenAPIToConnector.parseSpec(specContent);
|
|
141
|
+
|
|
142
|
+
const expectedPath = path.join(__dirname, 'scenarios/complex/expected/orders-resource.mts');
|
|
143
|
+
const expectedOutput = fs.readFileSync(expectedPath, 'utf-8');
|
|
144
|
+
|
|
145
|
+
const generator = new OpenAPIToConnector(spec, 'test-shop');
|
|
146
|
+
const actualOutput = generator.generateResourceClass('OrdersResource');
|
|
147
|
+
|
|
148
|
+
expect(normalizeOutput(actualOutput)).toEqual(normalizeOutput(expectedOutput));
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
// Test 6: TypeScript Interface Generation
|
|
152
|
+
test('TypeScript interfaces should be generated correctly', () => {
|
|
153
|
+
const specPath = path.join(__dirname, 'scenarios/complex/specs/products.json');
|
|
154
|
+
const specContent = fs.readFileSync(specPath, 'utf-8');
|
|
155
|
+
const spec = OpenAPIToConnector.parseSpec(specContent);
|
|
156
|
+
|
|
157
|
+
const generator = new OpenAPIToConnector(spec, 'test-shop');
|
|
158
|
+
const actualOutput = generator.generateResourceClass('ProductsResource');
|
|
159
|
+
|
|
160
|
+
expect(actualOutput).toInclude('export interface Product {');
|
|
161
|
+
expect(actualOutput).toInclude('export interface ProductList {');
|
|
162
|
+
expect(actualOutput).toInclude('export interface CreateProductRequest {');
|
|
163
|
+
expect(actualOutput).toInclude('Promise<ProductList>');
|
|
164
|
+
expect(actualOutput).toInclude('Promise<Product>');
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
// Test 7: Path Parameters and Options
|
|
168
|
+
test('Path parameters and options should be handled correctly', () => {
|
|
169
|
+
const specPath = path.join(__dirname, 'scenarios/complex/specs/orders.json');
|
|
170
|
+
const specContent = fs.readFileSync(specPath, 'utf-8');
|
|
171
|
+
const spec = OpenAPIToConnector.parseSpec(specContent);
|
|
172
|
+
|
|
173
|
+
const generator = new OpenAPIToConnector(spec, 'test-shop');
|
|
174
|
+
const actualOutput = generator.generateResourceClass('OrdersResource');
|
|
175
|
+
|
|
176
|
+
expect(actualOutput).toInclude('export function getOrder(this: any, orderId: string)');
|
|
177
|
+
expect(actualOutput).toInclude('export function updateOrderStatus(this: any, orderId: string, options');
|
|
178
|
+
expect(actualOutput).toInclude('export function cancelOrder(this: any, orderId: string)');
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
// Test 8: Exposed Methods in Main Controller
|
|
182
|
+
test('Exposed methods should be present in main controller', () => {
|
|
183
|
+
const productsSpecPath = path.join(__dirname, 'scenarios/complex/specs/products.json');
|
|
184
|
+
const ordersSpecPath = path.join(__dirname, 'scenarios/complex/specs/orders.json');
|
|
185
|
+
const productsSpecContent = fs.readFileSync(productsSpecPath, 'utf-8');
|
|
186
|
+
const ordersSpecContent = fs.readFileSync(ordersSpecPath, 'utf-8');
|
|
187
|
+
|
|
188
|
+
const productsSpec = OpenAPIToConnector.parseSpec(productsSpecContent);
|
|
189
|
+
const ordersSpec = OpenAPIToConnector.parseSpec(ordersSpecContent);
|
|
190
|
+
|
|
191
|
+
const resources = [
|
|
192
|
+
{ className: 'ProductsResource', fileName: 'products' },
|
|
193
|
+
{ className: 'OrdersResource', fileName: 'orders' }
|
|
194
|
+
];
|
|
195
|
+
const resourceSpecs = [
|
|
196
|
+
{ fileName: 'products', spec: productsSpec },
|
|
197
|
+
{ fileName: 'orders', spec: ordersSpec }
|
|
198
|
+
];
|
|
199
|
+
|
|
200
|
+
const mainGenerator = new OpenAPIToConnector(productsSpec, 'test-shop');
|
|
201
|
+
const actualOutput = mainGenerator.generateMainController(resources, resourceSpecs);
|
|
202
|
+
|
|
203
|
+
expect(actualOutput).toInclude('async productsGetProducts(');
|
|
204
|
+
expect(actualOutput).toInclude('async productsCreateProduct(');
|
|
205
|
+
expect(actualOutput).toInclude('async productsGetProduct(');
|
|
206
|
+
expect(actualOutput).toInclude('async productsUpdateProduct(');
|
|
207
|
+
expect(actualOutput).toInclude('async productsDeleteProduct(');
|
|
208
|
+
|
|
209
|
+
expect(actualOutput).toInclude('async ordersGetOrders(');
|
|
210
|
+
expect(actualOutput).toInclude('async ordersCreateOrder(');
|
|
211
|
+
expect(actualOutput).toInclude('async ordersGetOrder(');
|
|
212
|
+
expect(actualOutput).toInclude('async ordersUpdateOrderStatus(');
|
|
213
|
+
expect(actualOutput).toInclude('async ordersCancelOrder(');
|
|
214
|
+
|
|
215
|
+
expect(actualOutput).toInclude('this.bindResourceFunctions(\'products\', productsFunctions);');
|
|
216
|
+
expect(actualOutput).toInclude('this.bindResourceFunctions(\'orders\', ordersFunctions);');
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
// Test 9: Schema Name Sanitization
|
|
220
|
+
test('Schema names should be sanitized correctly', () => {
|
|
221
|
+
const testSpec = {
|
|
222
|
+
openapi: '3.0.0',
|
|
223
|
+
info: { title: 'Test API', version: '1.0.0' },
|
|
224
|
+
paths: {
|
|
225
|
+
'/test': {
|
|
226
|
+
get: {
|
|
227
|
+
operationId: 'testOperation',
|
|
228
|
+
responses: {
|
|
229
|
+
'200': {
|
|
230
|
+
description: 'Success',
|
|
231
|
+
content: {
|
|
232
|
+
'application/json': {
|
|
233
|
+
schema: {
|
|
234
|
+
'$ref': '#/components/schemas/Complex.Name.With.Dots'
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
},
|
|
243
|
+
components: {
|
|
244
|
+
schemas: {
|
|
245
|
+
'Complex.Name.With.Dots': {
|
|
246
|
+
type: 'object',
|
|
247
|
+
properties: {
|
|
248
|
+
id: { type: 'string' },
|
|
249
|
+
name: { type: 'string' }
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
const generator = new OpenAPIToConnector(testSpec, 'test');
|
|
257
|
+
const output = generator.generateController();
|
|
258
|
+
|
|
259
|
+
expect(output).toInclude('export interface Complex_Name_With_Dots {');
|
|
260
|
+
expect(output).toNotInclude('export interface Complex.Name.With.Dots {');
|
|
261
|
+
expect(output).toInclude('Promise<Complex_Name_With_Dots>');
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
// Test 10: No Options Parameter for Simple Methods
|
|
265
|
+
test('Simple methods should not have options parameter', () => {
|
|
266
|
+
const simpleSpec = {
|
|
267
|
+
openapi: '3.0.0',
|
|
268
|
+
info: { title: 'Simple API', version: '1.0.0' },
|
|
269
|
+
paths: {
|
|
270
|
+
'/simple': {
|
|
271
|
+
get: {
|
|
272
|
+
operationId: 'getSimple',
|
|
273
|
+
responses: { '200': { description: 'Success' } }
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
const generator = new OpenAPIToConnector(simpleSpec, 'test');
|
|
280
|
+
const output = generator.generateController();
|
|
281
|
+
|
|
282
|
+
expect(output).toInclude('async getSimple() {');
|
|
283
|
+
expect(output).toNotInclude('options = options || {}');
|
|
284
|
+
expect(output).toNotInclude('headers: options');
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
// Print final results
|
|
288
|
+
console.log(`${colors.yellow}📊 Test Results:${colors.reset}`);
|
|
289
|
+
console.log(`${colors.green}✓ Passed: ${passed}${colors.reset}`);
|
|
290
|
+
console.log(`${colors.red}✗ Failed: ${failed}${colors.reset}`);
|
|
291
|
+
|
|
292
|
+
if (failed === 0) {
|
|
293
|
+
console.log(`\n${colors.green}🎉 All tests passed!${colors.reset}`);
|
|
294
|
+
process.exit(0);
|
|
295
|
+
} else {
|
|
296
|
+
console.log(`\n${colors.red}❌ Some tests failed!${colors.reset}`);
|
|
297
|
+
process.exit(1);
|
|
298
|
+
}
|