@gugananuvem/aws-local-simulator 1.0.15 → 1.0.16

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 +789 -594
  2. package/bin/aws-local-simulator.js +63 -63
  3. package/package.json +2 -2
  4. package/src/config/config-loader.js +114 -114
  5. package/src/config/default-config.js +68 -68
  6. package/src/config/env-loader.js +68 -68
  7. package/src/index.js +146 -146
  8. package/src/index.mjs +123 -123
  9. package/src/server.js +227 -227
  10. package/src/services/apigateway/index.js +75 -73
  11. package/src/services/apigateway/server.js +570 -507
  12. package/src/services/apigateway/simulator.js +1261 -1261
  13. package/src/services/athena/index.js +75 -75
  14. package/src/services/athena/server.js +101 -101
  15. package/src/services/athena/simulador.js +998 -998
  16. package/src/services/athena/simulator.js +346 -346
  17. package/src/services/cloudformation/index.js +106 -106
  18. package/src/services/cloudformation/server.js +417 -417
  19. package/src/services/cloudformation/simulador.js +1045 -1045
  20. package/src/services/cloudtrail/index.js +84 -84
  21. package/src/services/cloudtrail/server.js +235 -235
  22. package/src/services/cloudtrail/simulador.js +719 -719
  23. package/src/services/cloudwatch/index.js +84 -84
  24. package/src/services/cloudwatch/server.js +366 -366
  25. package/src/services/cloudwatch/simulador.js +1173 -1173
  26. package/src/services/cognito/index.js +79 -79
  27. package/src/services/cognito/server.js +301 -301
  28. package/src/services/cognito/simulator.js +1655 -1655
  29. package/src/services/config/index.js +96 -96
  30. package/src/services/config/server.js +215 -215
  31. package/src/services/config/simulador.js +1260 -1260
  32. package/src/services/dynamodb/index.js +74 -74
  33. package/src/services/dynamodb/server.js +125 -125
  34. package/src/services/dynamodb/simulator.js +630 -630
  35. package/src/services/ecs/index.js +65 -65
  36. package/src/services/ecs/server.js +235 -235
  37. package/src/services/ecs/simulator.js +844 -844
  38. package/src/services/eventbridge/index.js +89 -89
  39. package/src/services/eventbridge/server.js +209 -209
  40. package/src/services/eventbridge/simulator.js +684 -684
  41. package/src/services/index.js +45 -45
  42. package/src/services/kms/index.js +75 -75
  43. package/src/services/kms/server.js +67 -67
  44. package/src/services/kms/simulator.js +324 -324
  45. package/src/services/lambda/handler-loader.js +183 -183
  46. package/src/services/lambda/index.js +78 -78
  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 -199
  50. package/src/services/parameter-store/index.js +80 -80
  51. package/src/services/parameter-store/server.js +50 -50
  52. package/src/services/parameter-store/simulator.js +201 -201
  53. package/src/services/s3/index.js +73 -73
  54. package/src/services/s3/server.js +329 -329
  55. package/src/services/s3/simulator.js +565 -565
  56. package/src/services/secret-manager/index.js +80 -80
  57. package/src/services/secret-manager/server.js +50 -50
  58. package/src/services/secret-manager/simulator.js +171 -171
  59. package/src/services/sns/index.js +89 -89
  60. package/src/services/sns/server.js +580 -580
  61. package/src/services/sns/simulator.js +1482 -1482
  62. package/src/services/sqs/index.js +98 -93
  63. package/src/services/sqs/server.js +349 -349
  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 -144
  67. package/src/services/sts/simulator.js +69 -69
  68. package/src/services/xray/index.js +83 -83
  69. package/src/services/xray/server.js +308 -308
  70. package/src/services/xray/simulador.js +994 -994
  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 -129
  76. package/src/utils/local-store.js +83 -83
  77. package/src/utils/logger.js +59 -59
@@ -1,366 +1,366 @@
1
- 'use strict';
2
-
3
- /**
4
- * @fileoverview CloudWatch HTTP Server
5
- *
6
- * Protocolo: JSON com header X-Amz-Target
7
- * Compatível com AWS SDK v3:
8
- * - @aws-sdk/client-cloudwatch-logs (CloudWatchLogsClient)
9
- * - @aws-sdk/client-cloudwatch (CloudWatchClient)
10
- *
11
- * Targets suportados (Logs):
12
- * CreateLogGroup, DeleteLogGroup, DescribeLogGroups
13
- * CreateLogStream, DeleteLogStream, DescribeLogStreams
14
- * PutLogEvents, GetLogEvents, FilterLogEvents
15
- * PutRetentionPolicy, DeleteRetentionPolicy
16
- * PutSubscriptionFilter, DeleteSubscriptionFilter, DescribeSubscriptionFilters
17
- *
18
- * Targets suportados (Metrics & Alarms):
19
- * PutMetricData, GetMetricStatistics, ListMetrics
20
- * PutMetricAlarm, DeleteAlarms, DescribeAlarms
21
- * DescribeAlarmsForMetric, SetAlarmState
22
- */
23
-
24
- const express = require('express');
25
- const cors = require('cors');
26
-
27
- /**
28
- * Cria e retorna o servidor Express do CloudWatch
29
- * @param {CloudWatchSimulator} simulator
30
- * @param {Object} logger
31
- * @returns {express.Application}
32
- */
33
- function createCloudWatchServer(simulator, logger) {
34
- const app = express();
35
-
36
- app.use(cors());
37
- app.use(express.json({ limit: '10mb' }));
38
- app.use(express.text({ type: 'application/x-amz-json-1.1', limit: '10mb' }));
39
-
40
- // Middleware: parseia body se vier como string
41
- app.use((req, res, next) => {
42
- if (typeof req.body === 'string') {
43
- try { req.body = JSON.parse(req.body); } catch (_) {}
44
- }
45
- next();
46
- });
47
-
48
- // ─── Roteador principal: X-Amz-Target ────────────────────────────────────
49
-
50
- app.post('/', async (req, res) => {
51
- const target = req.headers['x-amz-target'] || '';
52
- const body = req.body || {};
53
-
54
- logger.debug(`[CloudWatch] Request: ${target}`);
55
-
56
- try {
57
- let result;
58
-
59
- switch (target) {
60
- // ── Log Groups ──────────────────────────────────────────────────────
61
- case 'Logs_20140328.CreateLogGroup':
62
- simulator.createLogGroup({
63
- logGroupName: body.logGroupName,
64
- retentionInDays: body.retentionInDays,
65
- tags: body.tags,
66
- });
67
- result = {};
68
- break;
69
-
70
- case 'Logs_20140328.DeleteLogGroup':
71
- simulator.deleteLogGroup({ logGroupName: body.logGroupName });
72
- result = {};
73
- break;
74
-
75
- case 'Logs_20140328.DescribeLogGroups':
76
- result = simulator.describeLogGroups({
77
- logGroupNamePrefix: body.logGroupNamePrefix,
78
- logGroupNamePattern: body.logGroupNamePattern,
79
- limit: body.limit,
80
- nextToken: body.nextToken,
81
- });
82
- break;
83
-
84
- case 'Logs_20140328.PutRetentionPolicy':
85
- simulator.putRetentionPolicy({
86
- logGroupName: body.logGroupName,
87
- retentionInDays: body.retentionInDays,
88
- });
89
- result = {};
90
- break;
91
-
92
- case 'Logs_20140328.DeleteRetentionPolicy':
93
- simulator.deleteRetentionPolicy({ logGroupName: body.logGroupName });
94
- result = {};
95
- break;
96
-
97
- // ── Log Streams ─────────────────────────────────────────────────────
98
- case 'Logs_20140328.CreateLogStream':
99
- simulator.createLogStream({
100
- logGroupName: body.logGroupName,
101
- logStreamName: body.logStreamName,
102
- });
103
- result = {};
104
- break;
105
-
106
- case 'Logs_20140328.DeleteLogStream':
107
- simulator.deleteLogStream({
108
- logGroupName: body.logGroupName,
109
- logStreamName: body.logStreamName,
110
- });
111
- result = {};
112
- break;
113
-
114
- case 'Logs_20140328.DescribeLogStreams':
115
- result = simulator.describeLogStreams({
116
- logGroupName: body.logGroupName,
117
- logStreamNamePrefix: body.logStreamNamePrefix,
118
- orderBy: body.orderBy,
119
- descending: body.descending,
120
- limit: body.limit,
121
- nextToken: body.nextToken,
122
- });
123
- break;
124
-
125
- // ── Log Events ──────────────────────────────────────────────────────
126
- case 'Logs_20140328.PutLogEvents':
127
- result = await simulator.putLogEvents({
128
- logGroupName: body.logGroupName,
129
- logStreamName: body.logStreamName,
130
- logEvents: body.logEvents,
131
- sequenceToken: body.sequenceToken,
132
- });
133
- break;
134
-
135
- case 'Logs_20140328.GetLogEvents':
136
- result = simulator.getLogEvents({
137
- logGroupName: body.logGroupName,
138
- logStreamName: body.logStreamName,
139
- startTime: body.startTime,
140
- endTime: body.endTime,
141
- nextToken: body.nextToken,
142
- limit: body.limit,
143
- startFromHead: body.startFromHead,
144
- });
145
- break;
146
-
147
- case 'Logs_20140328.FilterLogEvents':
148
- result = simulator.filterLogEvents({
149
- logGroupName: body.logGroupName,
150
- logStreamNames: body.logStreamNames,
151
- startTime: body.startTime,
152
- endTime: body.endTime,
153
- filterPattern: body.filterPattern,
154
- nextToken: body.nextToken,
155
- limit: body.limit,
156
- });
157
- break;
158
-
159
- // ── Subscription Filters ────────────────────────────────────────────
160
- case 'Logs_20140328.PutSubscriptionFilter':
161
- simulator.putSubscriptionFilter({
162
- logGroupName: body.logGroupName,
163
- filterName: body.filterName,
164
- filterPattern: body.filterPattern,
165
- destinationArn: body.destinationArn,
166
- distribution: body.distribution,
167
- });
168
- result = {};
169
- break;
170
-
171
- case 'Logs_20140328.DeleteSubscriptionFilter':
172
- simulator.deleteSubscriptionFilter({
173
- logGroupName: body.logGroupName,
174
- filterName: body.filterName,
175
- });
176
- result = {};
177
- break;
178
-
179
- case 'Logs_20140328.DescribeSubscriptionFilters':
180
- result = simulator.describeSubscriptionFilters({
181
- logGroupName: body.logGroupName,
182
- filterNamePrefix: body.filterNamePrefix,
183
- limit: body.limit,
184
- nextToken: body.nextToken,
185
- });
186
- break;
187
-
188
- // ── Metrics ─────────────────────────────────────────────────────────
189
- case 'GraniteServiceVersion20100801.PutMetricData':
190
- simulator.putMetricData({
191
- namespace: body.Namespace || body.namespace,
192
- metricData: (body.MetricData || body.metricData || []).map(m => ({
193
- metricName: m.MetricName || m.metricName,
194
- dimensions: (m.Dimensions || m.dimensions || []).map(d => ({
195
- name: d.Name || d.name,
196
- value: d.Value || d.value,
197
- })),
198
- timestamp: m.Timestamp || m.timestamp,
199
- value: m.Value !== undefined ? m.Value : m.value,
200
- unit: m.Unit || m.unit,
201
- statistic: m.StatisticValues || m.statistic,
202
- storageResolution: m.StorageResolution || m.storageResolution,
203
- })),
204
- });
205
- result = {};
206
- break;
207
-
208
- case 'GraniteServiceVersion20100801.GetMetricStatistics':
209
- result = simulator.getMetricStatistics({
210
- namespace: body.Namespace || body.namespace,
211
- metricName: body.MetricName || body.metricName,
212
- dimensions: (body.Dimensions || body.dimensions || []).map(d => ({
213
- name: d.Name || d.name,
214
- value: d.Value || d.value,
215
- })),
216
- startTime: body.StartTime || body.startTime,
217
- endTime: body.EndTime || body.endTime,
218
- period: body.Period || body.period,
219
- statistics: body.Statistics || body.statistics,
220
- unit: body.Unit || body.unit,
221
- });
222
- break;
223
-
224
- case 'GraniteServiceVersion20100801.ListMetrics':
225
- result = simulator.listMetrics({
226
- namespace: body.Namespace || body.namespace,
227
- metricName: body.MetricName || body.metricName,
228
- dimensions: (body.Dimensions || body.dimensions || []).map(d => ({
229
- name: d.Name || d.name,
230
- value: d.Value || d.value,
231
- })),
232
- nextToken: body.NextToken || body.nextToken,
233
- });
234
- break;
235
-
236
- // ── Alarms ──────────────────────────────────────────────────────────
237
- case 'GraniteServiceVersion20100801.PutMetricAlarm':
238
- simulator.putMetricAlarm({
239
- alarmName: body.AlarmName || body.alarmName,
240
- alarmDescription: body.AlarmDescription || body.alarmDescription,
241
- actionsEnabled: body.ActionsEnabled !== undefined ? body.ActionsEnabled : body.actionsEnabled,
242
- okActions: body.OKActions || body.okActions,
243
- alarmActions: body.AlarmActions || body.alarmActions,
244
- insufficientDataActions: body.InsufficientDataActions || body.insufficientDataActions,
245
- metricName: body.MetricName || body.metricName,
246
- namespace: body.Namespace || body.namespace,
247
- statistic: body.Statistic || body.statistic,
248
- dimensions: (body.Dimensions || body.dimensions || []).map(d => ({
249
- name: d.Name || d.name,
250
- value: d.Value || d.value,
251
- })),
252
- period: body.Period || body.period,
253
- evaluationPeriods: body.EvaluationPeriods || body.evaluationPeriods,
254
- datapointsToAlarm: body.DatapointsToAlarm || body.datapointsToAlarm,
255
- threshold: body.Threshold !== undefined ? body.Threshold : body.threshold,
256
- comparisonOperator: body.ComparisonOperator || body.comparisonOperator,
257
- treatMissingData: body.TreatMissingData || body.treatMissingData,
258
- unit: body.Unit || body.unit,
259
- });
260
- result = {};
261
- break;
262
-
263
- case 'GraniteServiceVersion20100801.DeleteAlarms':
264
- simulator.deleteAlarms({
265
- alarmNames: body.AlarmNames || body.alarmNames,
266
- });
267
- result = {};
268
- break;
269
-
270
- case 'GraniteServiceVersion20100801.DescribeAlarms':
271
- result = simulator.describeAlarms({
272
- alarmNames: body.AlarmNames || body.alarmNames,
273
- alarmNamePrefix: body.AlarmNamePrefix || body.alarmNamePrefix,
274
- stateValue: body.StateValue || body.stateValue,
275
- actionPrefix: body.ActionPrefix || body.actionPrefix,
276
- maxRecords: body.MaxRecords || body.maxRecords,
277
- nextToken: body.NextToken || body.nextToken,
278
- });
279
- break;
280
-
281
- case 'GraniteServiceVersion20100801.DescribeAlarmsForMetric':
282
- result = simulator.describeAlarmsForMetric({
283
- metricName: body.MetricName || body.metricName,
284
- namespace: body.Namespace || body.namespace,
285
- statistic: body.Statistic || body.statistic,
286
- dimensions: (body.Dimensions || body.dimensions || []).map(d => ({
287
- name: d.Name || d.name,
288
- value: d.Value || d.value,
289
- })),
290
- period: body.Period || body.period,
291
- unit: body.Unit || body.unit,
292
- });
293
- break;
294
-
295
- case 'GraniteServiceVersion20100801.SetAlarmState':
296
- simulator.setAlarmState({
297
- alarmName: body.AlarmName || body.alarmName,
298
- stateValue: body.StateValue || body.stateValue,
299
- stateReason: body.StateReason || body.stateReason,
300
- stateReasonData: body.StateReasonData || body.stateReasonData,
301
- });
302
- result = {};
303
- break;
304
-
305
- default:
306
- logger.warn(`[CloudWatch] Unknown target: ${target}`);
307
- return res.status(400).json({
308
- __type: 'UnknownOperationException',
309
- message: `Unknown operation: ${target}`,
310
- });
311
- }
312
-
313
- res.status(200).json(result || {});
314
-
315
- } catch (err) {
316
- logger.error(`[CloudWatch] Error on ${target}: ${err.message}`);
317
- const statusCode = err.statusCode || 500;
318
- res.status(statusCode).json({
319
- __type: err.code || 'InternalFailure',
320
- message: err.message,
321
- });
322
- }
323
- });
324
-
325
- // ─── Rotas Admin ─────────────────────────────────────────────────────────
326
-
327
- app.get('/__admin/health', (req, res) => {
328
- res.json({ status: 'ok', service: 'cloudwatch' });
329
- });
330
-
331
- app.get('/__admin/status', (req, res) => {
332
- res.json(simulator.getStatus());
333
- });
334
-
335
- app.get('/__admin/log-groups', (req, res) => {
336
- res.json(simulator.listAdminLogGroups());
337
- });
338
-
339
- app.get('/__admin/alarms', (req, res) => {
340
- res.json(simulator.listAdminAlarms());
341
- });
342
-
343
- app.get('/__admin/metrics', (req, res) => {
344
- res.json(simulator.listAdminMetrics());
345
- });
346
-
347
- app.post('/__admin/reset', (req, res) => {
348
- simulator.reset();
349
- res.json({ message: 'CloudWatch state reset' });
350
- });
351
-
352
- // Rota de log de Lambda (chamada internamente pelo simulador Lambda)
353
- app.post('/__internal/lambda-logs', async (req, res) => {
354
- const { functionName, requestId, logs } = req.body || {};
355
- try {
356
- await simulator.putLambdaLogs(functionName, requestId, logs || []);
357
- res.json({ ok: true });
358
- } catch (err) {
359
- res.status(500).json({ error: err.message });
360
- }
361
- });
362
-
363
- return app;
364
- }
365
-
366
- module.exports = { createCloudWatchServer };
1
+ 'use strict';
2
+
3
+ /**
4
+ * @fileoverview CloudWatch HTTP Server
5
+ *
6
+ * Protocolo: JSON com header X-Amz-Target
7
+ * Compatível com AWS SDK v3:
8
+ * - @aws-sdk/client-cloudwatch-logs (CloudWatchLogsClient)
9
+ * - @aws-sdk/client-cloudwatch (CloudWatchClient)
10
+ *
11
+ * Targets suportados (Logs):
12
+ * CreateLogGroup, DeleteLogGroup, DescribeLogGroups
13
+ * CreateLogStream, DeleteLogStream, DescribeLogStreams
14
+ * PutLogEvents, GetLogEvents, FilterLogEvents
15
+ * PutRetentionPolicy, DeleteRetentionPolicy
16
+ * PutSubscriptionFilter, DeleteSubscriptionFilter, DescribeSubscriptionFilters
17
+ *
18
+ * Targets suportados (Metrics & Alarms):
19
+ * PutMetricData, GetMetricStatistics, ListMetrics
20
+ * PutMetricAlarm, DeleteAlarms, DescribeAlarms
21
+ * DescribeAlarmsForMetric, SetAlarmState
22
+ */
23
+
24
+ const express = require('express');
25
+ const cors = require('cors');
26
+
27
+ /**
28
+ * Cria e retorna o servidor Express do CloudWatch
29
+ * @param {CloudWatchSimulator} simulator
30
+ * @param {Object} logger
31
+ * @returns {express.Application}
32
+ */
33
+ function createCloudWatchServer(simulator, logger) {
34
+ const app = express();
35
+
36
+ app.use(cors());
37
+ app.use(express.json({ limit: '10mb' }));
38
+ app.use(express.text({ type: 'application/x-amz-json-1.1', limit: '10mb' }));
39
+
40
+ // Middleware: parseia body se vier como string
41
+ app.use((req, res, next) => {
42
+ if (typeof req.body === 'string') {
43
+ try { req.body = JSON.parse(req.body); } catch (_) {}
44
+ }
45
+ next();
46
+ });
47
+
48
+ // ─── Roteador principal: X-Amz-Target ────────────────────────────────────
49
+
50
+ app.post('/', async (req, res) => {
51
+ const target = req.headers['x-amz-target'] || '';
52
+ const body = req.body || {};
53
+
54
+ logger.debug(`[CloudWatch] Request: ${target}`);
55
+
56
+ try {
57
+ let result;
58
+
59
+ switch (target) {
60
+ // ── Log Groups ──────────────────────────────────────────────────────
61
+ case 'Logs_20140328.CreateLogGroup':
62
+ simulator.createLogGroup({
63
+ logGroupName: body.logGroupName,
64
+ retentionInDays: body.retentionInDays,
65
+ tags: body.tags,
66
+ });
67
+ result = {};
68
+ break;
69
+
70
+ case 'Logs_20140328.DeleteLogGroup':
71
+ simulator.deleteLogGroup({ logGroupName: body.logGroupName });
72
+ result = {};
73
+ break;
74
+
75
+ case 'Logs_20140328.DescribeLogGroups':
76
+ result = simulator.describeLogGroups({
77
+ logGroupNamePrefix: body.logGroupNamePrefix,
78
+ logGroupNamePattern: body.logGroupNamePattern,
79
+ limit: body.limit,
80
+ nextToken: body.nextToken,
81
+ });
82
+ break;
83
+
84
+ case 'Logs_20140328.PutRetentionPolicy':
85
+ simulator.putRetentionPolicy({
86
+ logGroupName: body.logGroupName,
87
+ retentionInDays: body.retentionInDays,
88
+ });
89
+ result = {};
90
+ break;
91
+
92
+ case 'Logs_20140328.DeleteRetentionPolicy':
93
+ simulator.deleteRetentionPolicy({ logGroupName: body.logGroupName });
94
+ result = {};
95
+ break;
96
+
97
+ // ── Log Streams ─────────────────────────────────────────────────────
98
+ case 'Logs_20140328.CreateLogStream':
99
+ simulator.createLogStream({
100
+ logGroupName: body.logGroupName,
101
+ logStreamName: body.logStreamName,
102
+ });
103
+ result = {};
104
+ break;
105
+
106
+ case 'Logs_20140328.DeleteLogStream':
107
+ simulator.deleteLogStream({
108
+ logGroupName: body.logGroupName,
109
+ logStreamName: body.logStreamName,
110
+ });
111
+ result = {};
112
+ break;
113
+
114
+ case 'Logs_20140328.DescribeLogStreams':
115
+ result = simulator.describeLogStreams({
116
+ logGroupName: body.logGroupName,
117
+ logStreamNamePrefix: body.logStreamNamePrefix,
118
+ orderBy: body.orderBy,
119
+ descending: body.descending,
120
+ limit: body.limit,
121
+ nextToken: body.nextToken,
122
+ });
123
+ break;
124
+
125
+ // ── Log Events ──────────────────────────────────────────────────────
126
+ case 'Logs_20140328.PutLogEvents':
127
+ result = await simulator.putLogEvents({
128
+ logGroupName: body.logGroupName,
129
+ logStreamName: body.logStreamName,
130
+ logEvents: body.logEvents,
131
+ sequenceToken: body.sequenceToken,
132
+ });
133
+ break;
134
+
135
+ case 'Logs_20140328.GetLogEvents':
136
+ result = simulator.getLogEvents({
137
+ logGroupName: body.logGroupName,
138
+ logStreamName: body.logStreamName,
139
+ startTime: body.startTime,
140
+ endTime: body.endTime,
141
+ nextToken: body.nextToken,
142
+ limit: body.limit,
143
+ startFromHead: body.startFromHead,
144
+ });
145
+ break;
146
+
147
+ case 'Logs_20140328.FilterLogEvents':
148
+ result = simulator.filterLogEvents({
149
+ logGroupName: body.logGroupName,
150
+ logStreamNames: body.logStreamNames,
151
+ startTime: body.startTime,
152
+ endTime: body.endTime,
153
+ filterPattern: body.filterPattern,
154
+ nextToken: body.nextToken,
155
+ limit: body.limit,
156
+ });
157
+ break;
158
+
159
+ // ── Subscription Filters ────────────────────────────────────────────
160
+ case 'Logs_20140328.PutSubscriptionFilter':
161
+ simulator.putSubscriptionFilter({
162
+ logGroupName: body.logGroupName,
163
+ filterName: body.filterName,
164
+ filterPattern: body.filterPattern,
165
+ destinationArn: body.destinationArn,
166
+ distribution: body.distribution,
167
+ });
168
+ result = {};
169
+ break;
170
+
171
+ case 'Logs_20140328.DeleteSubscriptionFilter':
172
+ simulator.deleteSubscriptionFilter({
173
+ logGroupName: body.logGroupName,
174
+ filterName: body.filterName,
175
+ });
176
+ result = {};
177
+ break;
178
+
179
+ case 'Logs_20140328.DescribeSubscriptionFilters':
180
+ result = simulator.describeSubscriptionFilters({
181
+ logGroupName: body.logGroupName,
182
+ filterNamePrefix: body.filterNamePrefix,
183
+ limit: body.limit,
184
+ nextToken: body.nextToken,
185
+ });
186
+ break;
187
+
188
+ // ── Metrics ─────────────────────────────────────────────────────────
189
+ case 'GraniteServiceVersion20100801.PutMetricData':
190
+ simulator.putMetricData({
191
+ namespace: body.Namespace || body.namespace,
192
+ metricData: (body.MetricData || body.metricData || []).map(m => ({
193
+ metricName: m.MetricName || m.metricName,
194
+ dimensions: (m.Dimensions || m.dimensions || []).map(d => ({
195
+ name: d.Name || d.name,
196
+ value: d.Value || d.value,
197
+ })),
198
+ timestamp: m.Timestamp || m.timestamp,
199
+ value: m.Value !== undefined ? m.Value : m.value,
200
+ unit: m.Unit || m.unit,
201
+ statistic: m.StatisticValues || m.statistic,
202
+ storageResolution: m.StorageResolution || m.storageResolution,
203
+ })),
204
+ });
205
+ result = {};
206
+ break;
207
+
208
+ case 'GraniteServiceVersion20100801.GetMetricStatistics':
209
+ result = simulator.getMetricStatistics({
210
+ namespace: body.Namespace || body.namespace,
211
+ metricName: body.MetricName || body.metricName,
212
+ dimensions: (body.Dimensions || body.dimensions || []).map(d => ({
213
+ name: d.Name || d.name,
214
+ value: d.Value || d.value,
215
+ })),
216
+ startTime: body.StartTime || body.startTime,
217
+ endTime: body.EndTime || body.endTime,
218
+ period: body.Period || body.period,
219
+ statistics: body.Statistics || body.statistics,
220
+ unit: body.Unit || body.unit,
221
+ });
222
+ break;
223
+
224
+ case 'GraniteServiceVersion20100801.ListMetrics':
225
+ result = simulator.listMetrics({
226
+ namespace: body.Namespace || body.namespace,
227
+ metricName: body.MetricName || body.metricName,
228
+ dimensions: (body.Dimensions || body.dimensions || []).map(d => ({
229
+ name: d.Name || d.name,
230
+ value: d.Value || d.value,
231
+ })),
232
+ nextToken: body.NextToken || body.nextToken,
233
+ });
234
+ break;
235
+
236
+ // ── Alarms ──────────────────────────────────────────────────────────
237
+ case 'GraniteServiceVersion20100801.PutMetricAlarm':
238
+ simulator.putMetricAlarm({
239
+ alarmName: body.AlarmName || body.alarmName,
240
+ alarmDescription: body.AlarmDescription || body.alarmDescription,
241
+ actionsEnabled: body.ActionsEnabled !== undefined ? body.ActionsEnabled : body.actionsEnabled,
242
+ okActions: body.OKActions || body.okActions,
243
+ alarmActions: body.AlarmActions || body.alarmActions,
244
+ insufficientDataActions: body.InsufficientDataActions || body.insufficientDataActions,
245
+ metricName: body.MetricName || body.metricName,
246
+ namespace: body.Namespace || body.namespace,
247
+ statistic: body.Statistic || body.statistic,
248
+ dimensions: (body.Dimensions || body.dimensions || []).map(d => ({
249
+ name: d.Name || d.name,
250
+ value: d.Value || d.value,
251
+ })),
252
+ period: body.Period || body.period,
253
+ evaluationPeriods: body.EvaluationPeriods || body.evaluationPeriods,
254
+ datapointsToAlarm: body.DatapointsToAlarm || body.datapointsToAlarm,
255
+ threshold: body.Threshold !== undefined ? body.Threshold : body.threshold,
256
+ comparisonOperator: body.ComparisonOperator || body.comparisonOperator,
257
+ treatMissingData: body.TreatMissingData || body.treatMissingData,
258
+ unit: body.Unit || body.unit,
259
+ });
260
+ result = {};
261
+ break;
262
+
263
+ case 'GraniteServiceVersion20100801.DeleteAlarms':
264
+ simulator.deleteAlarms({
265
+ alarmNames: body.AlarmNames || body.alarmNames,
266
+ });
267
+ result = {};
268
+ break;
269
+
270
+ case 'GraniteServiceVersion20100801.DescribeAlarms':
271
+ result = simulator.describeAlarms({
272
+ alarmNames: body.AlarmNames || body.alarmNames,
273
+ alarmNamePrefix: body.AlarmNamePrefix || body.alarmNamePrefix,
274
+ stateValue: body.StateValue || body.stateValue,
275
+ actionPrefix: body.ActionPrefix || body.actionPrefix,
276
+ maxRecords: body.MaxRecords || body.maxRecords,
277
+ nextToken: body.NextToken || body.nextToken,
278
+ });
279
+ break;
280
+
281
+ case 'GraniteServiceVersion20100801.DescribeAlarmsForMetric':
282
+ result = simulator.describeAlarmsForMetric({
283
+ metricName: body.MetricName || body.metricName,
284
+ namespace: body.Namespace || body.namespace,
285
+ statistic: body.Statistic || body.statistic,
286
+ dimensions: (body.Dimensions || body.dimensions || []).map(d => ({
287
+ name: d.Name || d.name,
288
+ value: d.Value || d.value,
289
+ })),
290
+ period: body.Period || body.period,
291
+ unit: body.Unit || body.unit,
292
+ });
293
+ break;
294
+
295
+ case 'GraniteServiceVersion20100801.SetAlarmState':
296
+ simulator.setAlarmState({
297
+ alarmName: body.AlarmName || body.alarmName,
298
+ stateValue: body.StateValue || body.stateValue,
299
+ stateReason: body.StateReason || body.stateReason,
300
+ stateReasonData: body.StateReasonData || body.stateReasonData,
301
+ });
302
+ result = {};
303
+ break;
304
+
305
+ default:
306
+ logger.warn(`[CloudWatch] Unknown target: ${target}`);
307
+ return res.status(400).json({
308
+ __type: 'UnknownOperationException',
309
+ message: `Unknown operation: ${target}`,
310
+ });
311
+ }
312
+
313
+ res.status(200).json(result || {});
314
+
315
+ } catch (err) {
316
+ logger.error(`[CloudWatch] Error on ${target}: ${err.message}`);
317
+ const statusCode = err.statusCode || 500;
318
+ res.status(statusCode).json({
319
+ __type: err.code || 'InternalFailure',
320
+ message: err.message,
321
+ });
322
+ }
323
+ });
324
+
325
+ // ─── Rotas Admin ─────────────────────────────────────────────────────────
326
+
327
+ app.get('/__admin/health', (req, res) => {
328
+ res.json({ status: 'ok', service: 'cloudwatch' });
329
+ });
330
+
331
+ app.get('/__admin/status', (req, res) => {
332
+ res.json(simulator.getStatus());
333
+ });
334
+
335
+ app.get('/__admin/log-groups', (req, res) => {
336
+ res.json(simulator.listAdminLogGroups());
337
+ });
338
+
339
+ app.get('/__admin/alarms', (req, res) => {
340
+ res.json(simulator.listAdminAlarms());
341
+ });
342
+
343
+ app.get('/__admin/metrics', (req, res) => {
344
+ res.json(simulator.listAdminMetrics());
345
+ });
346
+
347
+ app.post('/__admin/reset', (req, res) => {
348
+ simulator.reset();
349
+ res.json({ message: 'CloudWatch state reset' });
350
+ });
351
+
352
+ // Rota de log de Lambda (chamada internamente pelo simulador Lambda)
353
+ app.post('/__internal/lambda-logs', async (req, res) => {
354
+ const { functionName, requestId, logs } = req.body || {};
355
+ try {
356
+ await simulator.putLambdaLogs(functionName, requestId, logs || []);
357
+ res.json({ ok: true });
358
+ } catch (err) {
359
+ res.status(500).json({ error: err.message });
360
+ }
361
+ });
362
+
363
+ return app;
364
+ }
365
+
366
+ module.exports = { createCloudWatchServer };