@gugananuvem/aws-local-simulator 1.0.35 → 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.
- package/README.md +662 -658
- package/package.json +10 -3
- package/src/config/default-config.js +71 -0
- package/src/config/env-loader.js +4 -2
- package/src/middleware/authorization.js +271 -0
- package/src/middleware/cloudtrail-logger.js +333 -0
- package/src/middleware/error-handler.js +138 -0
- package/src/middleware/index.js +79 -0
- package/src/middleware/policy-enforcement.js +407 -0
- package/src/server.js +74 -4
- package/src/services/acm/index.js +66 -0
- package/src/services/acm/models/certificate.js +202 -0
- package/src/services/acm/server.js +224 -0
- package/src/services/acm/simulator.js +293 -0
- package/src/services/alb/index.js +104 -0
- package/src/services/alb/models/loadBalancer.js +88 -0
- package/src/services/alb/models/targetGroup.js +128 -0
- package/src/services/alb/server.js +160 -0
- package/src/services/alb/simulator.js +262 -0
- package/src/services/apigateway/index.js +22 -0
- package/src/services/apigateway/server.js +247 -35
- package/src/services/apigateway/simulator.js +780 -26
- package/src/services/asg/index.js +100 -0
- package/src/services/asg/models/autoScalingGroup.js +110 -0
- package/src/services/asg/models/launchTemplate.js +41 -0
- package/src/services/asg/models/scalingPolicy.js +51 -0
- package/src/services/asg/server.js +236 -0
- package/src/services/asg/simulator.js +410 -0
- package/src/services/aurora/cluster-manager.js +731 -0
- package/src/services/aurora/data-api.js +514 -0
- package/src/services/aurora/db-engine.js +270 -0
- package/src/services/aurora/index.js +159 -0
- package/src/services/aurora/rds-api.js +189 -0
- package/src/services/aurora/server.js +213 -0
- package/src/services/aurora/sql-translator.js +334 -0
- package/src/services/cloudformation/index.js +2 -2
- package/src/services/cloudformation/server.js +1 -0
- package/src/services/cloudfront/cache-engine.js +225 -0
- package/src/services/cloudfront/geo-restrictions.js +377 -0
- package/src/services/cloudfront/index.js +220 -0
- package/src/services/cloudfront/models/behavior.js +158 -0
- package/src/services/cloudfront/models/distribution.js +284 -0
- package/src/services/cloudfront/models/index.js +99 -0
- package/src/services/cloudfront/models/invalidation.js +109 -0
- package/src/services/cloudfront/models/origin.js +155 -0
- package/src/services/cloudfront/origin-router.js +552 -0
- package/src/services/cloudfront/origin-validator.js +398 -0
- package/src/services/cloudfront/server.js +512 -0
- package/src/services/cloudfront/signed-url-generator.js +377 -0
- package/src/services/cloudfront/simulator.js +425 -0
- package/src/services/cloudfront/store.js +326 -0
- package/src/services/cloudshell/env-builder.js +27 -0
- package/src/services/cloudshell/index.js +48 -0
- package/src/services/cloudshell/routes.js +60 -0
- package/src/services/cloudshell/session-manager.js +226 -0
- package/src/services/cloudshell/static/.gitkeep +0 -0
- package/src/services/cloudshell/static/index.html +406 -0
- package/src/services/cloudshell/websocket-handler.js +107 -0
- package/src/services/cloudtrail/index.js +2 -3
- package/src/services/cloudtrail/server.js +68 -0
- package/src/services/cognito/server.js +4 -1
- package/src/services/dynamodb/server.js +4 -1
- package/src/services/dynamodb/simulator.js +2 -1
- package/src/services/ec2/index.js +101 -0
- package/src/services/ec2/instanceServer.js +311 -0
- package/src/services/ec2/models/ami.js +39 -0
- package/src/services/ec2/models/instance.js +98 -0
- package/src/services/ec2/models/networkInterface.js +74 -0
- package/src/services/ec2/models/securityGroup.js +71 -0
- package/src/services/ec2/server.js +350 -0
- package/src/services/ec2/simulator.js +436 -0
- package/src/services/eventbridge/server.js +36 -0
- package/src/services/eventbridge/simulator.js +230 -45
- package/src/services/iam/index.js +65 -0
- package/src/services/iam/middleware.js +99 -0
- package/src/services/iam/server.js +331 -0
- package/src/services/iam/simulator.js +1432 -0
- package/src/services/index.js +28 -0
- package/src/services/kinesis-analytics/index.js +105 -0
- package/src/services/kinesis-analytics/models/application.js +325 -0
- package/src/services/kinesis-analytics/models/index.js +12 -0
- package/src/services/kinesis-analytics/server.js +250 -0
- package/src/services/kinesis-analytics/simulator.js +407 -0
- package/src/services/kinesis-analytics/store.js +176 -0
- package/src/services/kinesis-data-streams/index.js +106 -0
- package/src/services/kinesis-data-streams/models/index.js +12 -0
- package/src/services/kinesis-data-streams/models/record.js +70 -0
- package/src/services/kinesis-data-streams/models/stream.js +280 -0
- package/src/services/kinesis-data-streams/server.js +255 -0
- package/src/services/kinesis-data-streams/simulator.js +514 -0
- package/src/services/kinesis-data-streams/store.js +163 -0
- package/src/services/kinesis-firehose/index.js +106 -0
- package/src/services/kinesis-firehose/models/delivery-stream.js +265 -0
- package/src/services/kinesis-firehose/models/index.js +10 -0
- package/src/services/kinesis-firehose/server.js +215 -0
- package/src/services/kinesis-firehose/simulator.js +388 -0
- package/src/services/kinesis-firehose/store.js +163 -0
- package/src/services/kms/server.js +4 -1
- package/src/services/lambda/index.js +26 -0
- package/src/services/lambda/server.js +12 -1
- package/src/services/lambda/simulator.js +4 -0
- package/src/services/opensearch/index.js +105 -0
- package/src/services/opensearch/models/document.js +115 -0
- package/src/services/opensearch/models/index.js +73 -0
- package/src/services/opensearch/server.js +310 -0
- package/src/services/opensearch/simulator.js +605 -0
- package/src/services/opensearch/store.js +162 -0
- package/src/services/organizations/control-tower-simulator.js +321 -0
- package/src/services/organizations/control-tower-store.js +182 -0
- package/src/services/organizations/iam-integration.js +413 -0
- package/src/services/organizations/index.js +172 -0
- package/src/services/organizations/models/index.js +13 -0
- package/src/services/organizations/models/invitation.js +48 -0
- package/src/services/organizations/models/organization.js +566 -0
- package/src/services/organizations/models/role.js +244 -0
- package/src/services/organizations/models/scp.js +131 -0
- package/src/services/organizations/server.js +736 -0
- package/src/services/organizations/simulator.js +593 -0
- package/src/services/organizations/store.js +235 -0
- package/src/services/organizations/vpc-integration.js +453 -0
- package/src/services/route53/index.js +67 -0
- package/src/services/route53/models/healthCheck.js +101 -0
- package/src/services/route53/models/hostedZone.js +37 -0
- package/src/services/route53/models/resourceRecord.js +106 -0
- package/src/services/route53/server.js +841 -0
- package/src/services/route53/simulator.js +350 -0
- package/src/services/s3/index.js +62 -0
- package/src/services/s3/presigned-url-generator.js +337 -0
- package/src/services/s3/server.js +231 -0
- package/src/services/s3/simulator.js +551 -4
- package/src/services/sns/index.js +31 -0
- package/src/services/sns/server.js +9 -0
- package/src/services/sqs/index.js +38 -0
- package/src/services/sqs/server.js +57 -4
- package/src/services/sqs/simulator.js +423 -33
- package/src/services/sts/simulator.js +209 -17
- package/src/services/vpc/README.md +459 -0
- package/src/services/vpc/ec2ResponseFormatter.js +431 -0
- package/src/services/vpc/index.js +90 -0
- package/src/services/vpc/models/internetGateway.js +71 -0
- package/src/services/vpc/models/routeTable.js +160 -0
- package/src/services/vpc/models/subnet.js +48 -0
- package/src/services/vpc/models/vpc.js +37 -0
- package/src/services/vpc/models/vpcEndpoint.js +100 -0
- package/src/services/vpc/models/vpcGateway.js +134 -0
- package/src/services/vpc/server.js +836 -0
- package/src/services/vpc/simulator.js +613 -0
- package/src/utils/policy-cache.js +215 -0
- package/aws-config +0 -154
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kinesis Data Firehose Service - Ponto de entrada
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const KinesisFirehoseServer = require('./server');
|
|
6
|
+
const KinesisFirehoseSimulator = require('./simulator');
|
|
7
|
+
|
|
8
|
+
class KinesisFirehoseService {
|
|
9
|
+
constructor(config) {
|
|
10
|
+
this.config = config;
|
|
11
|
+
this.name = 'kinesis-firehose';
|
|
12
|
+
this.port = config.ports['kinesis-firehose'] || 4569;
|
|
13
|
+
this.server = null;
|
|
14
|
+
this.simulator = null;
|
|
15
|
+
this.isRunning = false;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async initialize() {
|
|
19
|
+
const logger = require('../../utils/logger');
|
|
20
|
+
logger.debug(`Inicializando Kinesis Firehose Service na porta ${this.port}...`);
|
|
21
|
+
|
|
22
|
+
this.simulator = new KinesisFirehoseSimulator(this.config);
|
|
23
|
+
await this.simulator.initialize();
|
|
24
|
+
|
|
25
|
+
this.server = new KinesisFirehoseServer(this.port, this.config);
|
|
26
|
+
this.server.simulator = this.simulator;
|
|
27
|
+
|
|
28
|
+
await this.server.initialize();
|
|
29
|
+
|
|
30
|
+
logger.debug('Kinesis Firehose Service inicializado');
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
injectDependencies(server) {
|
|
34
|
+
// Aqui você pode injetar dependências de outros serviços se necessário
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async start() {
|
|
38
|
+
const logger = require('../../utils/logger');
|
|
39
|
+
|
|
40
|
+
if (this.isRunning) {
|
|
41
|
+
logger.warn('Kinesis Firehose Service já está em execução');
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
try {
|
|
46
|
+
await this.server.start();
|
|
47
|
+
this.isRunning = true;
|
|
48
|
+
logger.info('Kinesis Firehose Service iniciado com sucesso');
|
|
49
|
+
} catch (error) {
|
|
50
|
+
logger.error(`Erro ao iniciar Kinesis Firehose Service: ${error.message}`);
|
|
51
|
+
throw error;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async stop() {
|
|
56
|
+
const logger = require('../../utils/logger');
|
|
57
|
+
|
|
58
|
+
if (!this.isRunning) {
|
|
59
|
+
logger.warn('Kinesis Firehose Service não está em execução');
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
try {
|
|
64
|
+
await this.server.stop();
|
|
65
|
+
this.isRunning = false;
|
|
66
|
+
logger.info('Kinesis Firehose Service parado com sucesso');
|
|
67
|
+
} catch (error) {
|
|
68
|
+
logger.error(`Erro ao parar Kinesis Firehose Service: ${error.message}`);
|
|
69
|
+
throw error;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async reset() {
|
|
74
|
+
const logger = require('../../utils/logger');
|
|
75
|
+
try {
|
|
76
|
+
await this.simulator.reset();
|
|
77
|
+
logger.info('Kinesis Firehose Service resetado');
|
|
78
|
+
} catch (error) {
|
|
79
|
+
logger.error(`Erro ao resetar Kinesis Firehose Service: ${error.message}`);
|
|
80
|
+
throw error;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
getSimulator() {
|
|
85
|
+
return this.simulator;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
getStatus() {
|
|
89
|
+
const stats = this.simulator?.getStats() || { deliveryStreamsCount: 0, totalRecords: 0, totalDeliveredRecords: 0 };
|
|
90
|
+
|
|
91
|
+
return {
|
|
92
|
+
name: this.name,
|
|
93
|
+
running: this.isRunning,
|
|
94
|
+
port: this.port,
|
|
95
|
+
endpoint: `http://localhost:${this.port}`,
|
|
96
|
+
resources: {
|
|
97
|
+
deliveryStreamsCount: stats.deliveryStreamsCount,
|
|
98
|
+
totalRecords: stats.totalRecords,
|
|
99
|
+
totalDeliveredRecords: stats.totalDeliveredRecords,
|
|
100
|
+
totalFailedRecords: stats.totalFailedRecords
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
module.exports = KinesisFirehoseService;
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kinesis Data Firehose - DeliveryStream Model
|
|
3
|
+
* Representa um stream de entrega (delivery stream)
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const { v4: uuidv4 } = require('uuid');
|
|
7
|
+
|
|
8
|
+
class DeliveryStream {
|
|
9
|
+
constructor(deliveryStreamName, config = {}) {
|
|
10
|
+
this.deliveryStreamName = deliveryStreamName;
|
|
11
|
+
this.deliveryStreamArn = `arn:aws:firehose:us-east-1:123456789012:deliverystream/${deliveryStreamName}`;
|
|
12
|
+
this.deliveryStreamStatus = 'ACTIVE';
|
|
13
|
+
this.deliveryStreamType = config.deliveryStreamType || 'DirectPut';
|
|
14
|
+
this.deliveryStreamCreationTimestamp = new Date().toISOString();
|
|
15
|
+
this.lastUpdateTimestamp = new Date().toISOString();
|
|
16
|
+
|
|
17
|
+
// Fonte de dados (pode ser Kinesis Data Stream)
|
|
18
|
+
this.sourceDescription = config.sourceDescription || null;
|
|
19
|
+
|
|
20
|
+
// Destinos (S3, HTTP, Redshift, etc)
|
|
21
|
+
this.destinations = [];
|
|
22
|
+
this.failedDestinationCount = 0;
|
|
23
|
+
|
|
24
|
+
// Configurações de destino
|
|
25
|
+
this.s3DestinationDescription = config.s3DestinationDescription || null;
|
|
26
|
+
this.httpDestinationDescription = config.httpDestinationDescription || null;
|
|
27
|
+
this.redshiftDestinationDescription = config.redshiftDestinationDescription || null;
|
|
28
|
+
|
|
29
|
+
// Versão
|
|
30
|
+
this.versionId = config.versionId || '1';
|
|
31
|
+
|
|
32
|
+
// Buffer de entrega
|
|
33
|
+
this.deliveryBuffer = new DeliveryBuffer();
|
|
34
|
+
|
|
35
|
+
// Estatísticas
|
|
36
|
+
this.recordCount = 0;
|
|
37
|
+
this.deliveredRecordCount = 0;
|
|
38
|
+
this.failedDeliveryRecordCount = 0;
|
|
39
|
+
this.incomingRecords = 0;
|
|
40
|
+
this.incomingBytes = 0;
|
|
41
|
+
this.deliveredBytes = 0;
|
|
42
|
+
|
|
43
|
+
// Tags
|
|
44
|
+
this.tags = config.tags || {};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Adiciona destino
|
|
49
|
+
*/
|
|
50
|
+
addDestination(destination) {
|
|
51
|
+
this.destinations.push(destination);
|
|
52
|
+
if (destination.type === 's3') {
|
|
53
|
+
this.s3DestinationDescription = destination;
|
|
54
|
+
} else if (destination.type === 'http') {
|
|
55
|
+
this.httpDestinationDescription = destination;
|
|
56
|
+
} else if (destination.type === 'redshift') {
|
|
57
|
+
this.redshiftDestinationDescription = destination;
|
|
58
|
+
}
|
|
59
|
+
this.lastUpdateTimestamp = new Date().toISOString();
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Obtém primeira destination
|
|
64
|
+
*/
|
|
65
|
+
getFirstDestination() {
|
|
66
|
+
return this.destinations[0] || null;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Adiciona record ao buffer
|
|
71
|
+
*/
|
|
72
|
+
addRecordToBuffer(recordData) {
|
|
73
|
+
if (!recordData) {
|
|
74
|
+
throw new Error('recordData is required');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const record = {
|
|
78
|
+
recordId: uuidv4(),
|
|
79
|
+
data: recordData,
|
|
80
|
+
timestamp: new Date().toISOString(),
|
|
81
|
+
deliveryAttempts: 0,
|
|
82
|
+
deliveryStatus: 'PENDING'
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
this.deliveryBuffer.addRecord(record);
|
|
86
|
+
this.recordCount++;
|
|
87
|
+
this.incomingRecords++;
|
|
88
|
+
|
|
89
|
+
// Calcula tamanho do record
|
|
90
|
+
try {
|
|
91
|
+
const dataStr = typeof recordData === 'string' ? recordData : JSON.stringify(recordData);
|
|
92
|
+
this.incomingBytes += Buffer.byteLength(dataStr);
|
|
93
|
+
} catch (e) {
|
|
94
|
+
// Fallback para tamanho aproximado se conversão falhar
|
|
95
|
+
this.incomingBytes += 100;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return record;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Obtém records pendentes do buffer
|
|
103
|
+
*/
|
|
104
|
+
getPendingRecords(limit = 500) {
|
|
105
|
+
return this.deliveryBuffer.getPendingRecords(limit);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Marca record como entregue
|
|
110
|
+
*/
|
|
111
|
+
markRecordAsDelivered(recordId) {
|
|
112
|
+
const record = this.deliveryBuffer.getRecord(recordId);
|
|
113
|
+
if (record) {
|
|
114
|
+
record.deliveryStatus = 'DELIVERED';
|
|
115
|
+
this.deliveredRecordCount++;
|
|
116
|
+
this.deliveredBytes += Buffer.byteLength(JSON.stringify(record.data));
|
|
117
|
+
this.lastUpdateTimestamp = new Date().toISOString();
|
|
118
|
+
return true;
|
|
119
|
+
}
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Marca record como falha de entrega
|
|
125
|
+
*/
|
|
126
|
+
markRecordAsFailed(recordId) {
|
|
127
|
+
const record = this.deliveryBuffer.getRecord(recordId);
|
|
128
|
+
if (record) {
|
|
129
|
+
record.deliveryAttempts++;
|
|
130
|
+
record.deliveryStatus = 'FAILED';
|
|
131
|
+
this.failedDeliveryRecordCount++;
|
|
132
|
+
this.lastUpdateTimestamp = new Date().toISOString();
|
|
133
|
+
return true;
|
|
134
|
+
}
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Retorna descrição do delivery stream
|
|
140
|
+
*/
|
|
141
|
+
describe() {
|
|
142
|
+
return {
|
|
143
|
+
DeliveryStreamDescription: {
|
|
144
|
+
DeliveryStreamName: this.deliveryStreamName,
|
|
145
|
+
DeliveryStreamArn: this.deliveryStreamArn,
|
|
146
|
+
DeliveryStreamStatus: this.deliveryStreamStatus,
|
|
147
|
+
DeliveryStreamType: this.deliveryStreamType,
|
|
148
|
+
VersionId: this.versionId,
|
|
149
|
+
CreationTimestamp: this.deliveryStreamCreationTimestamp,
|
|
150
|
+
LastUpdateTimestamp: this.lastUpdateTimestamp,
|
|
151
|
+
Source: this.sourceDescription,
|
|
152
|
+
Destinations: this.destinations.map(d => ({
|
|
153
|
+
DestinationId: d.destinationId || 'destination-1',
|
|
154
|
+
DestinationStatus: d.destinationStatus || 'ACTIVE',
|
|
155
|
+
S3DestinationDescription: d.type === 's3' ? d : undefined,
|
|
156
|
+
HttpEndpointDestinationDescription: d.type === 'http' ? d : undefined,
|
|
157
|
+
RedshiftDestinationDescription: d.type === 'redshift' ? d : undefined
|
|
158
|
+
})),
|
|
159
|
+
HasMoreDestinations: false,
|
|
160
|
+
FailureDescription: null
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
toJSON() {
|
|
166
|
+
return {
|
|
167
|
+
deliveryStreamName: this.deliveryStreamName,
|
|
168
|
+
deliveryStreamArn: this.deliveryStreamArn,
|
|
169
|
+
deliveryStreamStatus: this.deliveryStreamStatus,
|
|
170
|
+
deliveryStreamType: this.deliveryStreamType,
|
|
171
|
+
creationTimestamp: this.deliveryStreamCreationTimestamp,
|
|
172
|
+
lastUpdateTimestamp: this.lastUpdateTimestamp,
|
|
173
|
+
recordCount: this.recordCount,
|
|
174
|
+
deliveredRecordCount: this.deliveredRecordCount,
|
|
175
|
+
failedDeliveryRecordCount: this.failedDeliveryRecordCount,
|
|
176
|
+
incomingRecords: this.incomingRecords,
|
|
177
|
+
incomingBytes: this.incomingBytes,
|
|
178
|
+
deliveredBytes: this.deliveredBytes
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* DeliveryBuffer Model - Buffer de records a serem entregues
|
|
185
|
+
*/
|
|
186
|
+
class DeliveryBuffer {
|
|
187
|
+
constructor(config = {}) {
|
|
188
|
+
this.bufferSizeMB = config.bufferSizeMB || 128;
|
|
189
|
+
this.bufferIntervalSeconds = config.bufferIntervalSeconds || 60;
|
|
190
|
+
this.records = new Map();
|
|
191
|
+
this.createdAt = new Date();
|
|
192
|
+
this.lastFlushTime = new Date();
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Adiciona record ao buffer
|
|
197
|
+
*/
|
|
198
|
+
addRecord(record) {
|
|
199
|
+
this.records.set(record.recordId, record);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Obtém record
|
|
204
|
+
*/
|
|
205
|
+
getRecord(recordId) {
|
|
206
|
+
return this.records.get(recordId);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Obtém records pendentes
|
|
211
|
+
*/
|
|
212
|
+
getPendingRecords(limit = 500) {
|
|
213
|
+
const pending = Array.from(this.records.values())
|
|
214
|
+
.filter(r => r.deliveryStatus === 'PENDING')
|
|
215
|
+
.slice(0, limit);
|
|
216
|
+
return pending;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Obtém todos os records
|
|
221
|
+
*/
|
|
222
|
+
getAllRecords() {
|
|
223
|
+
return Array.from(this.records.values());
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Remove record
|
|
228
|
+
*/
|
|
229
|
+
removeRecord(recordId) {
|
|
230
|
+
return this.records.delete(recordId);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Limpa buffer (para reset)
|
|
235
|
+
*/
|
|
236
|
+
clear() {
|
|
237
|
+
this.records.clear();
|
|
238
|
+
this.createdAt = new Date();
|
|
239
|
+
this.lastFlushTime = new Date();
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Obtém estatísticas do buffer
|
|
244
|
+
*/
|
|
245
|
+
getStats() {
|
|
246
|
+
const records = Array.from(this.records.values());
|
|
247
|
+
const totalRecords = records.length;
|
|
248
|
+
const pendingRecords = records.filter(r => r.deliveryStatus === 'PENDING').length;
|
|
249
|
+
const deliveredRecords = records.filter(r => r.deliveryStatus === 'DELIVERED').length;
|
|
250
|
+
const failedRecords = records.filter(r => r.deliveryStatus === 'FAILED').length;
|
|
251
|
+
|
|
252
|
+
return {
|
|
253
|
+
totalRecords,
|
|
254
|
+
pendingRecords,
|
|
255
|
+
deliveredRecords,
|
|
256
|
+
failedRecords,
|
|
257
|
+
bufferAge: Date.now() - this.createdAt.getTime()
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
module.exports = {
|
|
263
|
+
DeliveryStream,
|
|
264
|
+
DeliveryBuffer
|
|
265
|
+
};
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kinesis Data Firehose Server - Servidor HTTP REST/JSON para Firehose
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const express = require('express');
|
|
6
|
+
const cors = require('cors');
|
|
7
|
+
const logger = require('../../utils/logger');
|
|
8
|
+
|
|
9
|
+
class KinesisFirehoseServer {
|
|
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(cors());
|
|
21
|
+
this.app.use(express.json({ limit: '10mb' }));
|
|
22
|
+
|
|
23
|
+
// Custom header parsing para AWS SDK
|
|
24
|
+
this.app.use((req, res, next) => {
|
|
25
|
+
const target = req.headers['x-amz-target'];
|
|
26
|
+
if (target) {
|
|
27
|
+
const [, action] = target.split('.');
|
|
28
|
+
req.action = action;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
logger.debug(`Kinesis Firehose: ${req.method} ${req.path} - Action: ${req.action || 'none'}`);
|
|
32
|
+
next();
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async initialize() {
|
|
37
|
+
logger.debug(`Inicializando Kinesis Firehose Server na porta ${this.port}`);
|
|
38
|
+
this.setupRoutes();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
setupRoutes() {
|
|
42
|
+
this.app.post('/', (req, res) => {
|
|
43
|
+
const action = req.action || req.body.Action;
|
|
44
|
+
|
|
45
|
+
try {
|
|
46
|
+
switch (action) {
|
|
47
|
+
case 'CreateDeliveryStream':
|
|
48
|
+
return this.createDeliveryStream(req, res);
|
|
49
|
+
case 'DescribeDeliveryStream':
|
|
50
|
+
return this.describeDeliveryStream(req, res);
|
|
51
|
+
case 'ListDeliveryStreams':
|
|
52
|
+
return this.listDeliveryStreams(req, res);
|
|
53
|
+
case 'DeleteDeliveryStream':
|
|
54
|
+
return this.deleteDeliveryStream(req, res);
|
|
55
|
+
case 'PutRecord':
|
|
56
|
+
return this.putRecord(req, res);
|
|
57
|
+
case 'PutRecordBatch':
|
|
58
|
+
return this.putRecordBatch(req, res);
|
|
59
|
+
case 'UpdateDeliveryStream':
|
|
60
|
+
return this.updateDeliveryStream(req, res);
|
|
61
|
+
case 'DescribeDestination':
|
|
62
|
+
return this.describeDestination(req, res);
|
|
63
|
+
default:
|
|
64
|
+
res.status(400).json({ __type: 'UnknownAction', message: `Unknown action: ${action}` });
|
|
65
|
+
}
|
|
66
|
+
} catch (error) {
|
|
67
|
+
res.status(400).json({ __type: 'InternalFailure', message: error.message });
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
// Health check
|
|
72
|
+
this.app.get('/health', (req, res) => {
|
|
73
|
+
res.json({
|
|
74
|
+
status: 'healthy',
|
|
75
|
+
service: 'kinesis-firehose',
|
|
76
|
+
port: this.port,
|
|
77
|
+
stats: this.simulator.getStats()
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async createDeliveryStream(req, res) {
|
|
83
|
+
const {
|
|
84
|
+
DeliveryStreamName,
|
|
85
|
+
DeliveryStreamType = 'DirectPut',
|
|
86
|
+
S3DestinationConfiguration,
|
|
87
|
+
HttpEndpointDestinationConfiguration,
|
|
88
|
+
RedshiftDestinationConfiguration
|
|
89
|
+
} = req.body;
|
|
90
|
+
|
|
91
|
+
if (!DeliveryStreamName) {
|
|
92
|
+
throw new Error('DeliveryStreamName is required');
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const result = await this.simulator.createDeliveryStream(DeliveryStreamName, {
|
|
96
|
+
deliveryStreamType: DeliveryStreamType,
|
|
97
|
+
s3DestinationConfiguration: S3DestinationConfiguration,
|
|
98
|
+
httpEndpointDestinationConfiguration: HttpEndpointDestinationConfiguration,
|
|
99
|
+
redshiftDestinationConfiguration: RedshiftDestinationConfiguration
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
res.status(201).json(result);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async describeDeliveryStream(req, res) {
|
|
106
|
+
const { DeliveryStreamName } = req.body;
|
|
107
|
+
|
|
108
|
+
if (!DeliveryStreamName) {
|
|
109
|
+
throw new Error('DeliveryStreamName is required');
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const result = this.simulator.describeDeliveryStream(DeliveryStreamName);
|
|
113
|
+
res.json(result);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
async listDeliveryStreams(req, res) {
|
|
117
|
+
const { Limit, ExclusiveStartDeliveryStreamName } = req.body;
|
|
118
|
+
|
|
119
|
+
const result = this.simulator.listDeliveryStreams({
|
|
120
|
+
Limit,
|
|
121
|
+
ExclusiveStartDeliveryStreamName
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
res.json(result);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
async deleteDeliveryStream(req, res) {
|
|
128
|
+
const { DeliveryStreamName } = req.body;
|
|
129
|
+
|
|
130
|
+
if (!DeliveryStreamName) {
|
|
131
|
+
throw new Error('DeliveryStreamName is required');
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const result = await this.simulator.deleteDeliveryStream(DeliveryStreamName);
|
|
135
|
+
res.json(result);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
async putRecord(req, res) {
|
|
139
|
+
const { DeliveryStreamName, Record } = req.body;
|
|
140
|
+
|
|
141
|
+
if (!DeliveryStreamName || !Record) {
|
|
142
|
+
throw new Error('DeliveryStreamName and Record are required');
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const result = await this.simulator.putRecord(DeliveryStreamName, Record);
|
|
146
|
+
res.json(result);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
async putRecordBatch(req, res) {
|
|
150
|
+
const { DeliveryStreamName, Records } = req.body;
|
|
151
|
+
|
|
152
|
+
if (!DeliveryStreamName || !Records || !Array.isArray(Records)) {
|
|
153
|
+
throw new Error('DeliveryStreamName and Records array are required');
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const result = await this.simulator.putRecordBatch(DeliveryStreamName, Records);
|
|
157
|
+
res.json(result);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
async updateDeliveryStream(req, res) {
|
|
161
|
+
const { DeliveryStreamName, ...config } = req.body;
|
|
162
|
+
|
|
163
|
+
if (!DeliveryStreamName) {
|
|
164
|
+
throw new Error('DeliveryStreamName is required');
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const result = this.simulator.updateDeliveryStream(DeliveryStreamName, config);
|
|
168
|
+
res.json(result);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
async describeDestination(req, res) {
|
|
172
|
+
const { DeliveryStreamName, DestinationId } = req.body;
|
|
173
|
+
|
|
174
|
+
if (!DeliveryStreamName || !DestinationId) {
|
|
175
|
+
throw new Error('DeliveryStreamName and DestinationId are required');
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const result = this.simulator.describeDestination(DeliveryStreamName, DestinationId);
|
|
179
|
+
res.json(result);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
async start() {
|
|
183
|
+
return new Promise((resolve, reject) => {
|
|
184
|
+
try {
|
|
185
|
+
this.server = this.app.listen(this.port, () => {
|
|
186
|
+
logger.info(`✅ Kinesis Firehose Server iniciado na porta ${this.port}`);
|
|
187
|
+
resolve();
|
|
188
|
+
});
|
|
189
|
+
} catch (error) {
|
|
190
|
+
logger.error(`❌ Erro ao iniciar Kinesis Firehose Server: ${error.message}`);
|
|
191
|
+
reject(error);
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
async stop() {
|
|
197
|
+
return new Promise((resolve, reject) => {
|
|
198
|
+
if (this.server) {
|
|
199
|
+
this.server.close((err) => {
|
|
200
|
+
if (err) {
|
|
201
|
+
logger.error(`Erro ao parar Kinesis Firehose Server: ${err.message}`);
|
|
202
|
+
reject(err);
|
|
203
|
+
} else {
|
|
204
|
+
logger.info('Kinesis Firehose Server parado');
|
|
205
|
+
resolve();
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
} else {
|
|
209
|
+
resolve();
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
module.exports = KinesisFirehoseServer;
|