@gugananuvem/aws-local-simulator 1.0.12 → 1.0.15

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