@gugananuvem/aws-local-simulator 1.0.22 → 1.0.26

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/src/server.js CHANGED
@@ -5,6 +5,8 @@
5
5
  const path = require("path");
6
6
  const fs = require("fs");
7
7
  const mkdirp = require("mkdirp");
8
+ const express = require("express");
9
+ const cors = require("cors");
8
10
  const logger = require("./utils/logger");
9
11
 
10
12
  // Importa serviços
@@ -34,6 +36,10 @@ class Server {
34
36
  this.services = [];
35
37
  this.servicesMap = new Map();
36
38
  this.running = false;
39
+ this.managementApp = express();
40
+ this.managementApp.use(cors());
41
+ this.managementApp.use(express.json());
42
+ this.managementServer = null;
37
43
  this.setupDataDir();
38
44
  this.setupLogLevel();
39
45
  }
@@ -63,6 +69,8 @@ class Server {
63
69
  try {
64
70
  await this.initializeServices();
65
71
  await this.startServices();
72
+ this.setupManagementRoutes();
73
+ await this.startManagementServer();
66
74
 
67
75
  this.running = true;
68
76
  this.printStatus();
@@ -72,28 +80,150 @@ class Server {
72
80
  }
73
81
  }
74
82
 
75
- async initializeServices() {
76
- const serviceOrder = [
77
- { name: "sts", class: STSService, depends: [] },
78
- { name: "lambda", class: LambdaService, depends: [] },
79
- { name: "dynamodb", class: DynamoDBService, depends: [] },
80
- { name: "s3", class: S3Service, depends: [] },
81
- { name: "sqs", class: SQSService, depends: ["lambda"] },
82
- { name: "sns", class: SNSService, depends: [] },
83
- { name: "eventbridge", class: EventBridgeService, depends: [] },
84
- { name: "cognito", class: CognitoService, depends: ["lambda"] },
85
- { name: "ecs", class: ECSService, depends: [] },
86
- { name: "apigateway", class: APIGatewayService, depends: ["lambda", "cognito"] },
87
- { name: "kms", class: KMSService, depends: [] },
88
- { name: "cloudwatch", class: CloudWatchService, depends: [] },
89
- { name: "cloudtrail", class: CloudTrailService, depends: [] },
90
- { name: "cloudformation", class: CloudFormationService, depends: [] },
91
- { name: "xray", class: XRayService, depends: [] },
92
- { name: "secret-manager", class: SecretManagerService, depends: [] },
93
- { name: "parameter-store",class: ParameterStoreService, depends: [] },
94
- { name: "config", class: ConfigService, depends: [] },
95
- { name: "athena", class: AthenaService, depends: [] },
83
+ setupManagementRoutes() {
84
+ const app = this.managementApp;
85
+
86
+ // GET /__admin/services returns all services status
87
+ app.get("/__admin/services", (req, res) => {
88
+ const registry = this.buildServiceRegistry();
89
+ const services = registry.map((def) => {
90
+ const svc = this.servicesMap.get(def.name);
91
+ if (svc) {
92
+ const status = typeof svc.getStatus === "function"
93
+ ? svc.getStatus()
94
+ : { name: def.name, running: true, port: svc.port };
95
+ // Compute canDisable: no running service should depend on this one
96
+ const canDisable = !registry.some(
97
+ (other) =>
98
+ other.depends.includes(def.name) &&
99
+ this.servicesMap.has(other.name)
100
+ );
101
+ return {
102
+ name: def.name,
103
+ running: true,
104
+ enabled: true,
105
+ port: status.port || this.config.ports?.[def.name],
106
+ endpoint: status.endpoint || `http://localhost:${status.port || this.config.ports?.[def.name]}`,
107
+ dependencies: def.depends,
108
+ category: def.category,
109
+ canDisable,
110
+ ...status,
111
+ };
112
+ }
113
+ return {
114
+ name: def.name,
115
+ running: false,
116
+ enabled: false,
117
+ port: this.config.ports?.[def.name],
118
+ endpoint: `http://localhost:${this.config.ports?.[def.name]}`,
119
+ dependencies: def.depends,
120
+ canDisable: true,
121
+ };
122
+ });
123
+ res.json({ services });
124
+ });
125
+
126
+ // GET /__admin/services/:name — returns single service status
127
+ app.get("/__admin/services/:name", (req, res) => {
128
+ const { name } = req.params;
129
+ const registry = this.buildServiceRegistry();
130
+ const def = registry.find((d) => d.name === name);
131
+ if (!def) {
132
+ return res.status(404).json({ error: `Unknown service: ${name}` });
133
+ }
134
+ const svc = this.servicesMap.get(name);
135
+ const canDisable = !registry.some(
136
+ (other) =>
137
+ other.depends.includes(name) &&
138
+ this.servicesMap.has(other.name)
139
+ );
140
+ if (svc) {
141
+ const status = typeof svc.getStatus === "function"
142
+ ? svc.getStatus()
143
+ : { name, running: true, port: svc.port };
144
+ return res.json({
145
+ name,
146
+ running: true,
147
+ enabled: true,
148
+ port: status.port || this.config.ports?.[name],
149
+ endpoint: status.endpoint || `http://localhost:${status.port || this.config.ports?.[name]}`,
150
+ dependencies: def.depends,
151
+ canDisable,
152
+ ...status,
153
+ });
154
+ }
155
+ return res.json({
156
+ name,
157
+ running: false,
158
+ enabled: false,
159
+ port: this.config.ports?.[name],
160
+ endpoint: `http://localhost:${this.config.ports?.[name]}`,
161
+ dependencies: def.depends,
162
+ canDisable: true,
163
+ });
164
+ });
165
+
166
+ // POST /__admin/services/:name/enable — enables a service
167
+ app.post("/__admin/services/:name/enable", async (req, res) => {
168
+ const result = await this.enableService(req.params.name);
169
+ res.status(result.success ? 200 : 400).json(result);
170
+ });
171
+
172
+ // POST /__admin/services/:name/disable — disables a service
173
+ app.post("/__admin/services/:name/disable", async (req, res) => {
174
+ const result = await this.disableService(req.params.name);
175
+ res.status(result.success ? 200 : 400).json(result);
176
+ });
177
+ }
178
+
179
+ startManagementServer() {
180
+ return new Promise((resolve, reject) => {
181
+ const port = this.config.adminPort || 9999;
182
+ this.managementServer = this.managementApp.listen(port, () => {
183
+ logger.info(`🔧 Management API rodando em http://localhost:${port}`);
184
+ resolve();
185
+ });
186
+ this.managementServer.on("error", reject);
187
+ });
188
+ }
189
+
190
+ buildServiceRegistry() {
191
+ return [
192
+ //Armazenamento & BD
193
+ { name: "dynamodb", class: DynamoDBService, depends: [], category: 'Armazenamento & Banco de Dados' },
194
+ { name: "s3", class: S3Service, depends: [], category: 'Armazenamento & Banco de Dados' },
195
+ { name: "athena", class: AthenaService, depends: [], category: 'Armazenamento & Banco de Dados' },
196
+
197
+ //Computação
198
+ { name: "lambda", class: LambdaService, depends: [], category: 'Computação' },
199
+ /* { name: "ecs", class: ECSService, depends: [] , category:'Computação'},*/
200
+
201
+ { name: "cognito", class: CognitoService, depends: ["lambda"], category: 'Segurança & Identidade' },
202
+ { name: "sts", class: STSService, depends: [], category: 'Segurança & Identidade' },
203
+ { name: "kms", class: KMSService, depends: [], category: 'Segurança & Identidade' },
204
+ { name: "secret-manager", class: SecretManagerService, depends: [], category: 'Segurança & Identidade' },
205
+ { name: "parameter-store", class: ParameterStoreService, depends: [], category: 'Segurança & Identidade' },
206
+
207
+ //Mensageria
208
+ { name: "sqs", class: SQSService, depends: ["lambda"], category: 'Mensageria & Integração' },
209
+ { name: "sns", class: SNSService, depends: [], category: 'Mensageria & Integração' },
210
+ { name: "eventbridge", class: EventBridgeService, depends: [], category: 'Mensageria & Integração' },
211
+
212
+ //Networking
213
+ { name: "apigateway", class: APIGatewayService, depends: ["lambda", "cognito"], category: 'Networking' },
214
+
215
+ //Observabilidade & Conformidade
216
+ { name: "cloudwatch", class: CloudWatchService, depends: [], category: 'Observabilidade & Conformidade' },
217
+ { name: "cloudtrail", class: CloudTrailService, depends: [], category: 'Observabilidade & Conformidade' },
218
+ { name: "xray", class: XRayService, depends: [], category: 'Observabilidade & Conformidade' },
219
+ { name: "config", class: ConfigService, depends: [], category: 'Observabilidade & Conformidade' },
220
+ { name: "cloudformation", class: CloudFormationService, depends: [], category: 'Observabilidade & Conformidade' },
221
+
96
222
  ];
223
+ }
224
+
225
+ async initializeServices() {
226
+ const serviceOrder = this.buildServiceRegistry();
97
227
 
98
228
  for (const serviceDef of serviceOrder) {
99
229
  if (this.config.services[serviceDef.name]) {
@@ -143,6 +273,11 @@ class Server {
143
273
  const stopPromises = [...this.services].reverse().map((service) => service.stop());
144
274
  await Promise.all(stopPromises);
145
275
 
276
+ if (this.managementServer) {
277
+ await new Promise((resolve) => this.managementServer.close(resolve));
278
+ this.managementServer = null;
279
+ }
280
+
146
281
  this.running = false;
147
282
  logger.success("✅ Todos os serviços foram parados");
148
283
  } catch (error) {
@@ -177,6 +312,107 @@ class Server {
177
312
  return this.servicesMap.get(name);
178
313
  }
179
314
 
315
+ async enableService(name) {
316
+ // Check if already running
317
+ if (this.servicesMap.has(name)) {
318
+ return { success: false, error: "Service already running" };
319
+ }
320
+
321
+ // Look up in registry
322
+ const registry = this.buildServiceRegistry();
323
+ const serviceDef = registry.find((s) => s.name === name);
324
+ if (!serviceDef) {
325
+ return { success: false, error: "Unknown service" };
326
+ }
327
+
328
+ // Validate all dependencies are running
329
+ for (const dep of serviceDef.depends) {
330
+ if (!this.servicesMap.has(dep)) {
331
+ return { success: false, error: `Dependency not running: ${dep}` };
332
+ }
333
+ }
334
+
335
+ try {
336
+ const service = new serviceDef.class(this.config);
337
+ await service.initialize();
338
+ await service.start();
339
+
340
+ this.services.push(service);
341
+ this.servicesMap.set(name, service);
342
+
343
+ if (typeof service.injectDependencies === "function") {
344
+ service.injectDependencies(this);
345
+ }
346
+
347
+ // Build ServiceStatus for the response
348
+ const canDisable = !registry.some(
349
+ (other) =>
350
+ other.depends.includes(name) && this.servicesMap.has(other.name)
351
+ );
352
+ const rawStatus =
353
+ typeof service.getStatus === "function"
354
+ ? service.getStatus()
355
+ : { name, running: true, port: service.port };
356
+
357
+ const serviceStatus = {
358
+ name,
359
+ running: true,
360
+ enabled: true,
361
+ port: rawStatus.port || this.config.ports?.[name],
362
+ endpoint:
363
+ rawStatus.endpoint ||
364
+ `http://localhost:${rawStatus.port || this.config.ports?.[name]}`,
365
+ dependencies: serviceDef.depends,
366
+ canDisable,
367
+ ...rawStatus,
368
+ };
369
+
370
+ return { success: true, service: serviceStatus };
371
+ } catch (error) {
372
+ return { success: false, error: error.message };
373
+ }
374
+ }
375
+
376
+ async disableService(name) {
377
+ // Check if running
378
+ const service = this.servicesMap.get(name);
379
+ if (!service) {
380
+ return { success: false, error: "Service not running" };
381
+ }
382
+
383
+ // Check reverse dependencies
384
+ const registry = this.buildServiceRegistry();
385
+ for (const other of registry) {
386
+ if (other.depends.includes(name) && this.servicesMap.has(other.name)) {
387
+ return {
388
+ success: false,
389
+ error: `Cannot disable: ${other.name} depends on it`,
390
+ };
391
+ }
392
+ }
393
+
394
+ try {
395
+ await service.stop();
396
+ this.services = this.services.filter((s) => s !== service);
397
+ this.servicesMap.delete(name);
398
+
399
+ return {
400
+ success: true,
401
+ service: {
402
+ name,
403
+ running: false,
404
+ enabled: false,
405
+ port: this.config.ports?.[name],
406
+ endpoint: `http://localhost:${this.config.ports?.[name]}`,
407
+ dependencies: registry.find((d) => d.name === name)?.depends || [],
408
+ canDisable: true,
409
+ },
410
+ };
411
+ } catch (error) {
412
+ return { success: false, error: error.message };
413
+ }
414
+ }
415
+
180
416
  printStatus() {
181
417
  logger.info("\n" + "=".repeat(60));
182
418
  logger.info("📊 Status dos Serviços:");
@@ -209,6 +209,18 @@ class APIGatewayServer {
209
209
  }
210
210
  });
211
211
 
212
+ this.app.patch('/restapis/:apiId', (req, res) => {
213
+ try {
214
+ const result = this.simulator.updateRestApi({
215
+ ...req.body,
216
+ restApiId: req.params.apiId
217
+ });
218
+ res.json(result);
219
+ } catch (error) {
220
+ res.status(400).json({ error: error.message });
221
+ }
222
+ });
223
+
212
224
  this.app.delete('/restapis/:apiId', (req, res) => {
213
225
  try {
214
226
  this.simulator.deleteRestApi({ restApiId: req.params.apiId });
@@ -509,6 +521,31 @@ class APIGatewayServer {
509
521
  }
510
522
  });
511
523
 
524
+ this.app.post('/__admin/apis/:apiId/endpoints', (req, res) => {
525
+ try {
526
+ const result = this.simulator.putEndpoint({
527
+ ...req.body,
528
+ restApiId: req.params.apiId
529
+ });
530
+ res.json(result);
531
+ } catch (error) {
532
+ res.status(400).json({ error: error.message });
533
+ }
534
+ });
535
+
536
+ this.app.delete('/__admin/apis/:apiId/endpoints', (req, res) => {
537
+ try {
538
+ const result = this.simulator.deleteEndpoint({
539
+ restApiId: req.params.apiId,
540
+ path: req.query.path,
541
+ method: req.query.method
542
+ });
543
+ res.json(result);
544
+ } catch (error) {
545
+ res.status(400).json({ error: error.message });
546
+ }
547
+ });
548
+
512
549
  this.app.get('/__admin/apikeys', (req, res) => {
513
550
  const keys = Array.from(this.simulator.apiKeys.values()).map(k => ({
514
551
  id: k.id,
@@ -33,6 +33,7 @@ class APIGatewaySimulator {
33
33
  async initialize() {
34
34
  logger.debug('Inicializando API Gateway Simulator...');
35
35
  this.loadAPIs();
36
+ this._loadStaticAPIs();
36
37
  this.loadWebSocketAPIs();
37
38
  this.loadDeployments();
38
39
  this.loadStages();
@@ -44,9 +45,62 @@ class APIGatewaySimulator {
44
45
  this.loadApiKeys();
45
46
  this.loadDomainNames();
46
47
 
47
- logger.debug(`✅ API Gateway Simulator inicializado com ${this.apis.size} APIs, ${this.resources.size} resources, ${this.methods.size} methods`);
48
+ logger.debug(`✅ API Gateway Simulator inicializado com ${this.apis.size} APIs (${Array.from(this.apis.values()).filter(a => a.isStatic).length} estáticas)`);
48
49
  }
49
50
 
51
+ _loadStaticAPIs() {
52
+ const staticApis = this.config.apigateway?.apis || [];
53
+ staticApis.forEach((apiConfig, index) => {
54
+ const apiId = `static_${index}`;
55
+
56
+ const api = {
57
+ id: apiId,
58
+ name: apiConfig.name,
59
+ description: apiConfig.description || 'Configured in aws-local-simulator.json',
60
+ version: 'config',
61
+ createdDate: new Date().toISOString(),
62
+ isStatic: true,
63
+ apiKeySource: 'HEADER',
64
+ endpointConfiguration: { types: ['REGIONAL'] },
65
+ resources: new Map(),
66
+ stages: new Map(),
67
+ deployments: new Map(),
68
+ models: new Map(),
69
+ authorizers: new Map()
70
+ };
71
+
72
+ // Adiciona recursos a partir dos endpoints configurados
73
+ (apiConfig.endpoints || []).forEach((ep, epIndex) => {
74
+ const resId = `res_${apiId}_${epIndex}`;
75
+ api.resources.set(resId, {
76
+ id: resId,
77
+ path: ep.path,
78
+ pathPart: ep.path.split('/').pop() || '/',
79
+ resourceMethods: new Map([[ep.method, {
80
+ httpMethod: ep.method,
81
+ authorizationType: ep.authorizerRequired ? 'COGNITO_USER_POOLS' : 'NONE',
82
+ apiKeyRequired: false,
83
+ integration: {
84
+ type: ep.integrationType === 'lambda' ? 'AWS_PROXY' : 'HTTP',
85
+ uri: ep.lambdaName,
86
+ integrationHttpMethod: 'POST'
87
+ }
88
+ }]])
89
+ });
90
+ });
91
+
92
+ // Adiciona um stage padrão
93
+ api.stages.set('local', {
94
+ stageName: 'local',
95
+ createdDate: new Date().toISOString(),
96
+ deploymentId: 'static-deploy'
97
+ });
98
+
99
+ this.apis.set(apiId, api);
100
+ });
101
+ }
102
+
103
+
50
104
  // ============ REST API Operations ============
51
105
 
52
106
  createRestApi(params) {
@@ -104,11 +158,15 @@ class APIGatewaySimulator {
104
158
  description: api.description,
105
159
  version: api.version,
106
160
  createdDate: api.createdDate,
107
- apiKeySource: api.apiKeySource
161
+ apiKeySource: api.apiKeySource,
162
+ isStatic: api.isStatic || false,
163
+ resourceCount: api.resources.size,
164
+ stageCount: api.stages.size
108
165
  }))
109
166
  };
110
167
  }
111
168
 
169
+
112
170
  getRestApi(params) {
113
171
  const { restApiId } = params;
114
172
  const api = this.apis.get(restApiId);
@@ -129,6 +187,27 @@ class APIGatewaySimulator {
129
187
  };
130
188
  }
131
189
 
190
+ updateRestApi(params) {
191
+ const { restApiId, name, description } = params;
192
+ const api = this.apis.get(restApiId);
193
+
194
+ if (!api) {
195
+ throw new Error(`API ${restApiId} not found`);
196
+ }
197
+
198
+ if (name !== undefined) api.name = name;
199
+ if (description !== undefined) api.description = description;
200
+
201
+ if (api.isStatic) {
202
+ // Once edited, the API is no longer static and will be persisted
203
+ api.isStatic = false;
204
+ }
205
+
206
+ this.persistAPIs();
207
+
208
+ return this.getRestApi({ restApiId });
209
+ }
210
+
132
211
  deleteRestApi(params) {
133
212
  const { restApiId } = params;
134
213
 
@@ -142,8 +221,67 @@ class APIGatewaySimulator {
142
221
  return {};
143
222
  }
144
223
 
224
+ // ============ Simplified Dashboard Endpoint Operations ============
225
+
226
+ putEndpoint(params) {
227
+ const { restApiId, path, method, integrationType, lambdaName, authorizerRequired } = params;
228
+ const api = this.apis.get(restApiId);
229
+ if (!api) throw new Error(`API ${restApiId} not found`);
230
+
231
+ if (api.isStatic) api.isStatic = false;
232
+
233
+ // Ensure resource exists
234
+ let resource = Array.from(api.resources.values()).find(r => r.path === path);
235
+ if (!resource) {
236
+ const resourceId = `res_${Date.now()}`;
237
+ resource = {
238
+ id: resourceId,
239
+ path: path,
240
+ pathPart: path.split('/').pop() || '/',
241
+ parentId: null, // Simplified
242
+ resourceMethods: new Map()
243
+ };
244
+ api.resources.set(resourceId, resource);
245
+ }
246
+
247
+ // Put method
248
+ resource.resourceMethods.set(method.toUpperCase(), {
249
+ httpMethod: method.toUpperCase(),
250
+ authorizationType: authorizerRequired ? 'COGNITO_USER_POOLS' : 'NONE',
251
+ apiKeyRequired: false,
252
+ integration: {
253
+ type: integrationType === 'lambda' ? 'AWS_PROXY' : 'HTTP',
254
+ uri: lambdaName,
255
+ integrationHttpMethod: 'POST'
256
+ }
257
+ });
258
+
259
+ this.persistAPIs();
260
+ return { resourceId: resource.id, path, method };
261
+ }
262
+
263
+ deleteEndpoint(params) {
264
+ const { restApiId, path, method } = params;
265
+ const api = this.apis.get(restApiId);
266
+ if (!api) throw new Error(`API ${restApiId} not found`);
267
+
268
+ if (api.isStatic) api.isStatic = false;
269
+
270
+ const resource = Array.from(api.resources.values()).find(r => r.path === path);
271
+ if (resource) {
272
+ resource.resourceMethods.delete(method.toUpperCase());
273
+ // If no methods left and not root, we could delete the resource, but keeping it is fine.
274
+ if (resource.resourceMethods.size === 0 && resource.path !== '/') {
275
+ api.resources.delete(resource.id);
276
+ }
277
+ this.persistAPIs();
278
+ }
279
+ return {};
280
+ }
281
+
145
282
  // ============ Resource Operations ============
146
283
 
284
+
147
285
  createResource(params) {
148
286
  const { restApiId, parentId, pathPart } = params;
149
287
  const api = this.apis.get(restApiId);
@@ -1054,7 +1192,10 @@ class APIGatewaySimulator {
1054
1192
  if (saved) {
1055
1193
  for (const [id, data] of Object.entries(saved)) {
1056
1194
  // Reconstitui Maps
1057
- data.resources = new Map(Object.entries(data.resources || {}));
1195
+ data.resources = new Map(Object.entries(data.resources || {}).map(([rid, r]) => {
1196
+ r.resourceMethods = new Map(Object.entries(r.resourceMethods || {}));
1197
+ return [rid, r];
1198
+ }));
1058
1199
  data.stages = new Map(Object.entries(data.stages || {}));
1059
1200
  data.deployments = new Map(Object.entries(data.deployments || {}));
1060
1201
  data.models = new Map(Object.entries(data.models || {}));
@@ -1064,6 +1205,7 @@ class APIGatewaySimulator {
1064
1205
  }
1065
1206
  }
1066
1207
 
1208
+
1067
1209
  loadWebSocketAPIs() {
1068
1210
  const saved = this.store.read('__websocket_apis__');
1069
1211
  if (saved) {
@@ -1157,9 +1299,10 @@ class APIGatewaySimulator {
1157
1299
  persistAPIs() {
1158
1300
  const apisObj = {};
1159
1301
  for (const [id, api] of this.apis.entries()) {
1302
+ if (api.isStatic) continue;
1160
1303
  apisObj[id] = {
1161
1304
  ...api,
1162
- resources: Object.fromEntries(api.resources),
1305
+ resources: Object.fromEntries(Array.from(api.resources.entries()).map(([rid, r]) => [rid, { ...r, resourceMethods: Object.fromEntries(r.resourceMethods) }])),
1163
1306
  stages: Object.fromEntries(api.stages),
1164
1307
  deployments: Object.fromEntries(api.deployments),
1165
1308
  models: Object.fromEntries(api.models),
@@ -1169,6 +1312,7 @@ class APIGatewaySimulator {
1169
1312
  this.store.write('__apis__', apisObj);
1170
1313
  }
1171
1314
 
1315
+
1172
1316
  persistResources(apiId) {
1173
1317
  const api = this.apis.get(apiId);
1174
1318
  if (api) {
@@ -18,19 +18,10 @@ class CognitoServer {
18
18
 
19
19
  setupMiddlewares() {
20
20
  this.app.use(cors());
21
- this.app.use(express.raw({ type: '*/*', limit: '10mb' }));
22
- this.app.use((req, res, next) => {
23
- if (req.body && Buffer.isBuffer(req.body)) {
24
- try {
25
- req.body = JSON.parse(req.body.toString('utf8'));
26
- } catch (e) {
27
- req.body = {};
28
- }
29
- } else if (!req.body) {
30
- req.body = {};
31
- }
32
- next();
33
- });
21
+ this.app.use(express.json({
22
+ limit: '10mb',
23
+ type: ['application/json', 'application/x-amz-json-1.1']
24
+ }));
34
25
 
35
26
  if (logger.currentLogLevel === 'verboso') {
36
27
  this.app.use((req, res, next) => {
@@ -73,7 +64,8 @@ class CognitoServer {
73
64
 
74
65
  try {
75
66
  const result = await this.handleRequest(target, req.body || {});
76
- res.json(result);
67
+ res.setHeader('Content-Type', 'application/x-amz-json-1.1');
68
+ res.send(JSON.stringify(result));
77
69
  } catch (error) {
78
70
  logger.error('Cognito Error:', error.message);
79
71
  res.status(400).json({
@@ -108,6 +100,8 @@ class CognitoServer {
108
100
  return this.simulator.describeUserPool(params);
109
101
  case 'DeleteUserPool':
110
102
  return this.simulator.deleteUserPool(params);
103
+ case 'UpdateUserPool':
104
+ return this.simulator.updateUserPool(params);
111
105
 
112
106
  case 'ListUsers':
113
107
  return this.simulator.listUsers(params);