@aloma.io/integration-sdk 3.8.55 → 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.
@@ -0,0 +1,308 @@
1
+ {
2
+ "openapi": "3.0.0",
3
+ "info": {
4
+ "title": "Products API",
5
+ "version": "1.0.0",
6
+ "description": "A simple products management API"
7
+ },
8
+ "servers": [
9
+ {
10
+ "url": "https://api.testshop.com",
11
+ "description": "Production server"
12
+ }
13
+ ],
14
+ "paths": {
15
+ "/products": {
16
+ "get": {
17
+ "operationId": "getProducts",
18
+ "summary": "List all products",
19
+ "parameters": [
20
+ {
21
+ "name": "limit",
22
+ "in": "query",
23
+ "schema": {
24
+ "type": "integer",
25
+ "minimum": 1,
26
+ "maximum": 100
27
+ },
28
+ "description": "Maximum number of products to return"
29
+ },
30
+ {
31
+ "name": "category",
32
+ "in": "query",
33
+ "schema": {
34
+ "type": "string"
35
+ },
36
+ "description": "Filter by category"
37
+ },
38
+ {
39
+ "name": "archived",
40
+ "in": "query",
41
+ "schema": {
42
+ "type": "boolean"
43
+ },
44
+ "description": "Include archived products"
45
+ }
46
+ ],
47
+ "responses": {
48
+ "200": {
49
+ "description": "List of products",
50
+ "content": {
51
+ "application/json": {
52
+ "schema": {
53
+ "$ref": "#/components/schemas/ProductList"
54
+ }
55
+ }
56
+ }
57
+ }
58
+ }
59
+ },
60
+ "post": {
61
+ "operationId": "createProduct",
62
+ "summary": "Create a new product",
63
+ "requestBody": {
64
+ "required": true,
65
+ "content": {
66
+ "application/json": {
67
+ "schema": {
68
+ "$ref": "#/components/schemas/CreateProductRequest"
69
+ }
70
+ }
71
+ }
72
+ },
73
+ "responses": {
74
+ "201": {
75
+ "description": "Product created",
76
+ "content": {
77
+ "application/json": {
78
+ "schema": {
79
+ "$ref": "#/components/schemas/Product"
80
+ }
81
+ }
82
+ }
83
+ }
84
+ }
85
+ }
86
+ },
87
+ "/products/{productId}": {
88
+ "get": {
89
+ "operationId": "getProduct",
90
+ "summary": "Get a specific product",
91
+ "parameters": [
92
+ {
93
+ "name": "productId",
94
+ "in": "path",
95
+ "required": true,
96
+ "schema": {
97
+ "type": "string"
98
+ },
99
+ "description": "The product ID"
100
+ }
101
+ ],
102
+ "responses": {
103
+ "200": {
104
+ "description": "Product details",
105
+ "content": {
106
+ "application/json": {
107
+ "schema": {
108
+ "$ref": "#/components/schemas/Product"
109
+ }
110
+ }
111
+ }
112
+ }
113
+ }
114
+ },
115
+ "put": {
116
+ "operationId": "updateProduct",
117
+ "summary": "Update a product",
118
+ "parameters": [
119
+ {
120
+ "name": "productId",
121
+ "in": "path",
122
+ "required": true,
123
+ "schema": {
124
+ "type": "string"
125
+ },
126
+ "description": "The product ID"
127
+ }
128
+ ],
129
+ "requestBody": {
130
+ "required": true,
131
+ "content": {
132
+ "application/json": {
133
+ "schema": {
134
+ "$ref": "#/components/schemas/UpdateProductRequest"
135
+ }
136
+ }
137
+ }
138
+ },
139
+ "responses": {
140
+ "200": {
141
+ "description": "Product updated",
142
+ "content": {
143
+ "application/json": {
144
+ "schema": {
145
+ "$ref": "#/components/schemas/Product"
146
+ }
147
+ }
148
+ }
149
+ }
150
+ }
151
+ },
152
+ "delete": {
153
+ "operationId": "deleteProduct",
154
+ "summary": "Delete a product",
155
+ "parameters": [
156
+ {
157
+ "name": "productId",
158
+ "in": "path",
159
+ "required": true,
160
+ "schema": {
161
+ "type": "string"
162
+ },
163
+ "description": "The product ID"
164
+ }
165
+ ],
166
+ "responses": {
167
+ "204": {
168
+ "description": "Product deleted"
169
+ }
170
+ }
171
+ }
172
+ }
173
+ },
174
+ "components": {
175
+ "schemas": {
176
+ "Product": {
177
+ "type": "object",
178
+ "required": ["id", "name", "price"],
179
+ "properties": {
180
+ "id": {
181
+ "type": "string",
182
+ "description": "Unique product identifier"
183
+ },
184
+ "name": {
185
+ "type": "string",
186
+ "description": "Product name"
187
+ },
188
+ "description": {
189
+ "type": "string",
190
+ "description": "Product description"
191
+ },
192
+ "price": {
193
+ "type": "number",
194
+ "minimum": 0,
195
+ "description": "Product price"
196
+ },
197
+ "category": {
198
+ "type": "string",
199
+ "description": "Product category"
200
+ },
201
+ "inStock": {
202
+ "type": "boolean",
203
+ "description": "Whether product is in stock"
204
+ },
205
+ "tags": {
206
+ "type": "array",
207
+ "items": {
208
+ "type": "string"
209
+ },
210
+ "description": "Product tags"
211
+ },
212
+ "createdAt": {
213
+ "type": "string",
214
+ "format": "date-time",
215
+ "description": "Creation timestamp"
216
+ },
217
+ "updatedAt": {
218
+ "type": "string",
219
+ "format": "date-time",
220
+ "description": "Last update timestamp"
221
+ }
222
+ }
223
+ },
224
+ "ProductList": {
225
+ "type": "object",
226
+ "properties": {
227
+ "products": {
228
+ "type": "array",
229
+ "items": {
230
+ "$ref": "#/components/schemas/Product"
231
+ }
232
+ },
233
+ "total": {
234
+ "type": "integer",
235
+ "description": "Total number of products"
236
+ },
237
+ "hasMore": {
238
+ "type": "boolean",
239
+ "description": "Whether there are more products available"
240
+ }
241
+ }
242
+ },
243
+ "CreateProductRequest": {
244
+ "type": "object",
245
+ "required": ["name", "price"],
246
+ "properties": {
247
+ "name": {
248
+ "type": "string",
249
+ "description": "Product name"
250
+ },
251
+ "description": {
252
+ "type": "string",
253
+ "description": "Product description"
254
+ },
255
+ "price": {
256
+ "type": "number",
257
+ "minimum": 0,
258
+ "description": "Product price"
259
+ },
260
+ "category": {
261
+ "type": "string",
262
+ "description": "Product category"
263
+ },
264
+ "tags": {
265
+ "type": "array",
266
+ "items": {
267
+ "type": "string"
268
+ },
269
+ "description": "Product tags"
270
+ }
271
+ }
272
+ },
273
+ "UpdateProductRequest": {
274
+ "type": "object",
275
+ "properties": {
276
+ "name": {
277
+ "type": "string",
278
+ "description": "Product name"
279
+ },
280
+ "description": {
281
+ "type": "string",
282
+ "description": "Product description"
283
+ },
284
+ "price": {
285
+ "type": "number",
286
+ "minimum": 0,
287
+ "description": "Product price"
288
+ },
289
+ "category": {
290
+ "type": "string",
291
+ "description": "Product category"
292
+ },
293
+ "inStock": {
294
+ "type": "boolean",
295
+ "description": "Whether product is in stock"
296
+ },
297
+ "tags": {
298
+ "type": "array",
299
+ "items": {
300
+ "type": "string"
301
+ },
302
+ "description": "Product tags"
303
+ }
304
+ }
305
+ }
306
+ }
307
+ }
308
+ }
@@ -0,0 +1,60 @@
1
+ import {AbstractController} from '@aloma.io/integration-sdk';
2
+
3
+ export default class Controller extends AbstractController {
4
+
5
+ private api: any;
6
+
7
+ protected async start(): Promise<void> {
8
+ const config = this.config;
9
+
10
+ this.api = this.getClient({
11
+ baseUrl: 'https://api.example.com',
12
+ customize(request) {
13
+ request.headers ||= {};
14
+ // Add authentication headers based on your API requirements
15
+ // Example: request.headers["Authorization"] = `Bearer ${config.apiToken}`;
16
+ },
17
+ });
18
+ }
19
+
20
+ /**
21
+ * Get all products
22
+ *
23
+ *
24
+ * @returns {Promise<any>} GET /products response
25
+ */
26
+ async getProducts() {
27
+ const url = '/products';
28
+
29
+ const fetchOptions: any = {
30
+ method: 'GET',
31
+ };
32
+
33
+ return this.api.fetch(url, fetchOptions);
34
+ }
35
+
36
+ /**
37
+ * Create product
38
+ *
39
+ * @param {Object} options - Request options
40
+ * @param {any} options.body (required) - Request body
41
+ *
42
+ * @returns {Promise<any>} POST /products response
43
+ */
44
+ async createProduct(options: {body?: any}) {
45
+ options = options || {};
46
+
47
+ const url = '/products';
48
+
49
+ const { headers, ...bodyData } = options;
50
+ const requestBody = Object.keys(bodyData).length > 0 ? bodyData : undefined;
51
+
52
+ const fetchOptions: any = {
53
+ method: 'POST',
54
+ body: requestBody,
55
+ headers: options.headers,
56
+ };
57
+
58
+ return this.api.fetch(url, fetchOptions);
59
+ }
60
+ }
@@ -0,0 +1,39 @@
1
+ {
2
+ "openapi": "3.0.0",
3
+ "info": {
4
+ "title": "Simple API",
5
+ "version": "1.0.0"
6
+ },
7
+ "paths": {
8
+ "/products": {
9
+ "get": {
10
+ "operationId": "getProducts",
11
+ "summary": "Get all products",
12
+ "responses": {
13
+ "200": {
14
+ "description": "Success"
15
+ }
16
+ }
17
+ },
18
+ "post": {
19
+ "operationId": "createProduct",
20
+ "summary": "Create product",
21
+ "requestBody": {
22
+ "required": true,
23
+ "content": {
24
+ "application/json": {
25
+ "schema": {
26
+ "type": "object"
27
+ }
28
+ }
29
+ }
30
+ },
31
+ "responses": {
32
+ "201": {
33
+ "description": "Created"
34
+ }
35
+ }
36
+ }
37
+ }
38
+ }
39
+ }
@@ -0,0 +1,286 @@
1
+ import { describe, it } from 'mocha';
2
+ import { expect } from 'chai';
3
+ import fs from 'fs';
4
+ import path from 'path';
5
+ import { fileURLToPath } from 'url';
6
+ import { OpenAPIToConnector } from '../build/openapi-to-connector.mjs';
7
+
8
+ const __filename = fileURLToPath(import.meta.url);
9
+ const __dirname = path.dirname(__filename);
10
+
11
+ describe('Scenario Tests - Full Code Generation', () => {
12
+
13
+ describe('Simple Scenario (single controller)', () => {
14
+ it('should generate exact expected output for simple-api.json', () => {
15
+ // Read input spec
16
+ const specPath = path.join(__dirname, 'scenarios/simple/simple-api.json');
17
+ const specContent = fs.readFileSync(specPath, 'utf-8');
18
+ const spec = OpenAPIToConnector.parseSpec(specContent);
19
+
20
+ // Read expected output
21
+ const expectedPath = path.join(__dirname, 'scenarios/simple/expected-controller.mts');
22
+ const expectedOutput = fs.readFileSync(expectedPath, 'utf-8');
23
+
24
+ // Generate actual output
25
+ const generator = new OpenAPIToConnector(spec, 'simple-test');
26
+ const actualOutput = generator.generateController();
27
+
28
+ // Compare outputs (normalize line endings)
29
+ const normalizeOutput = (output: string) => output
30
+ .replace(/\r\n/g, '\n')
31
+ .trim();
32
+
33
+ expect(normalizeOutput(actualOutput)).to.equal(normalizeOutput(expectedOutput));
34
+ });
35
+
36
+ it('should correctly handle methods without options', () => {
37
+ // Read input spec
38
+ const specPath = path.join(__dirname, 'scenarios/simple/simple-api.json');
39
+ const specContent = fs.readFileSync(specPath, 'utf-8');
40
+ const spec = OpenAPIToConnector.parseSpec(specContent);
41
+
42
+ // Generate actual output
43
+ const generator = new OpenAPIToConnector(spec, 'simple-test');
44
+ const actualOutput = generator.generateController();
45
+
46
+ // Verify methods without options don't have unnecessary code
47
+ expect(actualOutput).to.include('async getProducts() {'); // No options parameter
48
+ expect(actualOutput).to.not.include('options = options || {}'); // No options initialization for getProducts
49
+ expect(actualOutput).to.include('async createProduct(options:'); // Has options parameter
50
+ expect(actualOutput).to.include('options = options || {};'); // Has options initialization for createProduct
51
+ });
52
+ });
53
+
54
+ describe('Complex Scenario (multi-resource)', () => {
55
+ it('should generate exact expected main controller output', () => {
56
+ // Read input specs
57
+ const productsSpecPath = path.join(__dirname, 'scenarios/complex/specs/products.json');
58
+ const ordersSpecPath = path.join(__dirname, 'scenarios/complex/specs/orders.json');
59
+ const productsSpecContent = fs.readFileSync(productsSpecPath, 'utf-8');
60
+ const ordersSpecContent = fs.readFileSync(ordersSpecPath, 'utf-8');
61
+
62
+ const productsSpec = OpenAPIToConnector.parseSpec(productsSpecContent);
63
+ const ordersSpec = OpenAPIToConnector.parseSpec(ordersSpecContent);
64
+
65
+ // Read expected output
66
+ const expectedPath = path.join(__dirname, 'scenarios/complex/expected/controller.mts');
67
+ const expectedOutput = fs.readFileSync(expectedPath, 'utf-8');
68
+
69
+ // Generate actual output
70
+ const resources = [
71
+ { className: 'ProductsResource', fileName: 'products' },
72
+ { className: 'OrdersResource', fileName: 'orders' }
73
+ ];
74
+ const resourceSpecs = [
75
+ { fileName: 'products', spec: productsSpec },
76
+ { fileName: 'orders', spec: ordersSpec }
77
+ ];
78
+
79
+ const mainGenerator = new OpenAPIToConnector(productsSpec, 'test-shop-temp');
80
+ const actualOutput = mainGenerator.generateMainController(resources, resourceSpecs);
81
+
82
+ // Compare outputs (normalize line endings)
83
+ const normalizeOutput = (output: string) => output
84
+ .replace(/\r\n/g, '\n')
85
+ .trim();
86
+
87
+ expect(normalizeOutput(actualOutput)).to.equal(normalizeOutput(expectedOutput));
88
+ });
89
+
90
+ it('should generate exact expected products resource output', () => {
91
+ // Read input spec
92
+ const specPath = path.join(__dirname, 'scenarios/complex/specs/products.json');
93
+ const specContent = fs.readFileSync(specPath, 'utf-8');
94
+ const spec = OpenAPIToConnector.parseSpec(specContent);
95
+
96
+ // Read expected output
97
+ const expectedPath = path.join(__dirname, 'scenarios/complex/expected/products-resource.mts');
98
+ const expectedOutput = fs.readFileSync(expectedPath, 'utf-8');
99
+
100
+ // Generate actual output
101
+ const generator = new OpenAPIToConnector(spec, 'test-shop');
102
+ const actualOutput = generator.generateResourceClass('ProductsResource');
103
+
104
+ // Compare outputs (normalize line endings)
105
+ const normalizeOutput = (output: string) => output
106
+ .replace(/\r\n/g, '\n')
107
+ .trim();
108
+
109
+ expect(normalizeOutput(actualOutput)).to.equal(normalizeOutput(expectedOutput));
110
+ });
111
+
112
+ it('should generate exact expected orders resource output', () => {
113
+ // Read input spec
114
+ const specPath = path.join(__dirname, 'scenarios/complex/specs/orders.json');
115
+ const specContent = fs.readFileSync(specPath, 'utf-8');
116
+ const spec = OpenAPIToConnector.parseSpec(specContent);
117
+
118
+ // Read expected output
119
+ const expectedPath = path.join(__dirname, 'scenarios/complex/expected/orders-resource.mts');
120
+ const expectedOutput = fs.readFileSync(expectedPath, 'utf-8');
121
+
122
+ // Generate actual output
123
+ const generator = new OpenAPIToConnector(spec, 'test-shop');
124
+ const actualOutput = generator.generateResourceClass('OrdersResource');
125
+
126
+ // Compare outputs (normalize line endings)
127
+ const normalizeOutput = (output: string) => output
128
+ .replace(/\r\n/g, '\n')
129
+ .trim();
130
+
131
+ expect(normalizeOutput(actualOutput)).to.equal(normalizeOutput(expectedOutput));
132
+ });
133
+
134
+ it('should correctly handle TypeScript interface generation', () => {
135
+ // Read products spec
136
+ const specPath = path.join(__dirname, 'scenarios/complex/specs/products.json');
137
+ const specContent = fs.readFileSync(specPath, 'utf-8');
138
+ const spec = OpenAPIToConnector.parseSpec(specContent);
139
+
140
+ // Generate output
141
+ const generator = new OpenAPIToConnector(spec, 'test-shop');
142
+ const actualOutput = generator.generateResourceClass('ProductsResource');
143
+
144
+ // Verify TypeScript interfaces are generated correctly
145
+ expect(actualOutput).to.include('export interface Product {');
146
+ expect(actualOutput).to.include('export interface ProductList {');
147
+ expect(actualOutput).to.include('export interface CreateProductRequest {');
148
+ expect(actualOutput).to.include('export interface UpdateProductRequest {');
149
+
150
+ // Verify proper TypeScript types in method signatures
151
+ expect(actualOutput).to.include('Promise<ProductList>');
152
+ expect(actualOutput).to.include('Promise<Product>');
153
+ });
154
+
155
+ it('should correctly handle path parameters and options separation', () => {
156
+ // Read orders spec (has path parameters)
157
+ const specPath = path.join(__dirname, 'scenarios/complex/specs/orders.json');
158
+ const specContent = fs.readFileSync(specPath, 'utf-8');
159
+ const spec = OpenAPIToConnector.parseSpec(specContent);
160
+
161
+ // Generate output
162
+ const generator = new OpenAPIToConnector(spec, 'test-shop');
163
+ const actualOutput = generator.generateResourceClass('OrdersResource');
164
+
165
+ // Verify path parameters are handled correctly
166
+ expect(actualOutput).to.include('export function getOrder(this: any, orderId: string)');
167
+ expect(actualOutput).to.include('export function updateOrderStatus(this: any, orderId: string, options');
168
+ expect(actualOutput).to.include('export function cancelOrder(this: any, orderId: string)');
169
+
170
+ // Verify methods without options don't have unnecessary code
171
+ expect(actualOutput).to.match(/export function getOrder\(this: any, orderId: string\) \{/);
172
+ expect(actualOutput).to.match(/export function cancelOrder\(this: any, orderId: string\) \{/);
173
+ });
174
+
175
+ it('should correctly expose resource methods in main controller', () => {
176
+ // Read both specs
177
+ const productsSpecPath = path.join(__dirname, 'scenarios/complex/specs/products.json');
178
+ const ordersSpecPath = path.join(__dirname, 'scenarios/complex/specs/orders.json');
179
+ const productsSpecContent = fs.readFileSync(productsSpecPath, 'utf-8');
180
+ const ordersSpecContent = fs.readFileSync(ordersSpecPath, 'utf-8');
181
+
182
+ const productsSpec = OpenAPIToConnector.parseSpec(productsSpecContent);
183
+ const ordersSpec = OpenAPIToConnector.parseSpec(ordersSpecContent);
184
+
185
+ // Generate main controller
186
+ const resources = [
187
+ { className: 'ProductsResource', fileName: 'products' },
188
+ { className: 'OrdersResource', fileName: 'orders' }
189
+ ];
190
+ const resourceSpecs = [
191
+ { fileName: 'products', spec: productsSpec },
192
+ { fileName: 'orders', spec: ordersSpec }
193
+ ];
194
+
195
+ const mainGenerator = new OpenAPIToConnector(productsSpec, 'test-shop');
196
+ const actualOutput = mainGenerator.generateMainController(resources, resourceSpecs);
197
+
198
+ // Verify exposed methods exist
199
+ expect(actualOutput).to.include('async productsGetProducts(');
200
+ expect(actualOutput).to.include('async productsCreateProduct(');
201
+ expect(actualOutput).to.include('async productsGetProduct(');
202
+ expect(actualOutput).to.include('async productsUpdateProduct(');
203
+ expect(actualOutput).to.include('async productsDeleteProduct(');
204
+
205
+ expect(actualOutput).to.include('async ordersGetOrders(');
206
+ expect(actualOutput).to.include('async ordersCreateOrder(');
207
+ expect(actualOutput).to.include('async ordersGetOrder(');
208
+ expect(actualOutput).to.include('async ordersUpdateOrderStatus(');
209
+ expect(actualOutput).to.include('async ordersCancelOrder(');
210
+
211
+ // Verify resource binding
212
+ expect(actualOutput).to.include('this.bindResourceFunctions(\'products\', productsFunctions);');
213
+ expect(actualOutput).to.include('this.bindResourceFunctions(\'orders\', ordersFunctions);');
214
+ });
215
+ });
216
+
217
+ describe('Regression Tests', () => {
218
+ it('should handle OpenAPI schemas with different naming patterns', () => {
219
+ const testSpec = {
220
+ openapi: '3.0.0',
221
+ info: { title: 'Test API', version: '1.0.0' },
222
+ paths: {
223
+ '/test': {
224
+ get: {
225
+ operationId: 'testOperation',
226
+ responses: {
227
+ '200': {
228
+ description: 'Success',
229
+ content: {
230
+ 'application/json': {
231
+ schema: {
232
+ '$ref': '#/components/schemas/Complex.Name.With.Dots'
233
+ }
234
+ }
235
+ }
236
+ }
237
+ }
238
+ }
239
+ }
240
+ },
241
+ components: {
242
+ schemas: {
243
+ 'Complex.Name.With.Dots': {
244
+ type: 'object',
245
+ properties: {
246
+ id: { type: 'string' },
247
+ name: { type: 'string' }
248
+ }
249
+ }
250
+ }
251
+ }
252
+ };
253
+
254
+ const generator = new OpenAPIToConnector(testSpec, 'test');
255
+ const output = generator.generateController();
256
+
257
+ // Should generate valid TypeScript interface names
258
+ expect(output).to.include('export interface Complex_Name_With_Dots {');
259
+ expect(output).to.not.include('export interface Complex.Name.With.Dots {'); // Invalid TS
260
+ expect(output).to.include('Promise<Complex_Name_With_Dots>');
261
+ });
262
+
263
+ it('should not add options parameter to methods that don\'t need it', () => {
264
+ const simpleSpec = {
265
+ openapi: '3.0.0',
266
+ info: { title: 'Simple API', version: '1.0.0' },
267
+ paths: {
268
+ '/simple': {
269
+ get: {
270
+ operationId: 'getSimple',
271
+ responses: { '200': { description: 'Success' } }
272
+ }
273
+ }
274
+ }
275
+ };
276
+
277
+ const generator = new OpenAPIToConnector(simpleSpec, 'test');
278
+ const output = generator.generateController();
279
+
280
+ // Should not have options parameter or initialization
281
+ expect(output).to.include('async getSimple() {');
282
+ expect(output).to.not.include('options = options || {}');
283
+ expect(output).to.not.include('headers: options');
284
+ });
285
+ });
286
+ });