@gugananuvem/aws-local-simulator 1.0.12 → 1.0.14
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/README.md +235 -11
- package/package.json +12 -2
- package/src/config/default-config.js +1 -0
- package/src/index.js +18 -2
- package/src/server.js +36 -32
- package/src/services/apigateway/index.js +5 -0
- package/src/services/apigateway/server.js +20 -0
- package/src/services/apigateway/simulator.js +13 -3
- package/src/services/athena/index.js +75 -0
- package/src/services/athena/server.js +101 -0
- package/src/services/athena/simulador.js +998 -0
- package/src/services/athena/simulator.js +346 -0
- package/src/services/cloudformation/index.js +106 -0
- package/src/services/cloudformation/server.js +417 -0
- package/src/services/cloudformation/simulador.js +1045 -0
- package/src/services/cloudtrail/index.js +84 -0
- package/src/services/cloudtrail/server.js +235 -0
- package/src/services/cloudtrail/simulador.js +719 -0
- package/src/services/cloudwatch/index.js +84 -0
- package/src/services/cloudwatch/server.js +366 -0
- package/src/services/cloudwatch/simulador.js +1173 -0
- package/src/services/cognito/index.js +5 -0
- package/src/services/cognito/simulator.js +4 -0
- package/src/services/config/index.js +96 -0
- package/src/services/config/server.js +215 -0
- package/src/services/config/simulador.js +1260 -0
- package/src/services/dynamodb/index.js +7 -3
- package/src/services/dynamodb/server.js +4 -2
- package/src/services/dynamodb/simulator.js +39 -29
- package/src/services/eventbridge/index.js +55 -51
- package/src/services/eventbridge/server.js +209 -0
- package/src/services/eventbridge/simulator.js +684 -0
- package/src/services/index.js +30 -4
- package/src/services/kms/index.js +75 -0
- package/src/services/kms/server.js +67 -0
- package/src/services/kms/simulator.js +324 -0
- package/src/services/lambda/index.js +5 -0
- package/src/services/lambda/simulator.js +48 -38
- package/src/services/parameter-store/index.js +80 -0
- package/src/services/parameter-store/server.js +50 -0
- package/src/services/parameter-store/simulator.js +201 -0
- package/src/services/s3/index.js +7 -3
- package/src/services/s3/server.js +20 -13
- package/src/services/s3/simulator.js +163 -407
- package/src/services/secret-manager/index.js +80 -0
- package/src/services/secret-manager/server.js +50 -0
- package/src/services/secret-manager/simulator.js +171 -0
- package/src/services/sns/index.js +55 -42
- package/src/services/sns/server.js +580 -0
- package/src/services/sns/simulator.js +1482 -0
- package/src/services/sqs/index.js +2 -4
- package/src/services/sqs/server.js +4 -2
- package/src/services/xray/index.js +83 -0
- package/src/services/xray/server.js +308 -0
- package/src/services/xray/simulador.js +994 -0
- package/src/utils/cloudtrail-audit.js +129 -0
- package/src/utils/local-store.js +18 -2
|
@@ -21,10 +21,9 @@ class DynamoDBService {
|
|
|
21
21
|
const logger = require('../../utils/logger');
|
|
22
22
|
logger.debug(`Inicializando DynamoDB Service na porta ${this.port}...`);
|
|
23
23
|
|
|
24
|
-
// Cria o simulador
|
|
25
24
|
this.simulator = new DynamoDBSimulator(this.config);
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
await this.simulator.initialize();
|
|
26
|
+
|
|
28
27
|
this.server = new DynamoDBServer(this.port, this.config);
|
|
29
28
|
this.server.simulator = this.simulator;
|
|
30
29
|
|
|
@@ -33,6 +32,11 @@ class DynamoDBService {
|
|
|
33
32
|
logger.debug('DynamoDB Service inicializado');
|
|
34
33
|
}
|
|
35
34
|
|
|
35
|
+
injectDependencies(server) {
|
|
36
|
+
const ct = server.getService('cloudtrail');
|
|
37
|
+
if (ct?.simulator) this.simulator.audit.setTrail(ct.simulator);
|
|
38
|
+
}
|
|
39
|
+
|
|
36
40
|
async start() {
|
|
37
41
|
if (this.isRunning) return;
|
|
38
42
|
await this.server.start();
|
|
@@ -35,8 +35,10 @@ class DynamoDBServer {
|
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
async initialize() {
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
if (!this.simulator) {
|
|
39
|
+
this.simulator = new DynamoDBSimulator(this.config);
|
|
40
|
+
await this.simulator.initialize();
|
|
41
|
+
}
|
|
40
42
|
this.setupRoutes();
|
|
41
43
|
}
|
|
42
44
|
|
|
@@ -6,6 +6,7 @@ const LocalStore = require("../../utils/local-store");
|
|
|
6
6
|
const logger = require("../../utils/logger");
|
|
7
7
|
const crypto = require("crypto");
|
|
8
8
|
const path = require("path");
|
|
9
|
+
const { CloudTrailAudit } = require("../../utils/cloudtrail-audit");
|
|
9
10
|
|
|
10
11
|
class DynamoDBSimulator {
|
|
11
12
|
constructor(config) {
|
|
@@ -19,6 +20,7 @@ class DynamoDBSimulator {
|
|
|
19
20
|
this.dataDir = path.join(dataDir, "dynamodb");
|
|
20
21
|
this.store = new LocalStore(this.dataDir);
|
|
21
22
|
this.tables = new Map();
|
|
23
|
+
this.audit = new CloudTrailAudit("dynamodb.amazonaws.com");
|
|
22
24
|
}
|
|
23
25
|
async initialize() {
|
|
24
26
|
logger.debug("Inicializando DynamoDB Simulator...");
|
|
@@ -74,10 +76,13 @@ class DynamoDBSimulator {
|
|
|
74
76
|
this.tables.set(TableName, table);
|
|
75
77
|
this.persistTables();
|
|
76
78
|
|
|
77
|
-
// Inicializa arquivo de dados
|
|
78
|
-
this.store.
|
|
79
|
+
// Inicializa arquivo de dados apenas se não existir (preserva dados entre reinicializações)
|
|
80
|
+
if (!this.store.exists(TableName)) {
|
|
81
|
+
this.store.write(TableName, []);
|
|
82
|
+
}
|
|
79
83
|
|
|
80
84
|
logger.debug(`✅ Tabela criada: ${TableName}`);
|
|
85
|
+
this.audit.record({ eventName: "CreateTable", readOnly: false, resources: [{ ARN: `arn:aws:dynamodb:local:000000000000:table/${TableName}`, type: "AWS::DynamoDB::Table" }], requestParameters: { tableName: TableName } });
|
|
81
86
|
|
|
82
87
|
return {
|
|
83
88
|
TableDescription: {
|
|
@@ -101,34 +106,39 @@ class DynamoDBSimulator {
|
|
|
101
106
|
|
|
102
107
|
logger.verboso(`DynamoDB Action: ${action}`, params);
|
|
103
108
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
return this.
|
|
111
|
-
|
|
112
|
-
return this.deleteTable(params);
|
|
113
|
-
|
|
114
|
-
return this.
|
|
115
|
-
|
|
116
|
-
return this.
|
|
117
|
-
|
|
118
|
-
return this.
|
|
119
|
-
|
|
120
|
-
return this.
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
109
|
+
const readActions = new Set(["GetItem", "BatchGetItem", "Query", "Scan", "DescribeTable", "ListTables"]);
|
|
110
|
+
const dataActions = new Set(["PutItem", "GetItem", "UpdateItem", "DeleteItem", "BatchWriteItem", "BatchGetItem", "Query", "Scan"]);
|
|
111
|
+
|
|
112
|
+
const result = (() => {
|
|
113
|
+
switch (action) {
|
|
114
|
+
case "CreateTable": return this.createTable(params);
|
|
115
|
+
case "DescribeTable": return this.describeTable(params.TableName);
|
|
116
|
+
case "ListTables": return this.listTables(params);
|
|
117
|
+
case "DeleteTable": return this.deleteTable(params);
|
|
118
|
+
case "PutItem": return this.putItem(params);
|
|
119
|
+
case "GetItem": return this.getItem(params);
|
|
120
|
+
case "UpdateItem": return this.updateItem(params);
|
|
121
|
+
case "DeleteItem": return this.deleteItem(params);
|
|
122
|
+
case "BatchWriteItem": return this.batchWriteItem(params);
|
|
123
|
+
case "BatchGetItem": return this.batchGetItem(params);
|
|
124
|
+
case "Query": return this.query(params);
|
|
125
|
+
case "Scan": return this.scan(params);
|
|
126
|
+
default: throw new Error(`Unsupported action: ${action}`);
|
|
127
|
+
}
|
|
128
|
+
})();
|
|
129
|
+
|
|
130
|
+
const tableName = params.TableName;
|
|
131
|
+
if (tableName) {
|
|
132
|
+
this.audit.record({
|
|
133
|
+
eventName: action,
|
|
134
|
+
readOnly: readActions.has(action),
|
|
135
|
+
isDataEvent: dataActions.has(action),
|
|
136
|
+
resources: [{ ARN: `arn:aws:dynamodb:local:000000000000:table/${tableName}`, type: "AWS::DynamoDB::Table" }],
|
|
137
|
+
requestParameters: { tableName },
|
|
138
|
+
});
|
|
131
139
|
}
|
|
140
|
+
|
|
141
|
+
return result;
|
|
132
142
|
}
|
|
133
143
|
|
|
134
144
|
describeTable(tableName) {
|
|
@@ -1,45 +1,76 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
|
-
* EventBridge Service
|
|
4
|
+
* @fileoverview EventBridge Service — entry point
|
|
5
|
+
* Porta padrão: 4010
|
|
3
6
|
*/
|
|
4
7
|
|
|
5
|
-
const
|
|
8
|
+
const http = require('http');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const { EventBridgeSimulator } = require('./simulator');
|
|
11
|
+
const { createEventBridgeServer } = require('./server');
|
|
12
|
+
const LocalStore = require('../../utils/local-store');
|
|
6
13
|
|
|
7
14
|
class EventBridgeService {
|
|
8
15
|
constructor(config) {
|
|
9
16
|
this.config = config;
|
|
17
|
+
this.logger = require('../../utils/logger');
|
|
10
18
|
this.name = 'eventbridge';
|
|
11
|
-
this.port = config
|
|
19
|
+
this.port = config?.ports?.eventbridge || config?.services?.eventbridge?.port || 4010;
|
|
20
|
+
this.store = null;
|
|
21
|
+
this.simulator = null;
|
|
22
|
+
this.app = null;
|
|
23
|
+
this.server = null;
|
|
12
24
|
this.isRunning = false;
|
|
13
|
-
this.buses = new Map();
|
|
14
|
-
this.events = [];
|
|
15
25
|
}
|
|
16
26
|
|
|
17
27
|
async initialize() {
|
|
18
|
-
logger.debug(`Inicializando EventBridge Service na porta ${this.port}...`);
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
this.
|
|
23
|
-
this.
|
|
28
|
+
this.logger.debug(`Inicializando EventBridge Service na porta ${this.port}...`);
|
|
29
|
+
const dataDir = process.env.AWS_LOCAL_SIMULATOR_DATA_DIR;
|
|
30
|
+
this.store = new LocalStore(path.join(dataDir, 'eventbridge'));
|
|
31
|
+
this.simulator = new EventBridgeSimulator(this.config, this.store, this.logger);
|
|
32
|
+
this.app = createEventBridgeServer(this.simulator, this.config, this.logger);
|
|
33
|
+
this.logger.debug('EventBridge Service inicializado');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
injectDependencies(server) {
|
|
37
|
+
if (!server) return;
|
|
38
|
+
const lambda = server.getService('lambda');
|
|
39
|
+
if (lambda) this.simulator.setLambdaService(lambda);
|
|
40
|
+
const sqs = server.getService('sqs');
|
|
41
|
+
if (sqs) this.simulator.setSqsService(sqs);
|
|
42
|
+
const sns = server.getService('sns');
|
|
43
|
+
if (sns) this.simulator.setSnsService(sns);
|
|
24
44
|
}
|
|
25
45
|
|
|
26
46
|
async start() {
|
|
27
47
|
if (this.isRunning) return;
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
48
|
+
await this.store.ensureDir();
|
|
49
|
+
await this.simulator.load();
|
|
50
|
+
return new Promise((resolve, reject) => {
|
|
51
|
+
this.server = http.createServer(this.app);
|
|
52
|
+
this.server.on('error', reject);
|
|
53
|
+
this.server.listen(this.port, () => {
|
|
54
|
+
this.isRunning = true;
|
|
55
|
+
this.logger.debug(`EventBridge rodando na porta ${this.port}`);
|
|
56
|
+
resolve();
|
|
57
|
+
});
|
|
58
|
+
});
|
|
32
59
|
}
|
|
33
60
|
|
|
34
61
|
async stop() {
|
|
35
|
-
if (!this.isRunning) return;
|
|
36
|
-
|
|
62
|
+
if (!this.isRunning || !this.server) return;
|
|
63
|
+
return new Promise((resolve, reject) => {
|
|
64
|
+
this.server.close((err) => {
|
|
65
|
+
if (err) return reject(err);
|
|
66
|
+
this.isRunning = false;
|
|
67
|
+
resolve();
|
|
68
|
+
});
|
|
69
|
+
});
|
|
37
70
|
}
|
|
38
71
|
|
|
39
72
|
async reset() {
|
|
40
|
-
this.
|
|
41
|
-
this.events = [];
|
|
42
|
-
logger.debug('EventBridge: Todos os dados resetados');
|
|
73
|
+
await this.simulator.reset();
|
|
43
74
|
}
|
|
44
75
|
|
|
45
76
|
getStatus() {
|
|
@@ -47,39 +78,12 @@ class EventBridgeService {
|
|
|
47
78
|
running: this.isRunning,
|
|
48
79
|
port: this.port,
|
|
49
80
|
endpoint: `http://localhost:${this.port}`,
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
eventsCount: this.events.length
|
|
81
|
+
buses: this.simulator?.buses.size || 0,
|
|
82
|
+
rules: this.simulator?.rules.size || 0,
|
|
53
83
|
};
|
|
54
84
|
}
|
|
55
85
|
|
|
56
|
-
|
|
57
|
-
async createEventBus(name) {
|
|
58
|
-
if (!this.buses.has(name)) {
|
|
59
|
-
this.buses.set(name, {
|
|
60
|
-
name,
|
|
61
|
-
arn: `arn:aws:events:local:000000000000:event-bus/${name}`,
|
|
62
|
-
createdAt: new Date().toISOString()
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
|
-
return this.buses.get(name);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
async putEvents(entries) {
|
|
69
|
-
const results = [];
|
|
70
|
-
for (const entry of entries) {
|
|
71
|
-
const eventId = Math.random().toString(36).substring(7);
|
|
72
|
-
this.events.push({
|
|
73
|
-
...entry,
|
|
74
|
-
eventId,
|
|
75
|
-
time: new Date().toISOString(),
|
|
76
|
-
receivedAt: new Date().toISOString()
|
|
77
|
-
});
|
|
78
|
-
results.push({ EventId: eventId });
|
|
79
|
-
logger.verboso(`EventBridge: Event ${eventId} published to ${entry.EventBusName || 'default'}`);
|
|
80
|
-
}
|
|
81
|
-
return { Entries: results, FailedEntryCount: 0 };
|
|
82
|
-
}
|
|
86
|
+
getSimulator() { return this.simulator; }
|
|
83
87
|
}
|
|
84
88
|
|
|
85
|
-
module.exports = EventBridgeService;
|
|
89
|
+
module.exports = { EventBridgeService };
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview EventBridge HTTP Server
|
|
3
|
+
* Express server compatível com AWS EventBridge REST API (JSON)
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
const express = require('express');
|
|
9
|
+
const cors = require('cors');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Create EventBridge Express application
|
|
13
|
+
* @param {Object} simulator - EventBridgeSimulator instance
|
|
14
|
+
* @param {Object} config - Service configuration
|
|
15
|
+
* @param {Object} logger - Logger instance
|
|
16
|
+
* @returns {express.Application}
|
|
17
|
+
*/
|
|
18
|
+
function createEventBridgeServer(simulator, config, logger) {
|
|
19
|
+
const app = express();
|
|
20
|
+
|
|
21
|
+
if (config.cors?.enabled) {
|
|
22
|
+
app.use(cors({ origin: config.cors.origin || '*' }));
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
app.use(express.json({ limit: '10mb' }));
|
|
26
|
+
|
|
27
|
+
app.use((req, _res, next) => {
|
|
28
|
+
logger.debug('EventBridge', `${req.method} ${req.path}`);
|
|
29
|
+
next();
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
// ==================== Event Buses ====================
|
|
33
|
+
|
|
34
|
+
app.post('/event-buses', async (req, res) => {
|
|
35
|
+
try {
|
|
36
|
+
const result = await simulator.createEventBus(req.body);
|
|
37
|
+
res.status(201).json(result);
|
|
38
|
+
} catch (err) { sendError(res, err); }
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
app.delete('/event-buses/:name', async (req, res) => {
|
|
42
|
+
try {
|
|
43
|
+
await simulator.deleteEventBus({ Name: req.params.name });
|
|
44
|
+
res.status(200).json({});
|
|
45
|
+
} catch (err) { sendError(res, err); }
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
app.get('/event-buses', (_req, res) => {
|
|
49
|
+
try {
|
|
50
|
+
const result = simulator.listEventBuses();
|
|
51
|
+
res.json(result);
|
|
52
|
+
} catch (err) { sendError(res, err); }
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
app.get('/event-buses/:name', (req, res) => {
|
|
56
|
+
try {
|
|
57
|
+
const result = simulator.describeEventBus({ Name: req.params.name });
|
|
58
|
+
res.json(result);
|
|
59
|
+
} catch (err) { sendError(res, err); }
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// ==================== Rules ====================
|
|
63
|
+
|
|
64
|
+
app.put('/rules', async (req, res) => {
|
|
65
|
+
try {
|
|
66
|
+
const result = await simulator.putRule(req.body);
|
|
67
|
+
res.json(result);
|
|
68
|
+
} catch (err) { sendError(res, err); }
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
app.delete('/rules/:name', async (req, res) => {
|
|
72
|
+
try {
|
|
73
|
+
await simulator.deleteRule({ Name: req.params.name, EventBusName: req.query.eventBusName });
|
|
74
|
+
res.status(200).json({});
|
|
75
|
+
} catch (err) { sendError(res, err); }
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
app.get('/rules', (req, res) => {
|
|
79
|
+
try {
|
|
80
|
+
const result = simulator.listRules({
|
|
81
|
+
EventBusName: req.query.EventBusName || 'default',
|
|
82
|
+
NamePrefix: req.query.NamePrefix,
|
|
83
|
+
Limit: req.query.Limit ? parseInt(req.query.Limit) : undefined
|
|
84
|
+
});
|
|
85
|
+
res.json(result);
|
|
86
|
+
} catch (err) { sendError(res, err); }
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
app.get('/rules/:name', (req, res) => {
|
|
90
|
+
try {
|
|
91
|
+
const result = simulator.describeRule({
|
|
92
|
+
Name: req.params.name,
|
|
93
|
+
EventBusName: req.query.EventBusName || 'default'
|
|
94
|
+
});
|
|
95
|
+
res.json(result);
|
|
96
|
+
} catch (err) { sendError(res, err); }
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
app.patch('/rules/:name/enable', async (req, res) => {
|
|
100
|
+
try {
|
|
101
|
+
await simulator.enableRule({ Name: req.params.name, EventBusName: req.query.EventBusName || 'default' });
|
|
102
|
+
res.json({ message: 'Rule enabled' });
|
|
103
|
+
} catch (err) { sendError(res, err); }
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
app.patch('/rules/:name/disable', async (req, res) => {
|
|
107
|
+
try {
|
|
108
|
+
await simulator.disableRule({ Name: req.params.name, EventBusName: req.query.EventBusName || 'default' });
|
|
109
|
+
res.json({ message: 'Rule disabled' });
|
|
110
|
+
} catch (err) { sendError(res, err); }
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
// ==================== Targets ====================
|
|
114
|
+
|
|
115
|
+
app.put('/rules/:name/targets', async (req, res) => {
|
|
116
|
+
try {
|
|
117
|
+
const result = await simulator.putTargets({
|
|
118
|
+
Rule: req.params.name,
|
|
119
|
+
EventBusName: req.body.EventBusName || 'default',
|
|
120
|
+
Targets: req.body.Targets
|
|
121
|
+
});
|
|
122
|
+
res.json(result);
|
|
123
|
+
} catch (err) { sendError(res, err); }
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
app.delete('/rules/:name/targets', async (req, res) => {
|
|
127
|
+
try {
|
|
128
|
+
const result = await simulator.removeTargets({
|
|
129
|
+
Rule: req.params.name,
|
|
130
|
+
EventBusName: req.query.EventBusName || 'default',
|
|
131
|
+
Ids: req.body.Ids || []
|
|
132
|
+
});
|
|
133
|
+
res.json(result);
|
|
134
|
+
} catch (err) { sendError(res, err); }
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
app.get('/rules/:name/targets', (req, res) => {
|
|
138
|
+
try {
|
|
139
|
+
const result = simulator.listTargetsByRule({
|
|
140
|
+
Rule: req.params.name,
|
|
141
|
+
EventBusName: req.query.EventBusName || 'default'
|
|
142
|
+
});
|
|
143
|
+
res.json(result);
|
|
144
|
+
} catch (err) { sendError(res, err); }
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
// ==================== PutEvents ====================
|
|
148
|
+
|
|
149
|
+
app.post('/events', async (req, res) => {
|
|
150
|
+
try {
|
|
151
|
+
const result = await simulator.putEvents(req.body);
|
|
152
|
+
res.json(result);
|
|
153
|
+
} catch (err) { sendError(res, err); }
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
// ==================== Admin ====================
|
|
157
|
+
|
|
158
|
+
app.get('/__admin/health', (_req, res) => {
|
|
159
|
+
res.json({
|
|
160
|
+
status: 'healthy',
|
|
161
|
+
service: 'eventbridge',
|
|
162
|
+
buses: simulator.buses.size,
|
|
163
|
+
rules: simulator.rules.size,
|
|
164
|
+
recentEvents: simulator.eventArchive.length,
|
|
165
|
+
timestamp: new Date().toISOString()
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
app.get('/__admin/buses', (_req, res) => {
|
|
170
|
+
res.json({ buses: Array.from(simulator.buses.values()) });
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
app.get('/__admin/rules', (_req, res) => {
|
|
174
|
+
res.json({ rules: Array.from(simulator.rules.values()) });
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
app.get('/__admin/events', (req, res) => {
|
|
178
|
+
const limit = parseInt(req.query.limit) || 50;
|
|
179
|
+
const events = simulator.eventArchive.slice(-limit);
|
|
180
|
+
res.json({ events, total: simulator.eventArchive.length });
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
app.post('/__admin/reset', async (_req, res) => {
|
|
184
|
+
await simulator.reset();
|
|
185
|
+
res.json({ message: 'EventBridge data reset' });
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
app.use((_req, res) => res.status(404).json({ message: 'Not Found' }));
|
|
189
|
+
|
|
190
|
+
return app;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Send error response
|
|
195
|
+
* @param {express.Response} res
|
|
196
|
+
* @param {Error} err
|
|
197
|
+
*/
|
|
198
|
+
function sendError(res, err) {
|
|
199
|
+
const statusMap = {
|
|
200
|
+
ValidationException: 400,
|
|
201
|
+
ResourceNotFoundException: 404,
|
|
202
|
+
ResourceAlreadyExistsException: 409,
|
|
203
|
+
InvalidEventPatternException: 400
|
|
204
|
+
};
|
|
205
|
+
const status = statusMap[err.code] || statusMap[err.__type] || 500;
|
|
206
|
+
res.status(status).json({ message: err.message, __type: err.__type || err.code });
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
module.exports = { createEventBridgeServer };
|