@aloma.io/integration-sdk 3.8.52 → 3.8.53
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 +217 -0
- package/README.md +55 -2
- package/build/cli.mjs +162 -11
- package/build/openapi-to-connector.d.mts +23 -0
- package/build/openapi-to-connector.mjs +261 -23
- package/examples/api-without-servers.json +32 -0
- package/examples/companies-resource-class.mts +310 -0
- package/examples/companies-resource.mts +310 -0
- package/examples/complete-example.sh +116 -0
- package/examples/create-hubspot-connector.sh +33 -0
- package/examples/hubspot-companies.json +1889 -0
- package/examples/hubspot-contacts.json +1919 -0
- package/examples/hubspot-controller-individual-params.mts +323 -0
- package/examples/hubspot-controller-with-implementation.mts +315 -0
- package/examples/hubspot-controller.mts +192 -0
- package/examples/hubspot-lists.json +5525 -0
- package/examples/main-controller-with-resources.mts +35 -0
- package/examples/stripe.json +182829 -0
- package/examples/utility-click.json +8992 -0
- package/package.json +1 -1
- package/src/cli.mts +195 -11
- package/src/openapi-to-connector.mts +298 -29
@@ -0,0 +1,217 @@
|
|
1
|
+
# Multi-Resource Connector Guide
|
2
|
+
|
3
|
+
This guide explains how to create and manage multi-resource connectors using the Aloma Integration SDK.
|
4
|
+
|
5
|
+
## Overview
|
6
|
+
|
7
|
+
Multi-resource connectors allow you to organize large APIs into logical resource groups (e.g., `companies`, `contacts`, `deals`) while maintaining a clean, modular architecture. Each resource is generated from its own OpenAPI specification and composed in a main controller.
|
8
|
+
|
9
|
+
## Architecture
|
10
|
+
|
11
|
+
```
|
12
|
+
src/
|
13
|
+
├── controller/
|
14
|
+
│ └── index.mts # Main controller (extends AbstractController)
|
15
|
+
└── resources/
|
16
|
+
├── companies.mts # CompaniesResource (plain class)
|
17
|
+
├── contacts.mts # ContactsResource (plain class)
|
18
|
+
└── lists.mts # ListsResource (plain class)
|
19
|
+
```
|
20
|
+
|
21
|
+
### Main Controller
|
22
|
+
- Extends `AbstractController`
|
23
|
+
- Has `private api: any` and `protected async start()`
|
24
|
+
- Composes all resources
|
25
|
+
- Initializes API client once
|
26
|
+
|
27
|
+
### Resource Classes
|
28
|
+
- Plain TypeScript classes (no inheritance)
|
29
|
+
- Receive controller reference in constructor
|
30
|
+
- Access `api` via getter: `this.controller['api']`
|
31
|
+
- Focus on their specific domain
|
32
|
+
|
33
|
+
## Creating Multi-Resource Connectors
|
34
|
+
|
35
|
+
### 1. Create from Multiple OpenAPI Specs
|
36
|
+
|
37
|
+
```bash
|
38
|
+
npx @aloma.io/integration-sdk@latest create-multi-resource "MyConnector" \
|
39
|
+
--connector-id "my-connector-123" \
|
40
|
+
--resources "CompaniesResource:companies.json,ContactsResource:contacts.json,ListsResource:lists.json" \
|
41
|
+
--base-url "https://api.example.com"
|
42
|
+
```
|
43
|
+
|
44
|
+
**Parameters:**
|
45
|
+
- `--resources`: Comma-separated list of `ClassName:specFile` pairs
|
46
|
+
- `--base-url`: API base URL (optional, extracted from first spec if not provided)
|
47
|
+
- `--no-build`: Skip dependency installation and building
|
48
|
+
|
49
|
+
### 2. Resource Specification Format
|
50
|
+
|
51
|
+
The `--resources` parameter accepts a comma-separated list in this format:
|
52
|
+
```
|
53
|
+
"ClassName1:specFile1,ClassName2:specFile2,ClassName3:specFile3"
|
54
|
+
```
|
55
|
+
|
56
|
+
**Examples:**
|
57
|
+
```bash
|
58
|
+
# HubSpot connector
|
59
|
+
--resources "CompaniesResource:hubspot-companies.json,ContactsResource:hubspot-contacts.json,ListsResource:hubspot-lists.json"
|
60
|
+
|
61
|
+
# Salesforce connector
|
62
|
+
--resources "AccountsResource:sf-accounts.json,ContactsResource:sf-contacts.json,OpportunitiesResource:sf-opportunities.json"
|
63
|
+
|
64
|
+
# Stripe connector
|
65
|
+
--resources "CustomersResource:stripe-customers.json,PaymentsResource:stripe-payments.json,SubscriptionsResource:stripe-subscriptions.json"
|
66
|
+
```
|
67
|
+
|
68
|
+
## Adding Resources to Existing Projects
|
69
|
+
|
70
|
+
### 1. Add New Resource
|
71
|
+
|
72
|
+
```bash
|
73
|
+
npx @aloma.io/integration-sdk@latest add-resource ./existing-project \
|
74
|
+
--className "DealsResource" \
|
75
|
+
--spec "deals.json"
|
76
|
+
```
|
77
|
+
|
78
|
+
This will:
|
79
|
+
- Generate the new resource class
|
80
|
+
- Save it to `src/resources/deals.mts`
|
81
|
+
- Provide instructions for updating the main controller
|
82
|
+
|
83
|
+
### 2. Update Main Controller Manually
|
84
|
+
|
85
|
+
After adding a resource, you need to update the main controller:
|
86
|
+
|
87
|
+
```typescript
|
88
|
+
// 1. Add import
|
89
|
+
import DealsResource from '../resources/deals.mjs';
|
90
|
+
|
91
|
+
// 2. Add property
|
92
|
+
deals!: DealsResource;
|
93
|
+
|
94
|
+
// 3. Add initialization in start()
|
95
|
+
this.deals = new DealsResource(this);
|
96
|
+
```
|
97
|
+
|
98
|
+
## Usage Examples
|
99
|
+
|
100
|
+
### Generated API Calls
|
101
|
+
|
102
|
+
```typescript
|
103
|
+
// Companies resource
|
104
|
+
await controller.companies.create({
|
105
|
+
body: { properties: { name: 'Acme Corp', domain: 'acme.com' } }
|
106
|
+
});
|
107
|
+
|
108
|
+
await controller.companies.getPage({
|
109
|
+
limit: 10,
|
110
|
+
properties: ['name', 'domain', 'industry']
|
111
|
+
});
|
112
|
+
|
113
|
+
await controller.companies.getById('12345', {
|
114
|
+
properties: ['name', 'email']
|
115
|
+
});
|
116
|
+
|
117
|
+
// Contacts resource
|
118
|
+
await controller.contacts.create({
|
119
|
+
body: { properties: { firstname: 'John', lastname: 'Doe', email: 'john@example.com' } }
|
120
|
+
});
|
121
|
+
|
122
|
+
await controller.contacts.getPage({
|
123
|
+
limit: 20,
|
124
|
+
properties: ['firstname', 'lastname', 'email']
|
125
|
+
});
|
126
|
+
|
127
|
+
// Lists resource
|
128
|
+
await controller.lists.getAll({ limit: 50 });
|
129
|
+
|
130
|
+
await controller.lists.create({
|
131
|
+
body: { name: 'My Contact List', processingType: 'MANUAL' }
|
132
|
+
});
|
133
|
+
```
|
134
|
+
|
135
|
+
### With Custom Headers
|
136
|
+
|
137
|
+
```typescript
|
138
|
+
await controller.companies.getPage({
|
139
|
+
limit: 10,
|
140
|
+
headers: {
|
141
|
+
'X-Request-ID': 'unique-id-123',
|
142
|
+
'X-Custom-Header': 'value'
|
143
|
+
}
|
144
|
+
});
|
145
|
+
```
|
146
|
+
|
147
|
+
## Best Practices
|
148
|
+
|
149
|
+
### 1. Resource Naming
|
150
|
+
- Use descriptive class names: `CompaniesResource`, `ContactsResource`
|
151
|
+
- File names are auto-generated: `companies.mts`, `contacts.mts`
|
152
|
+
- Property names in controller: `companies`, `contacts`
|
153
|
+
|
154
|
+
### 2. OpenAPI Specifications
|
155
|
+
- Each resource should have its own focused OpenAPI spec
|
156
|
+
- Ensure all specs use the same base URL or provide `--base-url`
|
157
|
+
- Use consistent parameter naming across specs
|
158
|
+
|
159
|
+
### 3. Error Handling
|
160
|
+
- All generated methods return promises
|
161
|
+
- API errors are handled by the underlying `api.fetch()` method
|
162
|
+
- Add custom error handling in your connector logic as needed
|
163
|
+
|
164
|
+
### 4. Type Safety
|
165
|
+
- Generated code uses `any` types for flexibility
|
166
|
+
- Consider adding TypeScript interfaces for better type safety
|
167
|
+
- Use JSDoc comments for parameter documentation
|
168
|
+
|
169
|
+
## Example: Complete HubSpot Connector
|
170
|
+
|
171
|
+
```bash
|
172
|
+
# Create the connector
|
173
|
+
npx @aloma.io/integration-sdk@latest create-multi-resource "HubSpot-v2" \
|
174
|
+
--connector-id "hubspot-123" \
|
175
|
+
--resources "CompaniesResource:examples/hubspot-companies.json,ContactsResource:examples/hubspot-contacts.json,ListsResource:examples/hubspot-lists.json" \
|
176
|
+
--base-url "https://api.hubapi.com"
|
177
|
+
|
178
|
+
# Install dependencies
|
179
|
+
cd HubSpot-v2
|
180
|
+
yarn --ignore-engines
|
181
|
+
|
182
|
+
# Build
|
183
|
+
yarn build
|
184
|
+
|
185
|
+
# Start
|
186
|
+
yarn start
|
187
|
+
```
|
188
|
+
|
189
|
+
## Troubleshooting
|
190
|
+
|
191
|
+
### Import Path Errors
|
192
|
+
If you see TypeScript errors about missing file extensions:
|
193
|
+
- Ensure you're using `.mjs` extensions in imports
|
194
|
+
- The generator handles this automatically in new projects
|
195
|
+
|
196
|
+
### Resource Not Found
|
197
|
+
If a resource property is undefined:
|
198
|
+
- Check that the resource is properly initialized in `start()`
|
199
|
+
- Verify the import path in the main controller
|
200
|
+
- Ensure the resource file was generated correctly
|
201
|
+
|
202
|
+
### Build Errors
|
203
|
+
If the project fails to build:
|
204
|
+
- Try using `--no-build` flag during creation
|
205
|
+
- Install dependencies manually: `yarn --ignore-engines`
|
206
|
+
- Check for TypeScript errors in generated files
|
207
|
+
|
208
|
+
## Migration from Single-Resource
|
209
|
+
|
210
|
+
To convert an existing single-resource connector to multi-resource:
|
211
|
+
|
212
|
+
1. Extract the relevant endpoints into separate OpenAPI specs
|
213
|
+
2. Create new resource classes using `from-openapi --resource`
|
214
|
+
3. Update the main controller to compose the resources
|
215
|
+
4. Remove the old single controller methods
|
216
|
+
|
217
|
+
This approach maintains backward compatibility while providing better organization for large APIs.
|
package/README.md
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
## Creating a new Connector
|
4
4
|
|
5
|
-
With the aloma integration SDK cli you can create a new connector in
|
5
|
+
With the aloma integration SDK cli you can create a new connector in three ways:
|
6
6
|
|
7
7
|
### 1. Create from scratch
|
8
8
|
```bash
|
@@ -16,4 +16,57 @@ npx @aloma.io/integration-sdk@latest from-openapi connectorName --connector-id 1
|
|
16
16
|
|
17
17
|
This will automatically generate a complete connector project with methods for all OpenAPI endpoints, ready for implementation.
|
18
18
|
|
19
|
-
|
19
|
+
### 3. Generate with Resource Architecture (Recommended for large APIs)
|
20
|
+
```bash
|
21
|
+
npx @aloma.io/integration-sdk@latest from-openapi connectorName \
|
22
|
+
--connector-id 1234 \
|
23
|
+
--spec api.yaml \
|
24
|
+
--out src/resources/myresource.mts \
|
25
|
+
--resource MyResource \
|
26
|
+
--no-build
|
27
|
+
```
|
28
|
+
|
29
|
+
### 4. Create Multi-Resource Connector (Recommended for complex APIs)
|
30
|
+
```bash
|
31
|
+
npx @aloma.io/integration-sdk@latest create-multi-resource "HubSpot-v2" \
|
32
|
+
--connector-id "hubspot-123" \
|
33
|
+
--resources "CompaniesResource:examples/hubspot-companies.json,ContactsResource:examples/hubspot-contacts.json,ListsResource:examples/hubspot-lists.json" \
|
34
|
+
--base-url "https://api.hubapi.com"
|
35
|
+
```
|
36
|
+
|
37
|
+
This creates a complete multi-resource connector with:
|
38
|
+
- Individual resource classes for each OpenAPI spec
|
39
|
+
- Main controller that composes all resources
|
40
|
+
- Proper TypeScript imports and architecture
|
41
|
+
|
42
|
+
```typescript
|
43
|
+
// Usage
|
44
|
+
await controller.companies.create({ body: { properties: { name: 'Acme' } } });
|
45
|
+
await controller.contacts.getPage({ limit: 10 });
|
46
|
+
await controller.lists.getAll({ limit: 50 });
|
47
|
+
```
|
48
|
+
|
49
|
+
### 5. Add Resource to Existing Project
|
50
|
+
```bash
|
51
|
+
npx @aloma.io/integration-sdk@latest add-resource ./existing-project \
|
52
|
+
--className "DealsResource" \
|
53
|
+
--spec "deals.json"
|
54
|
+
```
|
55
|
+
|
56
|
+
This adds a new resource to an existing multi-resource connector.
|
57
|
+
|
58
|
+
## 📚 Documentation
|
59
|
+
|
60
|
+
- **[OPENAPI_TO_CONNECTOR.md](./OPENAPI_TO_CONNECTOR.md)** - OpenAPI generator details
|
61
|
+
- **[MULTI_RESOURCE_GUIDE.md](./MULTI_RESOURCE_GUIDE.md)** - Complete guide for multi-resource connectors
|
62
|
+
|
63
|
+
## 🚀 Quick Examples
|
64
|
+
|
65
|
+
### HubSpot Multi-Resource Connector
|
66
|
+
```bash
|
67
|
+
# Create complete HubSpot connector with 3 resources
|
68
|
+
npx @aloma.io/integration-sdk@latest create-multi-resource "HubSpot-v2" \
|
69
|
+
--connector-id "hubspot-123" \
|
70
|
+
--resources "CompaniesResource:examples/hubspot-companies.json,ContactsResource:examples/hubspot-contacts.json,ListsResource:examples/hubspot-lists.json"
|
71
|
+
|
72
|
+
```
|
package/build/cli.mjs
CHANGED
@@ -109,6 +109,9 @@ program
|
|
109
109
|
.requiredOption('--connector-id <id>', 'id of the connector')
|
110
110
|
.requiredOption('--spec <file>', 'OpenAPI specification file (JSON or YAML)')
|
111
111
|
.option('--out <file>', 'output file path for the controller', 'src/controller/index.mts')
|
112
|
+
.option('--resource <className>', 'Generate as a resource class with the specified class name (e.g., CompaniesResource)')
|
113
|
+
.option('--multi-resource', 'Generate multiple resource files + main controller (requires multiple --spec files)')
|
114
|
+
.option('--no-build', 'Skip installing dependencies and building the project')
|
112
115
|
.action(async (name, options) => {
|
113
116
|
name = name.replace(/[\/\.]/gi, '');
|
114
117
|
if (!name)
|
@@ -123,30 +126,48 @@ program
|
|
123
126
|
console.log('Creating connector project...');
|
124
127
|
extract({ ...options, target, name });
|
125
128
|
// Generate the controller from OpenAPI spec
|
126
|
-
console.log('Generating controller from OpenAPI specification...');
|
127
129
|
const generator = new OpenAPIToConnector(spec, name);
|
128
|
-
|
130
|
+
let controllerCode;
|
131
|
+
if (options.resource) {
|
132
|
+
console.log(`Generating resource class '${options.resource}' from OpenAPI specification...`);
|
133
|
+
controllerCode = generator.generateResourceClass(options.resource);
|
134
|
+
}
|
135
|
+
else {
|
136
|
+
console.log('Generating controller from OpenAPI specification...');
|
137
|
+
controllerCode = generator.generateController();
|
138
|
+
}
|
129
139
|
// Write the generated controller
|
130
140
|
const controllerPath = `${target}/${options.out}`;
|
131
141
|
fs.mkdirSync(path.dirname(controllerPath), { recursive: true });
|
132
142
|
fs.writeFileSync(controllerPath, controllerCode);
|
133
143
|
console.log('Generating keys...');
|
134
144
|
await generateKeys({ target });
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
145
|
+
if (options.build !== false) {
|
146
|
+
console.log('Installing dependencies...');
|
147
|
+
await exec(`cd ${target}; yarn --ignore-engines`);
|
148
|
+
console.log('Building...');
|
149
|
+
await exec(`cd ${target}; yarn build`);
|
150
|
+
}
|
151
|
+
const nextSteps = options.build !== false
|
152
|
+
? `Next steps:
|
153
|
+
1.) Add the connector to a workspace
|
154
|
+
2.) Edit ./${name}/.env and insert the registration token
|
155
|
+
3.) Implement the actual API calls in each method in ${options.out}
|
156
|
+
4.) Start the connector with cd ./${name}/; yarn start`
|
157
|
+
: `Next steps:
|
158
|
+
1.) Install dependencies: cd ./${name}/ && yarn --ignore-engines
|
159
|
+
2.) Implement the actual API calls in each method in ${options.out}
|
160
|
+
3.) Build the project: yarn build
|
161
|
+
4.) Add the connector to a workspace
|
162
|
+
5.) Edit ./${name}/.env and insert the registration token
|
163
|
+
6.) Start the connector: yarn start`;
|
139
164
|
console.log(`
|
140
165
|
✅ Success! Generated connector from OpenAPI specification
|
141
166
|
📝 Connector name: ${name}
|
142
167
|
📊 Found ${generator.getOperationsCount()} operations
|
143
168
|
📄 Controller generated: ${options.out}
|
144
169
|
|
145
|
-
|
146
|
-
1.) Add the connector to a workspace
|
147
|
-
2.) Edit ./${name}/.env and insert the registration token
|
148
|
-
3.) Implement the actual API calls in each method in ${options.out}
|
149
|
-
4.) Start the connector with cd ./${name}/; yarn start`);
|
170
|
+
${nextSteps}`);
|
150
171
|
}
|
151
172
|
catch (error) {
|
152
173
|
console.error('❌ Error:', error instanceof Error ? error.message : 'Unknown error');
|
@@ -173,4 +194,134 @@ class Extractor {
|
|
173
194
|
}), { encoding: 'utf-8' });
|
174
195
|
}
|
175
196
|
}
|
197
|
+
// Multi-resource connector creation
|
198
|
+
program
|
199
|
+
.command('create-multi-resource')
|
200
|
+
.description('Create a multi-resource connector project with multiple OpenAPI specifications')
|
201
|
+
.argument('<name>', 'name of the connector project')
|
202
|
+
.requiredOption('--connector-id <id>', 'id of the connector')
|
203
|
+
.requiredOption('--resources <specs>', 'comma-separated list of "className:specFile" pairs (e.g., "CompaniesResource:companies.json,ContactsResource:contacts.json")')
|
204
|
+
.option('--base-url <url>', 'base URL for the API (if not specified, will be extracted from first OpenAPI spec)')
|
205
|
+
.option('--no-build', 'Skip installing dependencies and building the project')
|
206
|
+
.action(async (name, options) => {
|
207
|
+
name = name.replace(/[\/\.]/gi, '');
|
208
|
+
if (!name)
|
209
|
+
throw new Error('name is empty');
|
210
|
+
const target = `${process.cwd()}/${name}`;
|
211
|
+
try {
|
212
|
+
// Parse resources specification
|
213
|
+
const resourceSpecs = options.resources.split(',').map((spec) => {
|
214
|
+
const [className, specFile] = spec.split(':');
|
215
|
+
if (!className || !specFile) {
|
216
|
+
throw new Error(`Invalid resource specification: ${spec}. Expected format: "ClassName:specFile"`);
|
217
|
+
}
|
218
|
+
return { className: className.trim(), specFile: specFile.trim() };
|
219
|
+
});
|
220
|
+
console.log(`Creating multi-resource connector '${name}' with ${resourceSpecs.length} resources...`);
|
221
|
+
// Create the connector project structure
|
222
|
+
fs.mkdirSync(target);
|
223
|
+
extract({ ...options, target, name });
|
224
|
+
// Generate each resource
|
225
|
+
const resources = [];
|
226
|
+
let baseUrl = options.baseUrl;
|
227
|
+
for (const { className, specFile } of resourceSpecs) {
|
228
|
+
console.log(`Generating ${className} from ${specFile}...`);
|
229
|
+
// Read and parse the OpenAPI spec
|
230
|
+
const specContent = fs.readFileSync(specFile, 'utf-8');
|
231
|
+
const spec = OpenAPIToConnector.parseSpec(specContent);
|
232
|
+
// Extract base URL from first spec if not provided
|
233
|
+
if (!baseUrl && spec.servers && spec.servers.length > 0) {
|
234
|
+
baseUrl = spec.servers[0].url;
|
235
|
+
}
|
236
|
+
// Generate the resource class
|
237
|
+
const generator = new OpenAPIToConnector(spec, name);
|
238
|
+
const resourceCode = generator.generateResourceClass(className);
|
239
|
+
// Write the resource file
|
240
|
+
const fileName = className.toLowerCase().replace('resource', '');
|
241
|
+
const resourcePath = `${target}/src/resources/${fileName}.mts`;
|
242
|
+
fs.mkdirSync(path.dirname(resourcePath), { recursive: true });
|
243
|
+
fs.writeFileSync(resourcePath, resourceCode);
|
244
|
+
resources.push({ className, fileName });
|
245
|
+
}
|
246
|
+
// Generate the main controller
|
247
|
+
console.log('Generating main controller...');
|
248
|
+
const firstSpec = OpenAPIToConnector.parseSpec(fs.readFileSync(resourceSpecs[0].specFile, 'utf-8'));
|
249
|
+
const mainGenerator = new OpenAPIToConnector(firstSpec, name);
|
250
|
+
const mainControllerCode = mainGenerator.generateMainController(resources);
|
251
|
+
// Write the main controller
|
252
|
+
const controllerPath = `${target}/src/controller/index.mts`;
|
253
|
+
fs.writeFileSync(controllerPath, mainControllerCode);
|
254
|
+
console.log('Generating keys...');
|
255
|
+
await generateKeys({ target });
|
256
|
+
if (options.build !== false) {
|
257
|
+
console.log('Installing dependencies...');
|
258
|
+
await exec(`cd "${target}"; yarn --ignore-engines`);
|
259
|
+
console.log('Building...');
|
260
|
+
await exec(`cd "${target}"; yarn build`);
|
261
|
+
}
|
262
|
+
const nextSteps = options.build !== false
|
263
|
+
? `Next steps:
|
264
|
+
1.) Add the connector to a workspace
|
265
|
+
2.) Edit ./${name}/.env and insert the registration token
|
266
|
+
3.) Start the connector with cd ./${name}/; yarn start`
|
267
|
+
: `Next steps:
|
268
|
+
1.) Install dependencies: cd ./${name}/ && yarn --ignore-engines
|
269
|
+
2.) Build the project: yarn build
|
270
|
+
3.) Add the connector to a workspace
|
271
|
+
4.) Edit ./${name}/.env and insert the registration token
|
272
|
+
5.) Start the connector with yarn start`;
|
273
|
+
console.log(`\n✅ Multi-resource connector created successfully!
|
274
|
+
|
275
|
+
Generated resources:
|
276
|
+
${resources.map((r) => `- ${r.className} (${r.fileName}.mts)`).join('\n')}
|
277
|
+
|
278
|
+
Main controller: src/controller/index.mts
|
279
|
+
${nextSteps}`);
|
280
|
+
}
|
281
|
+
catch (error) {
|
282
|
+
console.error('Error creating multi-resource connector:', error.message);
|
283
|
+
process.exit(1);
|
284
|
+
}
|
285
|
+
});
|
286
|
+
// Add resource to existing project
|
287
|
+
program
|
288
|
+
.command('add-resource')
|
289
|
+
.description('Add a new resource to an existing multi-resource connector')
|
290
|
+
.argument('<projectPath>', 'path to the existing connector project')
|
291
|
+
.requiredOption('--className <name>', 'class name for the resource (e.g., DealsResource)')
|
292
|
+
.requiredOption('--spec <file>', 'OpenAPI specification file for the new resource')
|
293
|
+
.option('--no-build', 'Skip building the project after adding the resource')
|
294
|
+
.action(async (projectPath, options) => {
|
295
|
+
const target = path.resolve(projectPath);
|
296
|
+
if (!fs.existsSync(target)) {
|
297
|
+
throw new Error(`Project path does not exist: ${target}`);
|
298
|
+
}
|
299
|
+
try {
|
300
|
+
console.log(`Adding ${options.className} resource to existing project...`);
|
301
|
+
// Read and parse the OpenAPI spec
|
302
|
+
const specContent = fs.readFileSync(options.spec, 'utf-8');
|
303
|
+
const spec = OpenAPIToConnector.parseSpec(specContent);
|
304
|
+
// Generate the resource class
|
305
|
+
const generator = new OpenAPIToConnector(spec, 'Resource');
|
306
|
+
const resourceCode = generator.generateResourceClass(options.className);
|
307
|
+
// Write the resource file
|
308
|
+
const fileName = options.className.toLowerCase().replace('resource', '');
|
309
|
+
const resourcePath = `${target}/src/resources/${fileName}.mts`;
|
310
|
+
fs.mkdirSync(path.dirname(resourcePath), { recursive: true });
|
311
|
+
fs.writeFileSync(resourcePath, resourceCode);
|
312
|
+
console.log(`✅ Resource ${options.className} added successfully at ${resourcePath}`);
|
313
|
+
console.log('\n⚠️ You need to manually update the main controller to include this new resource:');
|
314
|
+
console.log(`1.) Add import: import ${options.className} from '../resources/${fileName}.mjs';`);
|
315
|
+
console.log(`2.) Add property: ${fileName}!: ${options.className};`);
|
316
|
+
console.log(`3.) Add initialization in start(): this.${fileName} = new ${options.className}(this);`);
|
317
|
+
if (options.build !== false) {
|
318
|
+
console.log('\nBuilding project...');
|
319
|
+
await exec(`cd "${target}"; yarn build`);
|
320
|
+
}
|
321
|
+
}
|
322
|
+
catch (error) {
|
323
|
+
console.error('Error adding resource:', error.message);
|
324
|
+
process.exit(1);
|
325
|
+
}
|
326
|
+
});
|
176
327
|
program.parse();
|
@@ -28,6 +28,29 @@ export declare class OpenAPIToConnector {
|
|
28
28
|
* Get the number of operations in the OpenAPI spec
|
29
29
|
*/
|
30
30
|
getOperationsCount(): number;
|
31
|
+
/**
|
32
|
+
* Generate method signature with options object
|
33
|
+
*/
|
34
|
+
private generateMethodSignature;
|
35
|
+
/**
|
36
|
+
* Generate method implementation code
|
37
|
+
*/
|
38
|
+
private generateMethodImplementation;
|
39
|
+
/**
|
40
|
+
* Generate proper import paths with .mjs extensions for TypeScript module resolution
|
41
|
+
*/
|
42
|
+
private generateImportPath;
|
43
|
+
/**
|
44
|
+
* Generate a resource class (does NOT extend AbstractController, receives controller reference)
|
45
|
+
*/
|
46
|
+
generateResourceClass(className: string): string;
|
47
|
+
/**
|
48
|
+
* Generate a main controller that composes multiple resources
|
49
|
+
*/
|
50
|
+
generateMainController(resources: Array<{
|
51
|
+
className: string;
|
52
|
+
fileName: string;
|
53
|
+
}>): string;
|
31
54
|
/**
|
32
55
|
* Generate the connector controller code
|
33
56
|
*/
|