@gugananuvem/aws-local-simulator 1.0.10 → 1.0.12

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 (44) hide show
  1. package/README.md +257 -193
  2. package/bin/aws-local-simulator.js +62 -62
  3. package/package.json +2 -2
  4. package/src/config/config-loader.js +114 -112
  5. package/src/config/default-config.js +67 -65
  6. package/src/config/env-loader.js +68 -68
  7. package/src/index.js +130 -130
  8. package/src/index.mjs +123 -123
  9. package/src/server.js +223 -222
  10. package/src/services/apigateway/index.js +68 -66
  11. package/src/services/apigateway/server.js +487 -434
  12. package/src/services/apigateway/simulator.js +1251 -1251
  13. package/src/services/cognito/index.js +65 -65
  14. package/src/services/cognito/server.js +279 -228
  15. package/src/services/cognito/simulator.js +1114 -847
  16. package/src/services/dynamodb/index.js +70 -70
  17. package/src/services/dynamodb/server.js +121 -121
  18. package/src/services/dynamodb/simulator.js +620 -614
  19. package/src/services/ecs/index.js +65 -65
  20. package/src/services/ecs/server.js +233 -233
  21. package/src/services/ecs/simulator.js +844 -844
  22. package/src/services/eventbridge/index.js +84 -84
  23. package/src/services/index.js +18 -18
  24. package/src/services/lambda/handler-loader.js +183 -172
  25. package/src/services/lambda/index.js +73 -72
  26. package/src/services/lambda/route-registry.js +274 -274
  27. package/src/services/lambda/server.js +145 -152
  28. package/src/services/lambda/simulator.js +172 -285
  29. package/src/services/s3/index.js +69 -69
  30. package/src/services/s3/server.js +238 -238
  31. package/src/services/s3/simulator.js +740 -740
  32. package/src/services/sns/index.js +75 -75
  33. package/src/services/sqs/index.js +95 -95
  34. package/src/services/sqs/server.js +345 -273
  35. package/src/services/sqs/simulator.js +441 -660
  36. package/src/services/sts/index.js +37 -0
  37. package/src/services/sts/server.js +142 -0
  38. package/src/services/sts/simulator.js +69 -0
  39. package/src/template/aws-config-template.js +87 -87
  40. package/src/template/aws-config-template.mjs +90 -90
  41. package/src/template/config-template.json +203 -203
  42. package/src/utils/aws-config.js +91 -91
  43. package/src/utils/local-store.js +67 -67
  44. package/src/utils/logger.js +59 -59
@@ -1,435 +1,488 @@
1
- /**
2
- * API Gateway Server - Servidor HTTP para API Gateway
3
- */
4
-
5
- const express = require('express');
6
- const cors = require('cors');
7
- const logger = require('../../utils/logger');
8
-
9
- class APIGatewayServer {
10
- constructor(port, config) {
11
- this.port = port;
12
- this.config = config;
13
- this.app = express();
14
- this.simulator = null;
15
- this.server = null;
16
- this.setupMiddlewares();
17
- }
18
-
19
- setupMiddlewares() {
20
- this.app.use(express.json({ limit: '10mb' }));
21
- this.app.use(express.urlencoded({ extended: true, limit: '10mb' }));
22
- this.app.use(cors());
23
-
24
- if (logger.currentLogLevel === 'verboso') {
25
- this.app.use((req, res, next) => {
26
- const start = Date.now();
27
- res.on('finish', () => {
28
- const duration = Date.now() - start;
29
- logger.verboso(`API Gateway: ${req.method} ${req.path} - ${duration}ms`);
30
- });
31
- next();
32
- });
33
- }
34
- }
35
-
36
- async initialize() {
37
- this.setupRoutes();
38
- this.setupProxyRoutes();
39
- logger.debug('API Gateway Server inicializado');
40
- }
41
-
42
- setupRoutes() {
43
- // Health check
44
- this.app.get('/health', (req, res) => {
45
- res.json({
46
- status: 'healthy',
47
- service: 'apigateway-simulator',
48
- version: '1.0.0'
49
- });
50
- });
51
-
52
- // Control Plane - REST API
53
- this.app.post('/restapis', async (req, res) => {
54
- try {
55
- const result = this.simulator.createRestApi(req.body);
56
- res.json(result);
57
- } catch (error) {
58
- res.status(400).json({ error: error.message });
59
- }
60
- });
61
-
62
- this.app.get('/restapis', (req, res) => {
63
- const result = this.simulator.getRestApis();
64
- res.json(result);
65
- });
66
-
67
- this.app.get('/restapis/:apiId', (req, res) => {
68
- try {
69
- const result = this.simulator.getRestApi({ restApiId: req.params.apiId });
70
- res.json(result);
71
- } catch (error) {
72
- res.status(404).json({ error: error.message });
73
- }
74
- });
75
-
76
- this.app.delete('/restapis/:apiId', (req, res) => {
77
- try {
78
- this.simulator.deleteRestApi({ restApiId: req.params.apiId });
79
- res.json({});
80
- } catch (error) {
81
- res.status(404).json({ error: error.message });
82
- }
83
- });
84
-
85
- // Resources
86
- this.app.get('/restapis/:apiId/resources', (req, res) => {
87
- try {
88
- const result = this.simulator.getResources({ restApiId: req.params.apiId });
89
- res.json(result);
90
- } catch (error) {
91
- res.status(404).json({ error: error.message });
92
- }
93
- });
94
-
95
- this.app.post('/restapis/:apiId/resources', (req, res) => {
96
- try {
97
- const result = this.simulator.createResource({
98
- ...req.body,
99
- restApiId: req.params.apiId
100
- });
101
- res.json(result);
102
- } catch (error) {
103
- res.status(400).json({ error: error.message });
104
- }
105
- });
106
-
107
- this.app.delete('/restapis/:apiId/resources/:resourceId', (req, res) => {
108
- try {
109
- this.simulator.deleteResource({
110
- restApiId: req.params.apiId,
111
- resourceId: req.params.resourceId
112
- });
113
- res.json({});
114
- } catch (error) {
115
- res.status(400).json({ error: error.message });
116
- }
117
- });
118
-
119
- // Methods
120
- this.app.put('/restapis/:apiId/resources/:resourceId/methods/:method', (req, res) => {
121
- try {
122
- const result = this.simulator.putMethod({
123
- ...req.body,
124
- restApiId: req.params.apiId,
125
- resourceId: req.params.resourceId,
126
- httpMethod: req.params.method
127
- });
128
- res.json(result);
129
- } catch (error) {
130
- res.status(400).json({ error: error.message });
131
- }
132
- });
133
-
134
- this.app.get('/restapis/:apiId/resources/:resourceId/methods/:method', (req, res) => {
135
- try {
136
- const result = this.simulator.getMethod({
137
- restApiId: req.params.apiId,
138
- resourceId: req.params.resourceId,
139
- httpMethod: req.params.method
140
- });
141
- res.json(result);
142
- } catch (error) {
143
- res.status(404).json({ error: error.message });
144
- }
145
- });
146
-
147
- this.app.delete('/restapis/:apiId/resources/:resourceId/methods/:method', (req, res) => {
148
- try {
149
- this.simulator.deleteMethod({
150
- restApiId: req.params.apiId,
151
- resourceId: req.params.resourceId,
152
- httpMethod: req.params.method
153
- });
154
- res.json({});
155
- } catch (error) {
156
- res.status(404).json({ error: error.message });
157
- }
158
- });
159
-
160
- // Integrations
161
- this.app.put('/restapis/:apiId/resources/:resourceId/methods/:method/integration', (req, res) => {
162
- try {
163
- const result = this.simulator.putIntegration({
164
- ...req.body,
165
- restApiId: req.params.apiId,
166
- resourceId: req.params.resourceId,
167
- httpMethod: req.params.method
168
- });
169
- res.json(result);
170
- } catch (error) {
171
- res.status(400).json({ error: error.message });
172
- }
173
- });
174
-
175
- this.app.get('/restapis/:apiId/resources/:resourceId/methods/:method/integration', (req, res) => {
176
- try {
177
- const result = this.simulator.getIntegration({
178
- restApiId: req.params.apiId,
179
- resourceId: req.params.resourceId,
180
- httpMethod: req.params.method
181
- });
182
- res.json(result);
183
- } catch (error) {
184
- res.status(404).json({ error: error.message });
185
- }
186
- });
187
-
188
- this.app.delete('/restapis/:apiId/resources/:resourceId/methods/:method/integration', (req, res) => {
189
- try {
190
- this.simulator.deleteIntegration({
191
- restApiId: req.params.apiId,
192
- resourceId: req.params.resourceId,
193
- httpMethod: req.params.method
194
- });
195
- res.json({});
196
- } catch (error) {
197
- res.status(404).json({ error: error.message });
198
- }
199
- });
200
-
201
- // Deployments
202
- this.app.post('/restapis/:apiId/deployments', (req, res) => {
203
- try {
204
- const result = this.simulator.createDeployment({
205
- ...req.body,
206
- restApiId: req.params.apiId
207
- });
208
- res.json(result);
209
- } catch (error) {
210
- res.status(400).json({ error: error.message });
211
- }
212
- });
213
-
214
- // Stages
215
- this.app.post('/restapis/:apiId/stages', (req, res) => {
216
- try {
217
- const result = this.simulator.createStage({
218
- ...req.body,
219
- restApiId: req.params.apiId
220
- });
221
- res.json(result);
222
- } catch (error) {
223
- res.status(400).json({ error: error.message });
224
- }
225
- });
226
-
227
- this.app.get('/restapis/:apiId/stages/:stageName', (req, res) => {
228
- try {
229
- const result = this.simulator.getStage({
230
- restApiId: req.params.apiId,
231
- stageName: req.params.stageName
232
- });
233
- res.json(result);
234
- } catch (error) {
235
- res.status(404).json({ error: error.message });
236
- }
237
- });
238
-
239
- this.app.patch('/restapis/:apiId/stages/:stageName', (req, res) => {
240
- try {
241
- const result = this.simulator.updateStage({
242
- ...req.body,
243
- restApiId: req.params.apiId,
244
- stageName: req.params.stageName
245
- });
246
- res.json(result);
247
- } catch (error) {
248
- res.status(400).json({ error: error.message });
249
- }
250
- });
251
-
252
- this.app.delete('/restapis/:apiId/stages/:stageName', (req, res) => {
253
- try {
254
- this.simulator.deleteStage({
255
- restApiId: req.params.apiId,
256
- stageName: req.params.stageName
257
- });
258
- res.json({});
259
- } catch (error) {
260
- res.status(404).json({ error: error.message });
261
- }
262
- });
263
-
264
- // API Keys
265
- this.app.post('/apikeys', (req, res) => {
266
- try {
267
- const result = this.simulator.createApiKey(req.body);
268
- res.json(result);
269
- } catch (error) {
270
- res.status(400).json({ error: error.message });
271
- }
272
- });
273
-
274
- this.app.get('/apikeys', (req, res) => {
275
- const result = this.simulator.getApiKeys(req.query);
276
- res.json(result);
277
- });
278
-
279
- // Usage Plans
280
- this.app.post('/usageplans', (req, res) => {
281
- try {
282
- const result = this.simulator.createUsagePlan(req.body);
283
- res.json(result);
284
- } catch (error) {
285
- res.status(400).json({ error: error.message });
286
- }
287
- });
288
-
289
- // HTTP APIs
290
- this.app.post('/httpapis', (req, res) => {
291
- try {
292
- const result = this.simulator.createHttpApi(req.body);
293
- res.json(result);
294
- } catch (error) {
295
- res.status(400).json({ error: error.message });
296
- }
297
- });
298
-
299
- this.app.post('/httpapis/:apiId/routes', (req, res) => {
300
- try {
301
- const result = this.simulator.createRoute({
302
- ...req.body,
303
- apiId: req.params.apiId
304
- });
305
- res.json(result);
306
- } catch (error) {
307
- res.status(400).json({ error: error.message });
308
- }
309
- });
310
-
311
- // Admin endpoints
312
- this.setupAdminRoutes();
313
- }
314
-
315
- setupProxyRoutes() {
316
- // Proxy para execução das APIs
317
- this.app.all('/:apiId/:stageName/*', async (req, res) => {
318
- const apiId = req.params.apiId;
319
- const stageName = req.params.stageName;
320
- const path = '/' + (req.params[0] || '');
321
-
322
- logger.debug(`🌐 Executando: ${req.method} ${path} (${apiId}/${stageName})`);
323
-
324
- try {
325
- const result = await this.simulator.executeRequest(
326
- apiId,
327
- stageName,
328
- req.method,
329
- path,
330
- req.headers,
331
- req.body,
332
- req.query
333
- );
334
-
335
- res.status(result.statusCode);
336
-
337
- if (result.headers) {
338
- Object.entries(result.headers).forEach(([key, value]) => {
339
- res.set(key, value);
340
- });
341
- }
342
-
343
- res.send(result.body);
344
- } catch (error) {
345
- logger.error('Error executing request:', error);
346
- res.status(500).json({ error: error.message });
347
- }
348
- });
349
- }
350
-
351
- setupAdminRoutes() {
352
- this.app.get('/__admin/apis', (req, res) => {
353
- res.json({
354
- totalApis: this.simulator.getAPIsCount(),
355
- totalDeployments: this.simulator.getDeploymentsCount(),
356
- totalStages: this.simulator.getStagesCount(),
357
- totalResources: this.simulator.getResourcesCount()
358
- });
359
- });
360
-
361
- this.app.get('/__admin/apis/:apiId', (req, res) => {
362
- const api = this.simulator.apis.get(req.params.apiId);
363
- if (api) {
364
- res.json({
365
- id: api.id,
366
- name: api.name,
367
- resources: Array.from(api.resources.keys()),
368
- stages: Array.from(api.stages.keys()),
369
- deployments: Array.from(api.deployments.keys())
370
- });
371
- } else {
372
- res.status(404).json({ error: 'API not found' });
373
- }
374
- });
375
-
376
- this.app.get('/__admin/apikeys', (req, res) => {
377
- const keys = Array.from(this.simulator.apiKeys.values()).map(k => ({
378
- id: k.id,
379
- name: k.name,
380
- enabled: k.enabled,
381
- createdDate: k.createdDate
382
- }));
383
- res.json(keys);
384
- });
385
-
386
- this.app.get('/__admin/usageplans', (req, res) => {
387
- const plans = Array.from(this.simulator.usagePlans.values());
388
- res.json(plans);
389
- });
390
- }
391
-
392
- start() {
393
- return new Promise((resolve) => {
394
- this.server = this.app.listen(this.port, () => {
395
- logger.info(`🌐 API Gateway rodando em http://localhost:${this.port}`);
396
- this.printInfo();
397
- resolve();
398
- });
399
- });
400
- }
401
-
402
- printInfo() {
403
- logger.info('\n📡 API Gateway Endpoints:');
404
- logger.info(` Control Plane: http://localhost:${this.port}/restapis`);
405
- logger.info(` Execute APIs: http://localhost:${this.port}/{apiId}/{stageName}/{path}`);
406
- logger.info('\n📚 Admin Endpoints:');
407
- logger.info(` GET http://localhost:${this.port}/__admin/apis`);
408
- logger.info(` GET http://localhost:${this.port}/__admin/apikeys`);
409
- logger.info(` GET http://localhost:${this.port}/__admin/usageplans`);
410
- }
411
-
412
- stop() {
413
- return new Promise((resolve) => {
414
- if (this.server) {
415
- this.server.close(() => resolve());
416
- } else {
417
- resolve();
418
- }
419
- });
420
- }
421
-
422
- getStatus() {
423
- return {
424
- running: !!this.server,
425
- port: this.port,
426
- endpoint: `http://localhost:${this.port}`,
427
- apisCount: this.simulator?.getAPIsCount() || 0,
428
- deploymentsCount: this.simulator?.getDeploymentsCount() || 0,
429
- stagesCount: this.simulator?.getStagesCount() || 0,
430
- resourcesCount: this.simulator?.getResourcesCount() || 0
431
- };
432
- }
433
- }
434
-
1
+ /**
2
+ * API Gateway Server - Servidor HTTP para API Gateway
3
+ */
4
+
5
+ const express = require('express');
6
+ const cors = require('cors');
7
+ const logger = require('../../utils/logger');
8
+
9
+ class APIGatewayServer {
10
+ constructor(port, config) {
11
+ this.port = port;
12
+ this.config = config;
13
+ this.app = express();
14
+ this.simulator = null;
15
+ this.server = null;
16
+ this.setupMiddlewares();
17
+ }
18
+
19
+ setupMiddlewares() {
20
+ this.app.use(express.json({ limit: '10mb' }));
21
+ this.app.use(express.urlencoded({ extended: true, limit: '10mb' }));
22
+ this.app.use(cors());
23
+
24
+ if (logger.currentLogLevel === 'verboso') {
25
+ this.app.use((req, res, next) => {
26
+ const start = Date.now();
27
+ res.on('finish', () => {
28
+ const duration = Date.now() - start;
29
+ logger.verboso(`API Gateway: ${req.method} ${req.path} - ${duration}ms`);
30
+ });
31
+ next();
32
+ });
33
+ }
34
+ }
35
+
36
+ async initialize() {
37
+ this.setupRoutes();
38
+ this.setupConfigRoutes();
39
+ this.setupProxyRoutes();
40
+ logger.debug('API Gateway Server inicializado');
41
+ }
42
+
43
+ setupConfigRoutes() {
44
+ // Register routes from aws-local-simulator.json config directly
45
+ const apis = this.config.apigateway?.apis || [];
46
+ for (const api of apis) {
47
+ for (const endpoint of (api.endpoints || [])) {
48
+ const { path, method, lambdaName, integrationType } = endpoint;
49
+ if (!path || !method) continue;
50
+
51
+ const expressPath = path.replace(/\{([^}]+)\}/g, ':$1');
52
+ const httpMethod = method.toLowerCase();
53
+
54
+ logger.debug(`📡 Registrando rota: ${method} ${path} -> ${lambdaName}`);
55
+
56
+ this.app[httpMethod](expressPath, async (req, res) => {
57
+ try {
58
+ const lambdaService = this.lambdaService;
59
+ if (!lambdaService) {
60
+ return res.status(500).json({ error: 'Lambda service not available' });
61
+ }
62
+
63
+ const event = {
64
+ httpMethod: req.method,
65
+ path: req.path,
66
+ headers: req.headers,
67
+ queryStringParameters: Object.keys(req.query).length ? req.query : null,
68
+ pathParameters: Object.keys(req.params).length ? req.params : null,
69
+ body: req.body ? JSON.stringify(req.body) : null,
70
+ isBase64Encoded: false,
71
+ requestContext: {
72
+ path: req.path,
73
+ stage: 'local',
74
+ requestId: Math.random().toString(36).substring(7),
75
+ identity: { sourceIp: req.ip }
76
+ }
77
+ };
78
+
79
+ const result = await lambdaService.simulator.invoke(lambdaName, event);
80
+ const payload = result.Payload || {};
81
+ const statusCode = payload.statusCode || 200;
82
+ const headers = payload.headers || { 'Content-Type': 'application/json' };
83
+ const body = payload.body;
84
+
85
+ res.status(statusCode).set(headers).send(body);
86
+ } catch (err) {
87
+ logger.error(`Lambda invoke error (${lambdaName}):`, err);
88
+ res.status(500).json({ error: err.message });
89
+ }
90
+ });
91
+ }
92
+ }
93
+ }
94
+
95
+ setupRoutes() {
96
+ // Health check
97
+ this.app.get('/health', (req, res) => {
98
+ res.json({
99
+ status: 'healthy',
100
+ service: 'apigateway-simulator',
101
+ version: '1.0.0'
102
+ });
103
+ });
104
+
105
+ // Control Plane - REST API
106
+ this.app.post('/restapis', async (req, res) => {
107
+ try {
108
+ const result = this.simulator.createRestApi(req.body);
109
+ res.json(result);
110
+ } catch (error) {
111
+ res.status(400).json({ error: error.message });
112
+ }
113
+ });
114
+
115
+ this.app.get('/restapis', (req, res) => {
116
+ const result = this.simulator.getRestApis();
117
+ res.json(result);
118
+ });
119
+
120
+ this.app.get('/restapis/:apiId', (req, res) => {
121
+ try {
122
+ const result = this.simulator.getRestApi({ restApiId: req.params.apiId });
123
+ res.json(result);
124
+ } catch (error) {
125
+ res.status(404).json({ error: error.message });
126
+ }
127
+ });
128
+
129
+ this.app.delete('/restapis/:apiId', (req, res) => {
130
+ try {
131
+ this.simulator.deleteRestApi({ restApiId: req.params.apiId });
132
+ res.json({});
133
+ } catch (error) {
134
+ res.status(404).json({ error: error.message });
135
+ }
136
+ });
137
+
138
+ // Resources
139
+ this.app.get('/restapis/:apiId/resources', (req, res) => {
140
+ try {
141
+ const result = this.simulator.getResources({ restApiId: req.params.apiId });
142
+ res.json(result);
143
+ } catch (error) {
144
+ res.status(404).json({ error: error.message });
145
+ }
146
+ });
147
+
148
+ this.app.post('/restapis/:apiId/resources', (req, res) => {
149
+ try {
150
+ const result = this.simulator.createResource({
151
+ ...req.body,
152
+ restApiId: req.params.apiId
153
+ });
154
+ res.json(result);
155
+ } catch (error) {
156
+ res.status(400).json({ error: error.message });
157
+ }
158
+ });
159
+
160
+ this.app.delete('/restapis/:apiId/resources/:resourceId', (req, res) => {
161
+ try {
162
+ this.simulator.deleteResource({
163
+ restApiId: req.params.apiId,
164
+ resourceId: req.params.resourceId
165
+ });
166
+ res.json({});
167
+ } catch (error) {
168
+ res.status(400).json({ error: error.message });
169
+ }
170
+ });
171
+
172
+ // Methods
173
+ this.app.put('/restapis/:apiId/resources/:resourceId/methods/:method', (req, res) => {
174
+ try {
175
+ const result = this.simulator.putMethod({
176
+ ...req.body,
177
+ restApiId: req.params.apiId,
178
+ resourceId: req.params.resourceId,
179
+ httpMethod: req.params.method
180
+ });
181
+ res.json(result);
182
+ } catch (error) {
183
+ res.status(400).json({ error: error.message });
184
+ }
185
+ });
186
+
187
+ this.app.get('/restapis/:apiId/resources/:resourceId/methods/:method', (req, res) => {
188
+ try {
189
+ const result = this.simulator.getMethod({
190
+ restApiId: req.params.apiId,
191
+ resourceId: req.params.resourceId,
192
+ httpMethod: req.params.method
193
+ });
194
+ res.json(result);
195
+ } catch (error) {
196
+ res.status(404).json({ error: error.message });
197
+ }
198
+ });
199
+
200
+ this.app.delete('/restapis/:apiId/resources/:resourceId/methods/:method', (req, res) => {
201
+ try {
202
+ this.simulator.deleteMethod({
203
+ restApiId: req.params.apiId,
204
+ resourceId: req.params.resourceId,
205
+ httpMethod: req.params.method
206
+ });
207
+ res.json({});
208
+ } catch (error) {
209
+ res.status(404).json({ error: error.message });
210
+ }
211
+ });
212
+
213
+ // Integrations
214
+ this.app.put('/restapis/:apiId/resources/:resourceId/methods/:method/integration', (req, res) => {
215
+ try {
216
+ const result = this.simulator.putIntegration({
217
+ ...req.body,
218
+ restApiId: req.params.apiId,
219
+ resourceId: req.params.resourceId,
220
+ httpMethod: req.params.method
221
+ });
222
+ res.json(result);
223
+ } catch (error) {
224
+ res.status(400).json({ error: error.message });
225
+ }
226
+ });
227
+
228
+ this.app.get('/restapis/:apiId/resources/:resourceId/methods/:method/integration', (req, res) => {
229
+ try {
230
+ const result = this.simulator.getIntegration({
231
+ restApiId: req.params.apiId,
232
+ resourceId: req.params.resourceId,
233
+ httpMethod: req.params.method
234
+ });
235
+ res.json(result);
236
+ } catch (error) {
237
+ res.status(404).json({ error: error.message });
238
+ }
239
+ });
240
+
241
+ this.app.delete('/restapis/:apiId/resources/:resourceId/methods/:method/integration', (req, res) => {
242
+ try {
243
+ this.simulator.deleteIntegration({
244
+ restApiId: req.params.apiId,
245
+ resourceId: req.params.resourceId,
246
+ httpMethod: req.params.method
247
+ });
248
+ res.json({});
249
+ } catch (error) {
250
+ res.status(404).json({ error: error.message });
251
+ }
252
+ });
253
+
254
+ // Deployments
255
+ this.app.post('/restapis/:apiId/deployments', (req, res) => {
256
+ try {
257
+ const result = this.simulator.createDeployment({
258
+ ...req.body,
259
+ restApiId: req.params.apiId
260
+ });
261
+ res.json(result);
262
+ } catch (error) {
263
+ res.status(400).json({ error: error.message });
264
+ }
265
+ });
266
+
267
+ // Stages
268
+ this.app.post('/restapis/:apiId/stages', (req, res) => {
269
+ try {
270
+ const result = this.simulator.createStage({
271
+ ...req.body,
272
+ restApiId: req.params.apiId
273
+ });
274
+ res.json(result);
275
+ } catch (error) {
276
+ res.status(400).json({ error: error.message });
277
+ }
278
+ });
279
+
280
+ this.app.get('/restapis/:apiId/stages/:stageName', (req, res) => {
281
+ try {
282
+ const result = this.simulator.getStage({
283
+ restApiId: req.params.apiId,
284
+ stageName: req.params.stageName
285
+ });
286
+ res.json(result);
287
+ } catch (error) {
288
+ res.status(404).json({ error: error.message });
289
+ }
290
+ });
291
+
292
+ this.app.patch('/restapis/:apiId/stages/:stageName', (req, res) => {
293
+ try {
294
+ const result = this.simulator.updateStage({
295
+ ...req.body,
296
+ restApiId: req.params.apiId,
297
+ stageName: req.params.stageName
298
+ });
299
+ res.json(result);
300
+ } catch (error) {
301
+ res.status(400).json({ error: error.message });
302
+ }
303
+ });
304
+
305
+ this.app.delete('/restapis/:apiId/stages/:stageName', (req, res) => {
306
+ try {
307
+ this.simulator.deleteStage({
308
+ restApiId: req.params.apiId,
309
+ stageName: req.params.stageName
310
+ });
311
+ res.json({});
312
+ } catch (error) {
313
+ res.status(404).json({ error: error.message });
314
+ }
315
+ });
316
+
317
+ // API Keys
318
+ this.app.post('/apikeys', (req, res) => {
319
+ try {
320
+ const result = this.simulator.createApiKey(req.body);
321
+ res.json(result);
322
+ } catch (error) {
323
+ res.status(400).json({ error: error.message });
324
+ }
325
+ });
326
+
327
+ this.app.get('/apikeys', (req, res) => {
328
+ const result = this.simulator.getApiKeys(req.query);
329
+ res.json(result);
330
+ });
331
+
332
+ // Usage Plans
333
+ this.app.post('/usageplans', (req, res) => {
334
+ try {
335
+ const result = this.simulator.createUsagePlan(req.body);
336
+ res.json(result);
337
+ } catch (error) {
338
+ res.status(400).json({ error: error.message });
339
+ }
340
+ });
341
+
342
+ // HTTP APIs
343
+ this.app.post('/httpapis', (req, res) => {
344
+ try {
345
+ const result = this.simulator.createHttpApi(req.body);
346
+ res.json(result);
347
+ } catch (error) {
348
+ res.status(400).json({ error: error.message });
349
+ }
350
+ });
351
+
352
+ this.app.post('/httpapis/:apiId/routes', (req, res) => {
353
+ try {
354
+ const result = this.simulator.createRoute({
355
+ ...req.body,
356
+ apiId: req.params.apiId
357
+ });
358
+ res.json(result);
359
+ } catch (error) {
360
+ res.status(400).json({ error: error.message });
361
+ }
362
+ });
363
+
364
+ // Admin endpoints
365
+ this.setupAdminRoutes();
366
+ }
367
+
368
+ setupProxyRoutes() {
369
+ // Proxy para execução das APIs
370
+ this.app.all('/:apiId/:stageName/*', async (req, res) => {
371
+ const apiId = req.params.apiId;
372
+ const stageName = req.params.stageName;
373
+ const path = '/' + (req.params[0] || '');
374
+
375
+ logger.debug(`🌐 Executando: ${req.method} ${path} (${apiId}/${stageName})`);
376
+
377
+ try {
378
+ const result = await this.simulator.executeRequest(
379
+ apiId,
380
+ stageName,
381
+ req.method,
382
+ path,
383
+ req.headers,
384
+ req.body,
385
+ req.query
386
+ );
387
+
388
+ res.status(result.statusCode);
389
+
390
+ if (result.headers) {
391
+ Object.entries(result.headers).forEach(([key, value]) => {
392
+ res.set(key, value);
393
+ });
394
+ }
395
+
396
+ res.send(result.body);
397
+ } catch (error) {
398
+ logger.error('Error executing request:', error);
399
+ res.status(500).json({ error: error.message });
400
+ }
401
+ });
402
+ }
403
+
404
+ setupAdminRoutes() {
405
+ this.app.get('/__admin/apis', (req, res) => {
406
+ res.json({
407
+ totalApis: this.simulator.getAPIsCount(),
408
+ totalDeployments: this.simulator.getDeploymentsCount(),
409
+ totalStages: this.simulator.getStagesCount(),
410
+ totalResources: this.simulator.getResourcesCount()
411
+ });
412
+ });
413
+
414
+ this.app.get('/__admin/apis/:apiId', (req, res) => {
415
+ const api = this.simulator.apis.get(req.params.apiId);
416
+ if (api) {
417
+ res.json({
418
+ id: api.id,
419
+ name: api.name,
420
+ resources: Array.from(api.resources.keys()),
421
+ stages: Array.from(api.stages.keys()),
422
+ deployments: Array.from(api.deployments.keys())
423
+ });
424
+ } else {
425
+ res.status(404).json({ error: 'API not found' });
426
+ }
427
+ });
428
+
429
+ this.app.get('/__admin/apikeys', (req, res) => {
430
+ const keys = Array.from(this.simulator.apiKeys.values()).map(k => ({
431
+ id: k.id,
432
+ name: k.name,
433
+ enabled: k.enabled,
434
+ createdDate: k.createdDate
435
+ }));
436
+ res.json(keys);
437
+ });
438
+
439
+ this.app.get('/__admin/usageplans', (req, res) => {
440
+ const plans = Array.from(this.simulator.usagePlans.values());
441
+ res.json(plans);
442
+ });
443
+ }
444
+
445
+ start() {
446
+ return new Promise((resolve) => {
447
+ this.server = this.app.listen(this.port, () => {
448
+ logger.info(`🌐 API Gateway rodando em http://localhost:${this.port}`);
449
+ this.printInfo();
450
+ resolve();
451
+ });
452
+ });
453
+ }
454
+
455
+ printInfo() {
456
+ logger.info('\n📡 API Gateway Endpoints:');
457
+ logger.info(` Control Plane: http://localhost:${this.port}/restapis`);
458
+ logger.info(` Execute APIs: http://localhost:${this.port}/{apiId}/{stageName}/{path}`);
459
+ logger.info('\n📚 Admin Endpoints:');
460
+ logger.info(` GET http://localhost:${this.port}/__admin/apis`);
461
+ logger.info(` GET http://localhost:${this.port}/__admin/apikeys`);
462
+ logger.info(` GET http://localhost:${this.port}/__admin/usageplans`);
463
+ }
464
+
465
+ stop() {
466
+ return new Promise((resolve) => {
467
+ if (this.server) {
468
+ this.server.close(() => resolve());
469
+ } else {
470
+ resolve();
471
+ }
472
+ });
473
+ }
474
+
475
+ getStatus() {
476
+ return {
477
+ running: !!this.server,
478
+ port: this.port,
479
+ endpoint: `http://localhost:${this.port}`,
480
+ apisCount: this.simulator?.getAPIsCount() || 0,
481
+ deploymentsCount: this.simulator?.getDeploymentsCount() || 0,
482
+ stagesCount: this.simulator?.getStagesCount() || 0,
483
+ resourcesCount: this.simulator?.getResourcesCount() || 0
484
+ };
485
+ }
486
+ }
487
+
435
488
  module.exports = APIGatewayServer;