@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,250 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kinesis Data Analytics Server - Servidor HTTP REST/JSON para Analytics
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const express = require('express');
|
|
6
|
+
const cors = require('cors');
|
|
7
|
+
const logger = require('../../utils/logger');
|
|
8
|
+
|
|
9
|
+
class KinesisAnalyticsServer {
|
|
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 Analytics: ${req.method} ${req.path} - Action: ${req.action || 'none'}`);
|
|
32
|
+
next();
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async initialize() {
|
|
37
|
+
logger.debug(`Inicializando Kinesis Data Analytics 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 'CreateApplication':
|
|
48
|
+
return this.createApplication(req, res);
|
|
49
|
+
case 'DescribeApplication':
|
|
50
|
+
return this.describeApplication(req, res);
|
|
51
|
+
case 'ListApplications':
|
|
52
|
+
return this.listApplications(req, res);
|
|
53
|
+
case 'DeleteApplication':
|
|
54
|
+
return this.deleteApplication(req, res);
|
|
55
|
+
case 'AddApplicationInput':
|
|
56
|
+
return this.addApplicationInput(req, res);
|
|
57
|
+
case 'DeleteApplicationInput':
|
|
58
|
+
return this.deleteApplicationInput(req, res);
|
|
59
|
+
case 'AddApplicationOutput':
|
|
60
|
+
return this.addApplicationOutput(req, res);
|
|
61
|
+
case 'DeleteApplicationOutput':
|
|
62
|
+
return this.deleteApplicationOutput(req, res);
|
|
63
|
+
case 'UpdateApplicationCode':
|
|
64
|
+
return this.updateApplicationCode(req, res);
|
|
65
|
+
case 'StartApplication':
|
|
66
|
+
return this.startApplication(req, res);
|
|
67
|
+
case 'StopApplication':
|
|
68
|
+
return this.stopApplication(req, res);
|
|
69
|
+
default:
|
|
70
|
+
res.status(400).json({ __type: 'UnknownAction', message: `Unknown action: ${action}` });
|
|
71
|
+
}
|
|
72
|
+
} catch (error) {
|
|
73
|
+
res.status(400).json({ __type: 'InternalFailure', message: error.message });
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
// Health check
|
|
78
|
+
this.app.get('/health', (req, res) => {
|
|
79
|
+
res.json({
|
|
80
|
+
status: 'healthy',
|
|
81
|
+
service: 'kinesis-analytics',
|
|
82
|
+
port: this.port,
|
|
83
|
+
stats: this.simulator.getStats()
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async createApplication(req, res) {
|
|
89
|
+
const { ApplicationName, RuntimeEnvironment = 'SQL_1_0', ApplicationDescription = '' } = req.body;
|
|
90
|
+
|
|
91
|
+
if (!ApplicationName) {
|
|
92
|
+
throw new Error('ApplicationName is required');
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const result = this.simulator.createApplication(ApplicationName, {
|
|
96
|
+
runtimeEnvironment: RuntimeEnvironment,
|
|
97
|
+
applicationDescription: ApplicationDescription
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
res.status(201).json(result);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async describeApplication(req, res) {
|
|
104
|
+
const { ApplicationName } = req.body;
|
|
105
|
+
|
|
106
|
+
if (!ApplicationName) {
|
|
107
|
+
throw new Error('ApplicationName is required');
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const result = this.simulator.describeApplication(ApplicationName);
|
|
111
|
+
res.json(result);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
async listApplications(req, res) {
|
|
115
|
+
const { Limit } = req.body;
|
|
116
|
+
|
|
117
|
+
const result = this.simulator.listApplications({ Limit });
|
|
118
|
+
res.json(result);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
async deleteApplication(req, res) {
|
|
122
|
+
const { ApplicationName, CurrentApplicationVersionId } = req.body;
|
|
123
|
+
|
|
124
|
+
if (!ApplicationName) {
|
|
125
|
+
throw new Error('ApplicationName is required');
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const result = this.simulator.deleteApplication(ApplicationName, CurrentApplicationVersionId);
|
|
129
|
+
res.json(result);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
async addApplicationInput(req, res) {
|
|
133
|
+
const { ApplicationName, CurrentApplicationVersionId, Input } = req.body;
|
|
134
|
+
|
|
135
|
+
if (!ApplicationName || !Input) {
|
|
136
|
+
throw new Error('ApplicationName and Input are required');
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const result = this.simulator.addApplicationInput(ApplicationName, {
|
|
140
|
+
Input,
|
|
141
|
+
CurrentApplicationVersionId
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
res.json(result);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
async deleteApplicationInput(req, res) {
|
|
148
|
+
const { ApplicationName, CurrentApplicationVersionId, InputId } = req.body;
|
|
149
|
+
|
|
150
|
+
if (!ApplicationName || !InputId) {
|
|
151
|
+
throw new Error('ApplicationName and InputId are required');
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const result = this.simulator.deleteApplicationInput(ApplicationName, InputId);
|
|
155
|
+
res.json(result);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
async addApplicationOutput(req, res) {
|
|
159
|
+
const { ApplicationName, CurrentApplicationVersionId, Output } = req.body;
|
|
160
|
+
|
|
161
|
+
if (!ApplicationName || !Output) {
|
|
162
|
+
throw new Error('ApplicationName and Output are required');
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const result = this.simulator.addApplicationOutput(ApplicationName, {
|
|
166
|
+
Output,
|
|
167
|
+
CurrentApplicationVersionId
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
res.json(result);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
async deleteApplicationOutput(req, res) {
|
|
174
|
+
const { ApplicationName, CurrentApplicationVersionId, OutputId } = req.body;
|
|
175
|
+
|
|
176
|
+
if (!ApplicationName || !OutputId) {
|
|
177
|
+
throw new Error('ApplicationName and OutputId are required');
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const result = this.simulator.deleteApplicationOutput(ApplicationName, OutputId);
|
|
181
|
+
res.json(result);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
async updateApplicationCode(req, res) {
|
|
185
|
+
const { ApplicationName, CurrentApplicationVersionId, ApplicationCode } = req.body;
|
|
186
|
+
|
|
187
|
+
if (!ApplicationName || !ApplicationCode) {
|
|
188
|
+
throw new Error('ApplicationName and ApplicationCode are required');
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const result = this.simulator.updateApplicationCode(ApplicationName, ApplicationCode);
|
|
192
|
+
res.json(result);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
async startApplication(req, res) {
|
|
196
|
+
const { ApplicationName, RunConfiguration } = req.body;
|
|
197
|
+
|
|
198
|
+
if (!ApplicationName) {
|
|
199
|
+
throw new Error('ApplicationName is required');
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const result = this.simulator.startApplication(ApplicationName, RunConfiguration);
|
|
203
|
+
res.json(result);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
async stopApplication(req, res) {
|
|
207
|
+
const { ApplicationName } = req.body;
|
|
208
|
+
|
|
209
|
+
if (!ApplicationName) {
|
|
210
|
+
throw new Error('ApplicationName is required');
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const result = this.simulator.stopApplication(ApplicationName);
|
|
214
|
+
res.json(result);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
async start() {
|
|
218
|
+
return new Promise((resolve, reject) => {
|
|
219
|
+
try {
|
|
220
|
+
this.server = this.app.listen(this.port, () => {
|
|
221
|
+
logger.info(`✅ Kinesis Data Analytics Server iniciado na porta ${this.port}`);
|
|
222
|
+
resolve();
|
|
223
|
+
});
|
|
224
|
+
} catch (error) {
|
|
225
|
+
logger.error(`❌ Erro ao iniciar Kinesis Data Analytics Server: ${error.message}`);
|
|
226
|
+
reject(error);
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
async stop() {
|
|
232
|
+
return new Promise((resolve, reject) => {
|
|
233
|
+
if (this.server) {
|
|
234
|
+
this.server.close((err) => {
|
|
235
|
+
if (err) {
|
|
236
|
+
logger.error(`Erro ao parar Kinesis Data Analytics Server: ${err.message}`);
|
|
237
|
+
reject(err);
|
|
238
|
+
} else {
|
|
239
|
+
logger.info('Kinesis Data Analytics Server parado');
|
|
240
|
+
resolve();
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
} else {
|
|
244
|
+
resolve();
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
module.exports = KinesisAnalyticsServer;
|
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kinesis Data Analytics Simulator
|
|
3
|
+
* Simulador de Kinesis Data Analytics com suporte a aplicações e queries
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const { Application } = require('./models/application');
|
|
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
|
+
|
|
12
|
+
class KinesisAnalyticsSimulator {
|
|
13
|
+
constructor(config) {
|
|
14
|
+
this.config = config;
|
|
15
|
+
const dataDir = process.env.AWS_LOCAL_SIMULATOR_DATA_DIR || config.dataDir || './.aws-local-simulator-data';
|
|
16
|
+
this.dataDir = path.join(dataDir, 'kinesis-analytics');
|
|
17
|
+
|
|
18
|
+
this.store = new LocalStore(this.dataDir);
|
|
19
|
+
this.applications = new Map();
|
|
20
|
+
this.audit = new CloudTrailAudit('kinesisanalytics.amazonaws.com');
|
|
21
|
+
|
|
22
|
+
// Mutexes por aplicação
|
|
23
|
+
this._appLocks = new Map();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Executa operação com lock exclusivo por aplicação
|
|
28
|
+
*/
|
|
29
|
+
_withAppLock(appName, fn) {
|
|
30
|
+
const prev = this._appLocks.get(appName) || Promise.resolve();
|
|
31
|
+
const next = prev.then(() => fn());
|
|
32
|
+
this._appLocks.set(appName, next.catch(() => {}));
|
|
33
|
+
return next;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async initialize() {
|
|
37
|
+
logger.debug('Inicializando Kinesis Data Analytics Simulator...');
|
|
38
|
+
this.loadApplications();
|
|
39
|
+
|
|
40
|
+
// Cria aplicações da configuração
|
|
41
|
+
if (this.config['kinesis-analytics']?.applications) {
|
|
42
|
+
for (const appConfig of this.config['kinesis-analytics'].applications) {
|
|
43
|
+
if (!this.applications.has(appConfig.name)) {
|
|
44
|
+
this.createApplication(appConfig.name, appConfig);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
logger.debug(`✅ Kinesis Data Analytics Simulator inicializado com ${this.applications.size} aplicações`);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Carrega aplicações persistidas
|
|
54
|
+
*/
|
|
55
|
+
loadApplications() {
|
|
56
|
+
const metadata = this.store.read('__applications__');
|
|
57
|
+
if (!metadata) return;
|
|
58
|
+
|
|
59
|
+
for (const [appName, appData] of Object.entries(metadata)) {
|
|
60
|
+
const app = new Application(appName, appData);
|
|
61
|
+
this.applications.set(appName, app);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
logger.debug(`Carregadas ${this.applications.size} aplicações`);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Persiste metadados de aplicações
|
|
69
|
+
*/
|
|
70
|
+
persistApplications() {
|
|
71
|
+
const metadata = {};
|
|
72
|
+
|
|
73
|
+
for (const [appName, app] of this.applications) {
|
|
74
|
+
metadata[appName] = app.toJSON();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
this.store.write('__applications__', metadata);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Cria uma nova aplicação
|
|
82
|
+
*/
|
|
83
|
+
createApplication(applicationName, config = {}) {
|
|
84
|
+
if (this.applications.has(applicationName)) {
|
|
85
|
+
throw new Error(`Application '${applicationName}' already exists`);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const app = new Application(applicationName, config);
|
|
89
|
+
this.applications.set(applicationName, app);
|
|
90
|
+
this.persistApplications();
|
|
91
|
+
|
|
92
|
+
this.audit.record({
|
|
93
|
+
eventName: 'CreateApplication',
|
|
94
|
+
readOnly: false,
|
|
95
|
+
resources: [{
|
|
96
|
+
ARN: app.applicationArn,
|
|
97
|
+
type: 'AWS::KinesisAnalytics::Application'
|
|
98
|
+
}],
|
|
99
|
+
requestParameters: { applicationName }
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
logger.info(`✅ Aplicação criada: ${applicationName}`);
|
|
103
|
+
|
|
104
|
+
return {
|
|
105
|
+
ApplicationSummary: {
|
|
106
|
+
ApplicationName: applicationName,
|
|
107
|
+
ApplicationArn: app.applicationArn,
|
|
108
|
+
ApplicationStatus: app.applicationStatus,
|
|
109
|
+
ApplicationVersionId: app.applicationVersionId,
|
|
110
|
+
CreationTimestamp: app.creationTimestamp
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Descreve uma aplicação
|
|
117
|
+
*/
|
|
118
|
+
describeApplication(applicationName) {
|
|
119
|
+
const app = this.applications.get(applicationName);
|
|
120
|
+
if (!app) {
|
|
121
|
+
throw new Error(`Application '${applicationName}' not found`);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return app.describe();
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Lista aplicações
|
|
129
|
+
*/
|
|
130
|
+
listApplications(params = {}) {
|
|
131
|
+
const { Limit = 50 } = params;
|
|
132
|
+
const appNames = Array.from(this.applications.keys());
|
|
133
|
+
const result = appNames.slice(0, Limit);
|
|
134
|
+
|
|
135
|
+
return {
|
|
136
|
+
ApplicationSummaries: result.map(name => {
|
|
137
|
+
const app = this.applications.get(name);
|
|
138
|
+
return {
|
|
139
|
+
ApplicationName: app.applicationName,
|
|
140
|
+
ApplicationArn: app.applicationArn,
|
|
141
|
+
ApplicationStatus: app.applicationStatus,
|
|
142
|
+
ApplicationVersionId: app.applicationVersionId,
|
|
143
|
+
CreationTimestamp: app.creationTimestamp
|
|
144
|
+
};
|
|
145
|
+
}),
|
|
146
|
+
HasMoreApplications: result.length === Limit
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Deleta uma aplicação
|
|
152
|
+
*/
|
|
153
|
+
deleteApplication(applicationName, currentApplicationVersionId) {
|
|
154
|
+
const app = this.applications.get(applicationName);
|
|
155
|
+
if (!app) {
|
|
156
|
+
throw new Error(`Application '${applicationName}' not found`);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (currentApplicationVersionId && app.applicationVersionId !== currentApplicationVersionId) {
|
|
160
|
+
throw new Error('ConcurrentModificationException: Current version does not match');
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
this.applications.delete(applicationName);
|
|
164
|
+
this.persistApplications();
|
|
165
|
+
|
|
166
|
+
this.audit.record({
|
|
167
|
+
eventName: 'DeleteApplication',
|
|
168
|
+
readOnly: false,
|
|
169
|
+
resources: [{
|
|
170
|
+
ARN: app.applicationArn,
|
|
171
|
+
type: 'AWS::KinesisAnalytics::Application'
|
|
172
|
+
}],
|
|
173
|
+
requestParameters: { applicationName }
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
logger.info(`✅ Aplicação deletada: ${applicationName}`);
|
|
177
|
+
|
|
178
|
+
return {};
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Adiciona input a uma aplicação
|
|
183
|
+
*/
|
|
184
|
+
addApplicationInput(applicationName, config) {
|
|
185
|
+
const app = this.applications.get(applicationName);
|
|
186
|
+
if (!app) {
|
|
187
|
+
throw new Error(`Application '${applicationName}' not found`);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return this._withAppLock(applicationName, () => {
|
|
191
|
+
const input = app.addInput(config.Input.NamePrefix, {
|
|
192
|
+
sourceStreamArn: config.Input.InputParallelism?.InputParallelismCount || 1,
|
|
193
|
+
schema: config.Input.InputSchema
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
app.applicationVersionId++;
|
|
197
|
+
this.persistApplications();
|
|
198
|
+
|
|
199
|
+
this.audit.record({
|
|
200
|
+
eventName: 'AddApplicationInput',
|
|
201
|
+
readOnly: false,
|
|
202
|
+
resources: [{
|
|
203
|
+
ARN: app.applicationArn,
|
|
204
|
+
type: 'AWS::KinesisAnalytics::Application'
|
|
205
|
+
}],
|
|
206
|
+
requestParameters: { applicationName, inputName: input.inputName }
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
return {};
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Remove input de uma aplicação
|
|
215
|
+
*/
|
|
216
|
+
deleteApplicationInput(applicationName, inputId) {
|
|
217
|
+
const app = this.applications.get(applicationName);
|
|
218
|
+
if (!app) {
|
|
219
|
+
throw new Error(`Application '${applicationName}' not found`);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return this._withAppLock(applicationName, () => {
|
|
223
|
+
const input = Array.from(app.inputs.values()).find(i => i.inputId === inputId);
|
|
224
|
+
if (!input) {
|
|
225
|
+
throw new Error(`Input '${inputId}' not found`);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
app.removeInput(input.inputName);
|
|
229
|
+
app.applicationVersionId++;
|
|
230
|
+
this.persistApplications();
|
|
231
|
+
|
|
232
|
+
return {};
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Adiciona output a uma aplicação
|
|
238
|
+
*/
|
|
239
|
+
addApplicationOutput(applicationName, config) {
|
|
240
|
+
const app = this.applications.get(applicationName);
|
|
241
|
+
if (!app) {
|
|
242
|
+
throw new Error(`Application '${applicationName}' not found`);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
return this._withAppLock(applicationName, () => {
|
|
246
|
+
const output = app.addOutput(config.Output.Name, {
|
|
247
|
+
destination: config.Output.Destination,
|
|
248
|
+
destinationSchema: config.Output.DestinationSchema
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
app.applicationVersionId++;
|
|
252
|
+
this.persistApplications();
|
|
253
|
+
|
|
254
|
+
this.audit.record({
|
|
255
|
+
eventName: 'AddApplicationOutput',
|
|
256
|
+
readOnly: false,
|
|
257
|
+
resources: [{
|
|
258
|
+
ARN: app.applicationArn,
|
|
259
|
+
type: 'AWS::KinesisAnalytics::Application'
|
|
260
|
+
}],
|
|
261
|
+
requestParameters: { applicationName, outputName: output.outputName }
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
return {};
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Remove output de uma aplicação
|
|
270
|
+
*/
|
|
271
|
+
deleteApplicationOutput(applicationName, outputId) {
|
|
272
|
+
const app = this.applications.get(applicationName);
|
|
273
|
+
if (!app) {
|
|
274
|
+
throw new Error(`Application '${applicationName}' not found`);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
return this._withAppLock(applicationName, () => {
|
|
278
|
+
const output = Array.from(app.outputs.values()).find(o => o.outputId === outputId);
|
|
279
|
+
if (!output) {
|
|
280
|
+
throw new Error(`Output '${outputId}' not found`);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
app.removeOutput(output.outputName);
|
|
284
|
+
app.applicationVersionId++;
|
|
285
|
+
this.persistApplications();
|
|
286
|
+
|
|
287
|
+
return {};
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Atualiza código SQL da aplicação
|
|
293
|
+
*/
|
|
294
|
+
updateApplicationCode(applicationName, sqlCode) {
|
|
295
|
+
const app = this.applications.get(applicationName);
|
|
296
|
+
if (!app) {
|
|
297
|
+
throw new Error(`Application '${applicationName}' not found`);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
return this._withAppLock(applicationName, () => {
|
|
301
|
+
app.setSqlCode(sqlCode);
|
|
302
|
+
app.applicationVersionId++;
|
|
303
|
+
this.persistApplications();
|
|
304
|
+
|
|
305
|
+
this.audit.record({
|
|
306
|
+
eventName: 'UpdateApplicationCode',
|
|
307
|
+
readOnly: false,
|
|
308
|
+
resources: [{
|
|
309
|
+
ARN: app.applicationArn,
|
|
310
|
+
type: 'AWS::KinesisAnalytics::Application'
|
|
311
|
+
}],
|
|
312
|
+
requestParameters: { applicationName }
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
logger.info(`✅ Código da aplicação ${applicationName} atualizado`);
|
|
316
|
+
|
|
317
|
+
return {};
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Inicia uma aplicação
|
|
323
|
+
*/
|
|
324
|
+
startApplication(applicationName, runConfiguration) {
|
|
325
|
+
const app = this.applications.get(applicationName);
|
|
326
|
+
if (!app) {
|
|
327
|
+
throw new Error(`Application '${applicationName}' not found`);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
app.applicationStatus = 'RUNNING';
|
|
331
|
+
this.persistApplications();
|
|
332
|
+
|
|
333
|
+
this.audit.record({
|
|
334
|
+
eventName: 'StartApplication',
|
|
335
|
+
readOnly: false,
|
|
336
|
+
resources: [{
|
|
337
|
+
ARN: app.applicationArn,
|
|
338
|
+
type: 'AWS::KinesisAnalytics::Application'
|
|
339
|
+
}],
|
|
340
|
+
requestParameters: { applicationName }
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
logger.info(`✅ Aplicação iniciada: ${applicationName}`);
|
|
344
|
+
|
|
345
|
+
return {};
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Para uma aplicação
|
|
350
|
+
*/
|
|
351
|
+
stopApplication(applicationName) {
|
|
352
|
+
const app = this.applications.get(applicationName);
|
|
353
|
+
if (!app) {
|
|
354
|
+
throw new Error(`Application '${applicationName}' not found`);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
app.applicationStatus = 'READY';
|
|
358
|
+
this.persistApplications();
|
|
359
|
+
|
|
360
|
+
this.audit.record({
|
|
361
|
+
eventName: 'StopApplication',
|
|
362
|
+
readOnly: false,
|
|
363
|
+
resources: [{
|
|
364
|
+
ARN: app.applicationArn,
|
|
365
|
+
type: 'AWS::KinesisAnalytics::Application'
|
|
366
|
+
}],
|
|
367
|
+
requestParameters: { applicationName }
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
logger.info(`✅ Aplicação parada: ${applicationName}`);
|
|
371
|
+
|
|
372
|
+
return {};
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/**
|
|
376
|
+
* Obtém estatísticas do simulator
|
|
377
|
+
*/
|
|
378
|
+
getStats() {
|
|
379
|
+
const stats = {
|
|
380
|
+
applicationsCount: this.applications.size,
|
|
381
|
+
totalInputs: 0,
|
|
382
|
+
totalOutputs: 0,
|
|
383
|
+
applications: []
|
|
384
|
+
};
|
|
385
|
+
|
|
386
|
+
for (const [appName, app] of this.applications) {
|
|
387
|
+
const appStats = app.toJSON();
|
|
388
|
+
stats.totalInputs += appStats.inputCount;
|
|
389
|
+
stats.totalOutputs += appStats.outputCount;
|
|
390
|
+
stats.applications.push(appStats);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
return stats;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* Reset - limpa todos os dados
|
|
398
|
+
*/
|
|
399
|
+
async reset() {
|
|
400
|
+
this.applications.clear();
|
|
401
|
+
this.store.write('__applications__', {});
|
|
402
|
+
|
|
403
|
+
logger.info('✅ Kinesis Data Analytics Simulator resetado');
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
module.exports = KinesisAnalyticsSimulator;
|