@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,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;