@gugananuvem/aws-local-simulator 1.0.34 → 1.0.39

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 (150) hide show
  1. package/README.md +662 -658
  2. package/package.json +11 -4
  3. package/src/config/default-config.js +71 -0
  4. package/src/config/env-loader.js +4 -2
  5. package/src/middleware/authorization.js +271 -0
  6. package/src/middleware/cloudtrail-logger.js +333 -0
  7. package/src/middleware/error-handler.js +138 -0
  8. package/src/middleware/index.js +79 -0
  9. package/src/middleware/policy-enforcement.js +407 -0
  10. package/src/server.js +74 -4
  11. package/src/services/acm/index.js +66 -0
  12. package/src/services/acm/models/certificate.js +202 -0
  13. package/src/services/acm/server.js +224 -0
  14. package/src/services/acm/simulator.js +293 -0
  15. package/src/services/alb/index.js +104 -0
  16. package/src/services/alb/models/loadBalancer.js +88 -0
  17. package/src/services/alb/models/targetGroup.js +128 -0
  18. package/src/services/alb/server.js +160 -0
  19. package/src/services/alb/simulator.js +262 -0
  20. package/src/services/apigateway/index.js +22 -0
  21. package/src/services/apigateway/server.js +247 -35
  22. package/src/services/apigateway/simulator.js +780 -26
  23. package/src/services/asg/index.js +100 -0
  24. package/src/services/asg/models/autoScalingGroup.js +110 -0
  25. package/src/services/asg/models/launchTemplate.js +41 -0
  26. package/src/services/asg/models/scalingPolicy.js +51 -0
  27. package/src/services/asg/server.js +236 -0
  28. package/src/services/asg/simulator.js +410 -0
  29. package/src/services/aurora/cluster-manager.js +731 -0
  30. package/src/services/aurora/data-api.js +514 -0
  31. package/src/services/aurora/db-engine.js +270 -0
  32. package/src/services/aurora/index.js +159 -0
  33. package/src/services/aurora/rds-api.js +189 -0
  34. package/src/services/aurora/server.js +213 -0
  35. package/src/services/aurora/sql-translator.js +334 -0
  36. package/src/services/cloudformation/index.js +2 -2
  37. package/src/services/cloudformation/server.js +1 -0
  38. package/src/services/cloudfront/cache-engine.js +225 -0
  39. package/src/services/cloudfront/geo-restrictions.js +377 -0
  40. package/src/services/cloudfront/index.js +220 -0
  41. package/src/services/cloudfront/models/behavior.js +158 -0
  42. package/src/services/cloudfront/models/distribution.js +284 -0
  43. package/src/services/cloudfront/models/index.js +99 -0
  44. package/src/services/cloudfront/models/invalidation.js +109 -0
  45. package/src/services/cloudfront/models/origin.js +155 -0
  46. package/src/services/cloudfront/origin-router.js +552 -0
  47. package/src/services/cloudfront/origin-validator.js +398 -0
  48. package/src/services/cloudfront/server.js +512 -0
  49. package/src/services/cloudfront/signed-url-generator.js +377 -0
  50. package/src/services/cloudfront/simulator.js +425 -0
  51. package/src/services/cloudfront/store.js +326 -0
  52. package/src/services/cloudshell/env-builder.js +27 -0
  53. package/src/services/cloudshell/index.js +48 -0
  54. package/src/services/cloudshell/routes.js +60 -0
  55. package/src/services/cloudshell/session-manager.js +226 -0
  56. package/src/services/cloudshell/static/.gitkeep +0 -0
  57. package/src/services/cloudshell/static/index.html +406 -0
  58. package/src/services/cloudshell/websocket-handler.js +107 -0
  59. package/src/services/cloudtrail/index.js +2 -3
  60. package/src/services/cloudtrail/server.js +68 -0
  61. package/src/services/cognito/server.js +4 -1
  62. package/src/services/dynamodb/server.js +4 -1
  63. package/src/services/dynamodb/simulator.js +2 -1
  64. package/src/services/ec2/index.js +101 -0
  65. package/src/services/ec2/instanceServer.js +311 -0
  66. package/src/services/ec2/models/ami.js +39 -0
  67. package/src/services/ec2/models/instance.js +98 -0
  68. package/src/services/ec2/models/networkInterface.js +74 -0
  69. package/src/services/ec2/models/securityGroup.js +71 -0
  70. package/src/services/ec2/server.js +350 -0
  71. package/src/services/ec2/simulator.js +436 -0
  72. package/src/services/eventbridge/server.js +36 -0
  73. package/src/services/eventbridge/simulator.js +230 -45
  74. package/src/services/iam/index.js +65 -0
  75. package/src/services/iam/middleware.js +99 -0
  76. package/src/services/iam/server.js +331 -0
  77. package/src/services/iam/simulator.js +1432 -0
  78. package/src/services/index.js +28 -0
  79. package/src/services/kinesis-analytics/index.js +105 -0
  80. package/src/services/kinesis-analytics/models/application.js +325 -0
  81. package/src/services/kinesis-analytics/models/index.js +12 -0
  82. package/src/services/kinesis-analytics/server.js +250 -0
  83. package/src/services/kinesis-analytics/simulator.js +407 -0
  84. package/src/services/kinesis-analytics/store.js +176 -0
  85. package/src/services/kinesis-data-streams/index.js +106 -0
  86. package/src/services/kinesis-data-streams/models/index.js +12 -0
  87. package/src/services/kinesis-data-streams/models/record.js +70 -0
  88. package/src/services/kinesis-data-streams/models/stream.js +280 -0
  89. package/src/services/kinesis-data-streams/server.js +255 -0
  90. package/src/services/kinesis-data-streams/simulator.js +514 -0
  91. package/src/services/kinesis-data-streams/store.js +163 -0
  92. package/src/services/kinesis-firehose/index.js +106 -0
  93. package/src/services/kinesis-firehose/models/delivery-stream.js +265 -0
  94. package/src/services/kinesis-firehose/models/index.js +10 -0
  95. package/src/services/kinesis-firehose/server.js +215 -0
  96. package/src/services/kinesis-firehose/simulator.js +388 -0
  97. package/src/services/kinesis-firehose/store.js +163 -0
  98. package/src/services/kms/server.js +4 -1
  99. package/src/services/lambda/handler-loader.js +50 -5
  100. package/src/services/lambda/index.js +26 -0
  101. package/src/services/lambda/server.js +18 -12
  102. package/src/services/lambda/simulator.js +73 -17
  103. package/src/services/opensearch/index.js +105 -0
  104. package/src/services/opensearch/models/document.js +115 -0
  105. package/src/services/opensearch/models/index.js +73 -0
  106. package/src/services/opensearch/server.js +310 -0
  107. package/src/services/opensearch/simulator.js +605 -0
  108. package/src/services/opensearch/store.js +162 -0
  109. package/src/services/organizations/control-tower-simulator.js +321 -0
  110. package/src/services/organizations/control-tower-store.js +182 -0
  111. package/src/services/organizations/iam-integration.js +413 -0
  112. package/src/services/organizations/index.js +172 -0
  113. package/src/services/organizations/models/index.js +13 -0
  114. package/src/services/organizations/models/invitation.js +48 -0
  115. package/src/services/organizations/models/organization.js +566 -0
  116. package/src/services/organizations/models/role.js +244 -0
  117. package/src/services/organizations/models/scp.js +131 -0
  118. package/src/services/organizations/server.js +736 -0
  119. package/src/services/organizations/simulator.js +593 -0
  120. package/src/services/organizations/store.js +235 -0
  121. package/src/services/organizations/vpc-integration.js +453 -0
  122. package/src/services/route53/index.js +67 -0
  123. package/src/services/route53/models/healthCheck.js +101 -0
  124. package/src/services/route53/models/hostedZone.js +37 -0
  125. package/src/services/route53/models/resourceRecord.js +106 -0
  126. package/src/services/route53/server.js +841 -0
  127. package/src/services/route53/simulator.js +350 -0
  128. package/src/services/s3/index.js +62 -0
  129. package/src/services/s3/presigned-url-generator.js +337 -0
  130. package/src/services/s3/server.js +231 -0
  131. package/src/services/s3/simulator.js +551 -4
  132. package/src/services/sns/index.js +31 -0
  133. package/src/services/sns/server.js +9 -0
  134. package/src/services/sqs/index.js +38 -0
  135. package/src/services/sqs/server.js +57 -4
  136. package/src/services/sqs/simulator.js +423 -33
  137. package/src/services/sts/simulator.js +209 -17
  138. package/src/services/vpc/README.md +459 -0
  139. package/src/services/vpc/ec2ResponseFormatter.js +431 -0
  140. package/src/services/vpc/index.js +90 -0
  141. package/src/services/vpc/models/internetGateway.js +71 -0
  142. package/src/services/vpc/models/routeTable.js +160 -0
  143. package/src/services/vpc/models/subnet.js +48 -0
  144. package/src/services/vpc/models/vpc.js +37 -0
  145. package/src/services/vpc/models/vpcEndpoint.js +100 -0
  146. package/src/services/vpc/models/vpcGateway.js +134 -0
  147. package/src/services/vpc/server.js +836 -0
  148. package/src/services/vpc/simulator.js +613 -0
  149. package/src/utils/policy-cache.js +215 -0
  150. package/aws-config +0 -154
@@ -0,0 +1,388 @@
1
+ /**
2
+ * Kinesis Data Firehose Simulator
3
+ * Simulador de Kinesis Data Firehose com suporte a delivery streams e buffering
4
+ */
5
+
6
+ const { DeliveryStream } = require('./models/delivery-stream');
7
+ const LocalStore = require('../../utils/local-store');
8
+ const logger = require('../../utils/logger');
9
+ const path = require('path');
10
+ const { CloudTrailAudit } = require('../../utils/cloudtrail-audit');
11
+ const { v4: uuidv4 } = require('uuid');
12
+
13
+ class KinesisFirehoseSimulator {
14
+ constructor(config) {
15
+ this.config = config;
16
+ const dataDir = process.env.AWS_LOCAL_SIMULATOR_DATA_DIR || config.dataDir || './.aws-local-simulator-data';
17
+ this.dataDir = path.join(dataDir, 'kinesis-firehose');
18
+
19
+ this.store = new LocalStore(this.dataDir);
20
+ this.deliveryStreams = new Map();
21
+ this.audit = new CloudTrailAudit('firehose.amazonaws.com');
22
+
23
+ // Mutexes por stream para operações thread-safe
24
+ this._streamLocks = new Map();
25
+ }
26
+
27
+ /**
28
+ * Executa operação com lock exclusivo por stream
29
+ */
30
+ _withStreamLock(streamName, fn) {
31
+ const prev = this._streamLocks.get(streamName) || Promise.resolve();
32
+ const next = prev.then(() => fn());
33
+ this._streamLocks.set(streamName, next.catch(() => {}));
34
+ return next;
35
+ }
36
+
37
+ async initialize() {
38
+ logger.debug('Inicializando Kinesis Data Firehose Simulator...');
39
+ this.loadDeliveryStreams();
40
+
41
+ // Cria delivery streams da configuração
42
+ if (this.config['kinesis-firehose']?.deliveryStreams) {
43
+ for (const streamConfig of this.config['kinesis-firehose'].deliveryStreams) {
44
+ if (!this.deliveryStreams.has(streamConfig.name)) {
45
+ this.createDeliveryStream(streamConfig.name, streamConfig);
46
+ }
47
+ }
48
+ }
49
+
50
+ logger.debug(`✅ Kinesis Data Firehose Simulator inicializado com ${this.deliveryStreams.size} delivery streams`);
51
+ }
52
+
53
+ /**
54
+ * Carrega delivery streams persistidos
55
+ */
56
+ loadDeliveryStreams() {
57
+ const metadata = this.store.read('__delivery_streams__');
58
+ if (!metadata) return;
59
+
60
+ for (const [streamName, streamData] of Object.entries(metadata)) {
61
+ const stream = new DeliveryStream(streamName, streamData);
62
+ this.deliveryStreams.set(streamName, stream);
63
+ }
64
+
65
+ logger.debug(`Carregados ${this.deliveryStreams.size} delivery streams`);
66
+ }
67
+
68
+ /**
69
+ * Persiste metadados de delivery streams
70
+ */
71
+ persistDeliveryStreams() {
72
+ const metadata = {};
73
+
74
+ for (const [streamName, stream] of this.deliveryStreams) {
75
+ metadata[streamName] = stream.toJSON();
76
+ }
77
+
78
+ this.store.write('__delivery_streams__', metadata);
79
+ }
80
+
81
+ /**
82
+ * Cria um novo delivery stream
83
+ */
84
+ createDeliveryStream(deliveryStreamName, config = {}) {
85
+ if (this.deliveryStreams.has(deliveryStreamName)) {
86
+ throw new Error(`DeliveryStream '${deliveryStreamName}' already exists`);
87
+ }
88
+
89
+ const stream = new DeliveryStream(deliveryStreamName, config);
90
+
91
+ // Adiciona destinos (S3, HTTP, Redshift)
92
+ if (config.s3DestinationConfiguration) {
93
+ stream.addDestination({
94
+ type: 's3',
95
+ destinationId: uuidv4(),
96
+ arn: config.s3DestinationConfiguration.RoleArn,
97
+ bucketArn: config.s3DestinationConfiguration.BucketARN,
98
+ prefix: config.s3DestinationConfiguration.Prefix || '',
99
+ errorOutputPrefix: config.s3DestinationConfiguration.ErrorOutputPrefix || '',
100
+ bufferingHints: {
101
+ sizeInMBs: config.s3DestinationConfiguration.BufferingHints?.SizeInMBs || 128,
102
+ intervalInSeconds: config.s3DestinationConfiguration.BufferingHints?.IntervalInSeconds || 60
103
+ },
104
+ compressionFormat: config.s3DestinationConfiguration.CompressionFormat || 'UNCOMPRESSED',
105
+ destinationStatus: 'ACTIVE'
106
+ });
107
+ }
108
+
109
+ if (config.httpEndpointDestinationConfiguration) {
110
+ stream.addDestination({
111
+ type: 'http',
112
+ destinationId: uuidv4(),
113
+ url: config.httpEndpointDestinationConfiguration.EndpointConfiguration?.Url,
114
+ name: config.httpEndpointDestinationConfiguration.EndpointConfiguration?.Name,
115
+ roleArn: config.httpEndpointDestinationConfiguration.RoleARN,
116
+ bufferingHints: {
117
+ sizeInMBs: config.httpEndpointDestinationConfiguration.BufferingHints?.SizeInMBs || 128,
118
+ intervalInSeconds: config.httpEndpointDestinationConfiguration.BufferingHints?.IntervalInSeconds || 60
119
+ },
120
+ processingConfiguration: config.httpEndpointDestinationConfiguration.ProcessingConfiguration || null,
121
+ destinationStatus: 'ACTIVE'
122
+ });
123
+ }
124
+
125
+ if (config.redshiftDestinationConfiguration) {
126
+ stream.addDestination({
127
+ type: 'redshift',
128
+ destinationId: uuidv4(),
129
+ clusterArn: config.redshiftDestinationConfiguration.ClusterARN,
130
+ roleArn: config.redshiftDestinationConfiguration.RoleARN,
131
+ username: config.redshiftDestinationConfiguration.Username,
132
+ databaseName: config.redshiftDestinationConfiguration.DatabaseName,
133
+ tableName: config.redshiftDestinationConfiguration.TableName,
134
+ s3DestinationDescription: config.redshiftDestinationConfiguration.S3DestinationConfiguration,
135
+ destinationStatus: 'ACTIVE'
136
+ });
137
+ }
138
+
139
+ this.deliveryStreams.set(deliveryStreamName, stream);
140
+ this.persistDeliveryStreams();
141
+
142
+ this.audit.record({
143
+ eventName: 'CreateDeliveryStream',
144
+ readOnly: false,
145
+ resources: [{
146
+ ARN: stream.deliveryStreamArn,
147
+ type: 'AWS::Firehose::DeliveryStream'
148
+ }],
149
+ requestParameters: { deliveryStreamName }
150
+ });
151
+
152
+ logger.info(`✅ Delivery Stream criado: ${deliveryStreamName}`);
153
+
154
+ return {
155
+ DeliveryStreamArn: stream.deliveryStreamArn
156
+ };
157
+ }
158
+
159
+ /**
160
+ * Descreve um delivery stream
161
+ */
162
+ describeDeliveryStream(deliveryStreamName) {
163
+ const stream = this.deliveryStreams.get(deliveryStreamName);
164
+ if (!stream) {
165
+ throw new Error(`DeliveryStream '${deliveryStreamName}' not found`);
166
+ }
167
+
168
+ return stream.describe();
169
+ }
170
+
171
+ /**
172
+ * Lista delivery streams
173
+ */
174
+ listDeliveryStreams(params = {}) {
175
+ const { Limit = 100, ExclusiveStartDeliveryStreamName } = params;
176
+ const streamNames = Array.from(this.deliveryStreams.keys());
177
+
178
+ let startIndex = 0;
179
+ if (ExclusiveStartDeliveryStreamName) {
180
+ const index = streamNames.indexOf(ExclusiveStartDeliveryStreamName);
181
+ if (index !== -1) startIndex = index + 1;
182
+ }
183
+
184
+ const result = streamNames.slice(startIndex, startIndex + Limit);
185
+
186
+ return {
187
+ DeliveryStreamNames: result,
188
+ HasMoreDeliveryStreams: result.length === Limit
189
+ };
190
+ }
191
+
192
+ /**
193
+ * Deleta um delivery stream
194
+ */
195
+ deleteDeliveryStream(deliveryStreamName) {
196
+ const stream = this.deliveryStreams.get(deliveryStreamName);
197
+ if (!stream) {
198
+ throw new Error(`DeliveryStream '${deliveryStreamName}' not found`);
199
+ }
200
+
201
+ this.deliveryStreams.delete(deliveryStreamName);
202
+ this.persistDeliveryStreams();
203
+
204
+ this.audit.record({
205
+ eventName: 'DeleteDeliveryStream',
206
+ readOnly: false,
207
+ resources: [{
208
+ ARN: stream.deliveryStreamArn,
209
+ type: 'AWS::Firehose::DeliveryStream'
210
+ }],
211
+ requestParameters: { deliveryStreamName }
212
+ });
213
+
214
+ logger.info(`✅ Delivery Stream deletado: ${deliveryStreamName}`);
215
+
216
+ return {};
217
+ }
218
+
219
+ /**
220
+ * Coloca um record no delivery stream (PutRecord)
221
+ */
222
+ async putRecord(deliveryStreamName, record) {
223
+ const stream = this.deliveryStreams.get(deliveryStreamName);
224
+ if (!stream) {
225
+ throw new Error(`DeliveryStream '${deliveryStreamName}' not found`);
226
+ }
227
+
228
+ return this._withStreamLock(deliveryStreamName, () => {
229
+ const addedRecord = stream.addRecordToBuffer(record.Data);
230
+
231
+ this.persistDeliveryStreams();
232
+
233
+ this.audit.record({
234
+ eventName: 'PutRecord',
235
+ readOnly: false,
236
+ isDataEvent: true,
237
+ resources: [{
238
+ ARN: stream.deliveryStreamArn,
239
+ type: 'AWS::Firehose::DeliveryStream'
240
+ }],
241
+ requestParameters: { deliveryStreamName }
242
+ });
243
+
244
+ return {
245
+ RecordId: addedRecord.recordId,
246
+ Encrypted: false
247
+ };
248
+ });
249
+ }
250
+
251
+ /**
252
+ * Coloca múltiplos records no delivery stream (PutRecordBatch)
253
+ */
254
+ async putRecordBatch(deliveryStreamName, records) {
255
+ const stream = this.deliveryStreams.get(deliveryStreamName);
256
+ if (!stream) {
257
+ throw new Error(`DeliveryStream '${deliveryStreamName}' not found`);
258
+ }
259
+
260
+ return this._withStreamLock(deliveryStreamName, () => {
261
+ const results = [];
262
+ let failedCount = 0;
263
+
264
+ for (const record of records) {
265
+ try {
266
+ const addedRecord = stream.addRecordToBuffer(record.Data);
267
+ results.push({
268
+ RecordId: addedRecord.recordId,
269
+ ErrorCode: null,
270
+ ErrorMessage: null
271
+ });
272
+ } catch (error) {
273
+ failedCount++;
274
+ results.push({
275
+ RecordId: null,
276
+ ErrorCode: 'InternalFailure',
277
+ ErrorMessage: error.message
278
+ });
279
+ }
280
+ }
281
+
282
+ this.persistDeliveryStreams();
283
+
284
+ this.audit.record({
285
+ eventName: 'PutRecordBatch',
286
+ readOnly: false,
287
+ isDataEvent: true,
288
+ resources: [{
289
+ ARN: stream.deliveryStreamArn,
290
+ type: 'AWS::Firehose::DeliveryStream'
291
+ }],
292
+ requestParameters: { deliveryStreamName, recordCount: records.length }
293
+ });
294
+
295
+ return {
296
+ FailedPutCount: failedCount,
297
+ Encrypted: false,
298
+ Records: results
299
+ };
300
+ });
301
+ }
302
+
303
+ /**
304
+ * Obtém informações de destino
305
+ */
306
+ describeDestination(deliveryStreamName, destinationId) {
307
+ const stream = this.deliveryStreams.get(deliveryStreamName);
308
+ if (!stream) {
309
+ throw new Error(`DeliveryStream '${deliveryStreamName}' not found`);
310
+ }
311
+
312
+ const destination = stream.destinations.find(d => d.destinationId === destinationId);
313
+ if (!destination) {
314
+ throw new Error(`Destination '${destinationId}' not found`);
315
+ }
316
+
317
+ return {
318
+ DestinationDescription: destination
319
+ };
320
+ }
321
+
322
+ /**
323
+ * Atualiza delivery stream
324
+ */
325
+ updateDeliveryStream(deliveryStreamName, config) {
326
+ const stream = this.deliveryStreams.get(deliveryStreamName);
327
+ if (!stream) {
328
+ throw new Error(`DeliveryStream '${deliveryStreamName}' not found`);
329
+ }
330
+
331
+ // Atualiza configurações
332
+ if (config.deliveryStreamStatus) {
333
+ stream.deliveryStreamStatus = config.deliveryStreamStatus;
334
+ }
335
+
336
+ if (config.s3DestinationUpdate) {
337
+ const dest = stream.destinations[0];
338
+ if (dest && dest.type === 's3') {
339
+ Object.assign(dest, {
340
+ bufferingHints: config.s3DestinationUpdate.BufferingHints || dest.bufferingHints,
341
+ prefix: config.s3DestinationUpdate.Prefix || dest.prefix
342
+ });
343
+ }
344
+ }
345
+
346
+ stream.lastUpdateTimestamp = new Date().toISOString();
347
+ this.persistDeliveryStreams();
348
+
349
+ logger.info(`✅ Delivery Stream atualizado: ${deliveryStreamName}`);
350
+
351
+ return {};
352
+ }
353
+
354
+ /**
355
+ * Obtém estatísticas do simulator
356
+ */
357
+ getStats() {
358
+ const stats = {
359
+ deliveryStreamsCount: this.deliveryStreams.size,
360
+ totalRecords: 0,
361
+ totalDeliveredRecords: 0,
362
+ totalFailedRecords: 0,
363
+ deliveryStreams: []
364
+ };
365
+
366
+ for (const [streamName, stream] of this.deliveryStreams) {
367
+ const streamStats = stream.toJSON();
368
+ stats.totalRecords += streamStats.recordCount;
369
+ stats.totalDeliveredRecords += streamStats.deliveredRecordCount;
370
+ stats.totalFailedRecords += streamStats.failedDeliveryRecordCount;
371
+ stats.deliveryStreams.push(streamStats);
372
+ }
373
+
374
+ return stats;
375
+ }
376
+
377
+ /**
378
+ * Reset - limpa todos os dados
379
+ */
380
+ async reset() {
381
+ this.deliveryStreams.clear();
382
+ this.store.write('__delivery_streams__', {});
383
+
384
+ logger.info('✅ Kinesis Data Firehose Simulator resetado');
385
+ }
386
+ }
387
+
388
+ module.exports = KinesisFirehoseSimulator;
@@ -0,0 +1,163 @@
1
+ /**
2
+ * Kinesis Data Firehose Store - Persistência de delivery streams em JSON
3
+ */
4
+
5
+ const path = require('path');
6
+ const fs = require('fs');
7
+ const logger = require('../../utils/logger');
8
+
9
+ class KinesisFirehoseStore {
10
+ constructor(dataDir) {
11
+ this.dataDir = dataDir;
12
+ this.streamsDir = path.join(dataDir, 'kinesis-firehose');
13
+ this.metadataFile = path.join(this.streamsDir, '__delivery_streams__.json');
14
+
15
+ if (!fs.existsSync(this.streamsDir)) {
16
+ fs.mkdirSync(this.streamsDir, { recursive: true });
17
+ }
18
+ }
19
+
20
+ /**
21
+ * Carrega metadados de todos os delivery streams
22
+ */
23
+ loadMetadata() {
24
+ if (fs.existsSync(this.metadataFile)) {
25
+ try {
26
+ const data = fs.readFileSync(this.metadataFile, 'utf8');
27
+ return JSON.parse(data);
28
+ } catch (error) {
29
+ logger.warn(`Erro ao ler ${this.metadataFile}: ${error.message}`);
30
+ return {};
31
+ }
32
+ }
33
+ return {};
34
+ }
35
+
36
+ /**
37
+ * Salva metadados de todos os delivery streams
38
+ */
39
+ saveMetadata(metadata) {
40
+ try {
41
+ fs.writeFileSync(this.metadataFile, JSON.stringify(metadata, null, 2), 'utf8');
42
+ logger.debug(`Metadados de delivery streams salvos em ${this.metadataFile}`);
43
+ } catch (error) {
44
+ logger.error(`Erro ao salvar ${this.metadataFile}: ${error.message}`);
45
+ }
46
+ }
47
+
48
+ /**
49
+ * Obtém caminho do arquivo de delivery stream
50
+ */
51
+ getDeliveryStreamPath(deliveryStreamName) {
52
+ return path.join(this.streamsDir, `${deliveryStreamName}.json`);
53
+ }
54
+
55
+ /**
56
+ * Carrega records de um delivery stream
57
+ */
58
+ loadDeliveryStreamRecords(deliveryStreamName) {
59
+ const streamPath = this.getDeliveryStreamPath(deliveryStreamName);
60
+
61
+ if (!fs.existsSync(streamPath)) {
62
+ return [];
63
+ }
64
+
65
+ try {
66
+ const data = fs.readFileSync(streamPath, 'utf8');
67
+ return JSON.parse(data);
68
+ } catch (error) {
69
+ logger.warn(`Erro ao ler delivery stream ${deliveryStreamName}: ${error.message}`);
70
+ return [];
71
+ }
72
+ }
73
+
74
+ /**
75
+ * Salva records de um delivery stream
76
+ */
77
+ saveDeliveryStreamRecords(deliveryStreamName, records) {
78
+ try {
79
+ const streamPath = this.getDeliveryStreamPath(deliveryStreamName);
80
+ fs.writeFileSync(streamPath, JSON.stringify(records, null, 2), 'utf8');
81
+ logger.debug(`${records.length} records salvos no delivery stream ${deliveryStreamName}`);
82
+ } catch (error) {
83
+ logger.error(`Erro ao salvar delivery stream ${deliveryStreamName}: ${error.message}`);
84
+ }
85
+ }
86
+
87
+ /**
88
+ * Adiciona record ao delivery stream
89
+ */
90
+ addRecord(deliveryStreamName, record) {
91
+ const records = this.loadDeliveryStreamRecords(deliveryStreamName);
92
+ records.push(record);
93
+ this.saveDeliveryStreamRecords(deliveryStreamName, records);
94
+ }
95
+
96
+ /**
97
+ * Adiciona múltiplos records
98
+ */
99
+ addRecords(deliveryStreamName, newRecords) {
100
+ const records = this.loadDeliveryStreamRecords(deliveryStreamName);
101
+ records.push(...newRecords);
102
+ this.saveDeliveryStreamRecords(deliveryStreamName, records);
103
+ }
104
+
105
+ /**
106
+ * Remove record do delivery stream
107
+ */
108
+ removeRecord(deliveryStreamName, recordId) {
109
+ const records = this.loadDeliveryStreamRecords(deliveryStreamName);
110
+ const filtered = records.filter(r => r.recordId !== recordId);
111
+
112
+ if (filtered.length < records.length) {
113
+ this.saveDeliveryStreamRecords(deliveryStreamName, filtered);
114
+ return true;
115
+ }
116
+
117
+ return false;
118
+ }
119
+
120
+ /**
121
+ * Deleta delivery stream completamente
122
+ */
123
+ deleteDeliveryStream(deliveryStreamName) {
124
+ try {
125
+ const streamPath = this.getDeliveryStreamPath(deliveryStreamName);
126
+ if (fs.existsSync(streamPath)) {
127
+ fs.unlinkSync(streamPath);
128
+ logger.debug(`Delivery Stream ${deliveryStreamName} deletado`);
129
+ }
130
+ } catch (error) {
131
+ logger.error(`Erro ao deletar delivery stream ${deliveryStreamName}: ${error.message}`);
132
+ }
133
+ }
134
+
135
+ /**
136
+ * Calcula tamanho total do delivery stream em bytes
137
+ */
138
+ getDeliveryStreamSize(deliveryStreamName) {
139
+ const records = this.loadDeliveryStreamRecords(deliveryStreamName);
140
+ return records.reduce((sum, record) => sum + Buffer.byteLength(JSON.stringify(record.data)), 0);
141
+ }
142
+
143
+ /**
144
+ * Limpa todos os delivery streams
145
+ */
146
+ clear() {
147
+ try {
148
+ if (fs.existsSync(this.streamsDir)) {
149
+ const files = fs.readdirSync(this.streamsDir);
150
+ files.forEach(file => {
151
+ if (file.endsWith('.json')) {
152
+ fs.unlinkSync(path.join(this.streamsDir, file));
153
+ }
154
+ });
155
+ logger.debug('Todos os delivery streams foram limpos');
156
+ }
157
+ } catch (error) {
158
+ logger.error(`Erro ao limpar delivery streams: ${error.message}`);
159
+ }
160
+ }
161
+ }
162
+
163
+ module.exports = KinesisFirehoseStore;
@@ -1,10 +1,13 @@
1
+ const { setupServiceMiddleware } = require('../../middleware');
1
2
  'use strict';
2
3
 
3
4
  const express = require('express');
4
5
  const cors = require('cors');
5
6
 
6
7
  class KMSServer {
7
- constructor(simulator, logger, config) {
8
+ constructor(simulator, logger, config) {
9
+ this.organizationsService = null;
10
+ this.stsService = null;
8
11
  this.simulator = simulator;
9
12
  this.logger = logger;
10
13
  this.config = config;
@@ -146,9 +146,25 @@ class HandlerLoader {
146
146
  * Recarrega um handler (útil para hot reload)
147
147
  */
148
148
  static async reload(handlerPath, type = 'auto') {
149
- // Limpa cache
150
- const fullPath = path.resolve(process.cwd(), handlerPath);
151
- delete require.cache[require.resolve(fullPath)];
149
+ // Resolve path: try cwd first, then data dir (same logic as load())
150
+ let fullPath = path.resolve(process.cwd(), handlerPath);
151
+
152
+ if (!fs.existsSync(fullPath)) {
153
+ const dataDir = process.env.AWS_LOCAL_SIMULATOR_DATA_DIR;
154
+ if (dataDir) {
155
+ const dataPath = path.resolve(dataDir, 'lambda', handlerPath.replace(/^\.\//, ''));
156
+ if (fs.existsSync(dataPath)) {
157
+ fullPath = dataPath;
158
+ }
159
+ }
160
+ }
161
+
162
+ if (!fs.existsSync(fullPath)) {
163
+ throw new Error(`Handler não encontrado: ${fullPath}`);
164
+ }
165
+
166
+ // Limpa cache do require
167
+ delete require.cache[fullPath];
152
168
 
153
169
  // Recarrega
154
170
  return this.load(handlerPath, type);
@@ -158,7 +174,20 @@ class HandlerLoader {
158
174
  * Valida se um caminho de handler é válido
159
175
  */
160
176
  static isValid(handlerPath) {
161
- const fullPath = path.resolve(process.cwd(), handlerPath);
177
+ // Tenta resolver de cwd primeiro
178
+ let fullPath = path.resolve(process.cwd(), handlerPath);
179
+
180
+ if (!fs.existsSync(fullPath)) {
181
+ // Tenta resolver do data dir
182
+ const dataDir = process.env.AWS_LOCAL_SIMULATOR_DATA_DIR;
183
+ if (dataDir) {
184
+ const dataPath = path.resolve(dataDir, 'lambda', handlerPath.replace(/^\.\//, ''));
185
+ if (fs.existsSync(dataPath)) {
186
+ return true;
187
+ }
188
+ }
189
+ }
190
+
162
191
  return fs.existsSync(fullPath);
163
192
  }
164
193
 
@@ -166,7 +195,23 @@ class HandlerLoader {
166
195
  * Retorna informações sobre o handler
167
196
  */
168
197
  static async getInfo(handlerPath) {
169
- const fullPath = path.resolve(process.cwd(), handlerPath);
198
+ // Resolve path: try cwd first, then data dir (same logic as load())
199
+ let fullPath = path.resolve(process.cwd(), handlerPath);
200
+
201
+ if (!fs.existsSync(fullPath)) {
202
+ const dataDir = process.env.AWS_LOCAL_SIMULATOR_DATA_DIR;
203
+ if (dataDir) {
204
+ const dataPath = path.resolve(dataDir, 'lambda', handlerPath.replace(/^\.\//, ''));
205
+ if (fs.existsSync(dataPath)) {
206
+ fullPath = dataPath;
207
+ }
208
+ }
209
+ }
210
+
211
+ if (!fs.existsSync(fullPath)) {
212
+ throw new Error(`Handler não encontrado: ${fullPath}`);
213
+ }
214
+
170
215
  const type = this.detectType(fullPath);
171
216
  const stats = fs.statSync(fullPath);
172
217
 
@@ -39,6 +39,32 @@ class LambdaService {
39
39
 
40
40
  const cw = server.getService('cloudwatch');
41
41
  if (cw?.simulator) this.simulator.cloudwatchSimulator = cw.simulator;
42
+
43
+ // IAM enforcement middleware (only when IAM service is active)
44
+ const iamService = server.getService('iam');
45
+ if (iamService && iamService.getSimulator()) {
46
+ const { createIAMEnforcementMiddleware } = require('../iam/middleware');
47
+ const iamSimulator = iamService.getSimulator();
48
+ this.server.iamMiddleware = createIAMEnforcementMiddleware({
49
+ iamSimulator,
50
+ servicePrefix: 'lambda',
51
+ extractAction: (req) => {
52
+ if (req.path.includes('/invocations')) return 'InvokeFunction';
53
+ if (req.method === 'POST') return 'CreateFunction';
54
+ if (req.method === 'GET') return 'ListFunctions';
55
+ if (req.method === 'DELETE') return 'DeleteFunction';
56
+ if (req.method === 'PUT') return 'UpdateFunctionConfiguration';
57
+ return 'Unknown';
58
+ },
59
+ extractResource: (req) => {
60
+ const functionName = req.params?.functionName || req.path.split('/functions/')[1]?.split('/')[0] || 'unknown';
61
+ return `arn:aws:lambda:us-east-1:123456789012:function:${functionName}`;
62
+ },
63
+ formatError: (res, code, message) => {
64
+ res.status(403).json({ Message: message, Type: code });
65
+ }
66
+ });
67
+ }
42
68
  }
43
69
 
44
70
  async start() {