@gugananuvem/aws-local-simulator 1.0.2 → 1.0.5
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/bin/aws-local-simulator.js +1 -62
- package/package.json +8 -10
- package/src/index.js +1 -131
- package/src/config/config-loader.js +0 -113
- package/src/config/default-config.js +0 -65
- package/src/config/env-loader.js +0 -67
- package/src/index.mjs +0 -124
- package/src/server.js +0 -219
- package/src/services/apigateway/index.js +0 -67
- package/src/services/apigateway/server.js +0 -435
- package/src/services/apigateway/simulator.js +0 -1252
- package/src/services/cognito/index.js +0 -66
- package/src/services/cognito/server.js +0 -229
- package/src/services/cognito/simulator.js +0 -848
- package/src/services/dynamodb/index.js +0 -71
- package/src/services/dynamodb/server.js +0 -122
- package/src/services/dynamodb/simulator.js +0 -614
- package/src/services/eventbridge/index.js +0 -85
- package/src/services/index.js +0 -19
- package/src/services/lambda/handler-loader.js +0 -173
- package/src/services/lambda/index.js +0 -73
- package/src/services/lambda/route-registry.js +0 -275
- package/src/services/lambda/server.js +0 -153
- package/src/services/lambda/simulator.js +0 -278
- package/src/services/s3/index.js +0 -70
- package/src/services/s3/server.js +0 -239
- package/src/services/s3/simulator.js +0 -740
- package/src/services/sns/index.js +0 -76
- package/src/services/sqs/index.js +0 -96
- package/src/services/sqs/server.js +0 -274
- package/src/services/sqs/simulator.js +0 -660
- package/src/template/aws-config-template.js +0 -88
- package/src/template/aws-config-template.mjs +0 -91
- package/src/template/config-template.json +0 -165
- package/src/utils/aws-config.js +0 -92
- package/src/utils/local-store.js +0 -68
- package/src/utils/logger.js +0 -60
|
@@ -1,1252 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* API Gateway Simulator Core
|
|
3
|
-
* Simula REST APIs, HTTP APIs, WebSocket APIs, Routes, Integrations
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
const crypto = require('crypto');
|
|
7
|
-
const { v4: uuidv4 } = require('uuid');
|
|
8
|
-
const logger = require('../../utils/logger');
|
|
9
|
-
const LocalStore = require('../../utils/local-store');
|
|
10
|
-
const path = require('path');
|
|
11
|
-
const { URLPattern } = require('urlpattern-polyfill');
|
|
12
|
-
|
|
13
|
-
class APIGatewaySimulator {
|
|
14
|
-
constructor(config) {
|
|
15
|
-
this.config = config;
|
|
16
|
-
this.dataDir = path.join(process.env.AWS_LOCAL_SIMULATOR_DATA_DIR, 'apigateway');
|
|
17
|
-
this.store = new LocalStore(this.dataDir);
|
|
18
|
-
this.apis = new Map(); // REST/HTTP APIs
|
|
19
|
-
this.websocketApis = new Map(); // WebSocket APIs
|
|
20
|
-
this.deployments = new Map();
|
|
21
|
-
this.stages = new Map();
|
|
22
|
-
this.resources = new Map();
|
|
23
|
-
this.methods = new Map();
|
|
24
|
-
this.integrations = new Map();
|
|
25
|
-
this.models = new Map();
|
|
26
|
-
this.usagePlans = new Map();
|
|
27
|
-
this.apiKeys = new Map();
|
|
28
|
-
this.domainNames = new Map();
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
async initialize() {
|
|
32
|
-
logger.debug('Inicializando API Gateway Simulator...');
|
|
33
|
-
this.loadAPIs();
|
|
34
|
-
this.loadWebSocketAPIs();
|
|
35
|
-
this.loadDeployments();
|
|
36
|
-
this.loadStages();
|
|
37
|
-
this.loadResources();
|
|
38
|
-
this.loadMethods();
|
|
39
|
-
this.loadIntegrations();
|
|
40
|
-
this.loadModels();
|
|
41
|
-
this.loadUsagePlans();
|
|
42
|
-
this.loadApiKeys();
|
|
43
|
-
this.loadDomainNames();
|
|
44
|
-
|
|
45
|
-
logger.debug(`✅ API Gateway Simulator inicializado com ${this.apis.size} APIs, ${this.resources.size} resources, ${this.methods.size} methods`);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// ============ REST API Operations ============
|
|
49
|
-
|
|
50
|
-
createRestApi(params) {
|
|
51
|
-
const { name, description, version, apiKeySource, endpointConfiguration, tags } = params;
|
|
52
|
-
|
|
53
|
-
const apiId = `api_${Date.now()}_${crypto.randomBytes(4).toString('hex')}`;
|
|
54
|
-
const api = {
|
|
55
|
-
id: apiId,
|
|
56
|
-
name: name,
|
|
57
|
-
description: description || '',
|
|
58
|
-
version: version || '1.0',
|
|
59
|
-
createdDate: new Date().toISOString(),
|
|
60
|
-
apiKeySource: apiKeySource || 'HEADER',
|
|
61
|
-
endpointConfiguration: endpointConfiguration || {
|
|
62
|
-
types: ['REGIONAL']
|
|
63
|
-
},
|
|
64
|
-
tags: tags || {},
|
|
65
|
-
resources: new Map(),
|
|
66
|
-
stages: new Map(),
|
|
67
|
-
deployments: new Map(),
|
|
68
|
-
models: new Map(),
|
|
69
|
-
authorizers: new Map(),
|
|
70
|
-
gatewayResponses: new Map(),
|
|
71
|
-
documentationParts: new Map()
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
// Cria recurso raiz
|
|
75
|
-
const rootResource = {
|
|
76
|
-
id: uuidv4(),
|
|
77
|
-
path: '/',
|
|
78
|
-
pathPart: '',
|
|
79
|
-
parentId: null,
|
|
80
|
-
resourceMethods: new Map()
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
api.resources.set('/', rootResource);
|
|
84
|
-
|
|
85
|
-
this.apis.set(apiId, api);
|
|
86
|
-
this.persistAPIs();
|
|
87
|
-
|
|
88
|
-
logger.debug(`✅ REST API criada: ${name} (${apiId})`);
|
|
89
|
-
|
|
90
|
-
return {
|
|
91
|
-
id: apiId,
|
|
92
|
-
name: api.name,
|
|
93
|
-
createdDate: api.createdDate
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
getRestApis() {
|
|
98
|
-
return {
|
|
99
|
-
items: Array.from(this.apis.values()).map(api => ({
|
|
100
|
-
id: api.id,
|
|
101
|
-
name: api.name,
|
|
102
|
-
description: api.description,
|
|
103
|
-
version: api.version,
|
|
104
|
-
createdDate: api.createdDate,
|
|
105
|
-
apiKeySource: api.apiKeySource
|
|
106
|
-
}))
|
|
107
|
-
};
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
getRestApi(params) {
|
|
111
|
-
const { restApiId } = params;
|
|
112
|
-
const api = this.apis.get(restApiId);
|
|
113
|
-
|
|
114
|
-
if (!api) {
|
|
115
|
-
throw new Error(`API ${restApiId} not found`);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
return {
|
|
119
|
-
id: api.id,
|
|
120
|
-
name: api.name,
|
|
121
|
-
description: api.description,
|
|
122
|
-
version: api.version,
|
|
123
|
-
createdDate: api.createdDate,
|
|
124
|
-
apiKeySource: api.apiKeySource,
|
|
125
|
-
endpointConfiguration: api.endpointConfiguration,
|
|
126
|
-
tags: api.tags
|
|
127
|
-
};
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
deleteRestApi(params) {
|
|
131
|
-
const { restApiId } = params;
|
|
132
|
-
|
|
133
|
-
if (!this.apis.has(restApiId)) {
|
|
134
|
-
throw new Error(`API ${restApiId} not found`);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
this.apis.delete(restApiId);
|
|
138
|
-
this.persistAPIs();
|
|
139
|
-
|
|
140
|
-
return {};
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
// ============ Resource Operations ============
|
|
144
|
-
|
|
145
|
-
createResource(params) {
|
|
146
|
-
const { restApiId, parentId, pathPart } = params;
|
|
147
|
-
const api = this.apis.get(restApiId);
|
|
148
|
-
|
|
149
|
-
if (!api) {
|
|
150
|
-
throw new Error(`API ${restApiId} not found`);
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
const parentResource = api.resources.get(parentId);
|
|
154
|
-
if (!parentResource) {
|
|
155
|
-
throw new Error(`Parent resource ${parentId} not found`);
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
const resourceId = uuidv4();
|
|
159
|
-
const fullPath = parentResource.path === '/'
|
|
160
|
-
? `/${pathPart}`
|
|
161
|
-
: `${parentResource.path}/${pathPart}`;
|
|
162
|
-
|
|
163
|
-
const resource = {
|
|
164
|
-
id: resourceId,
|
|
165
|
-
path: fullPath,
|
|
166
|
-
pathPart: pathPart,
|
|
167
|
-
parentId: parentId,
|
|
168
|
-
resourceMethods: new Map()
|
|
169
|
-
};
|
|
170
|
-
|
|
171
|
-
api.resources.set(resourceId, resource);
|
|
172
|
-
this.persistResources(restApiId);
|
|
173
|
-
|
|
174
|
-
logger.debug(`📁 Recurso criado: ${fullPath} (${resourceId})`);
|
|
175
|
-
|
|
176
|
-
return {
|
|
177
|
-
id: resourceId,
|
|
178
|
-
path: resource.path,
|
|
179
|
-
parentId: resource.parentId
|
|
180
|
-
};
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
getResources(params) {
|
|
184
|
-
const { restApiId } = params;
|
|
185
|
-
const api = this.apis.get(restApiId);
|
|
186
|
-
|
|
187
|
-
if (!api) {
|
|
188
|
-
throw new Error(`API ${restApiId} not found`);
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
const items = Array.from(api.resources.values()).map(resource => ({
|
|
192
|
-
id: resource.id,
|
|
193
|
-
path: resource.path,
|
|
194
|
-
pathPart: resource.pathPart,
|
|
195
|
-
parentId: resource.parentId,
|
|
196
|
-
resourceMethods: Object.fromEntries(resource.resourceMethods)
|
|
197
|
-
}));
|
|
198
|
-
|
|
199
|
-
return { items };
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
deleteResource(params) {
|
|
203
|
-
const { restApiId, resourceId } = params;
|
|
204
|
-
const api = this.apis.get(restApiId);
|
|
205
|
-
|
|
206
|
-
if (!api) {
|
|
207
|
-
throw new Error(`API ${restApiId} not found`);
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
if (!api.resources.has(resourceId)) {
|
|
211
|
-
throw new Error(`Resource ${resourceId} not found`);
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
// Verifica se tem filhos
|
|
215
|
-
const hasChildren = Array.from(api.resources.values()).some(
|
|
216
|
-
r => r.parentId === resourceId
|
|
217
|
-
);
|
|
218
|
-
|
|
219
|
-
if (hasChildren) {
|
|
220
|
-
throw new Error('Resource has children');
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
api.resources.delete(resourceId);
|
|
224
|
-
this.persistResources(restApiId);
|
|
225
|
-
|
|
226
|
-
return {};
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
// ============ Method Operations ============
|
|
230
|
-
|
|
231
|
-
putMethod(params) {
|
|
232
|
-
const { restApiId, resourceId, httpMethod, authorizationType, apiKeyRequired, requestParameters, requestModels, authorizerId } = params;
|
|
233
|
-
const api = this.apis.get(restApiId);
|
|
234
|
-
|
|
235
|
-
if (!api) {
|
|
236
|
-
throw new Error(`API ${restApiId} not found`);
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
const resource = api.resources.get(resourceId);
|
|
240
|
-
if (!resource) {
|
|
241
|
-
throw new Error(`Resource ${resourceId} not found`);
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
const method = {
|
|
245
|
-
httpMethod: httpMethod,
|
|
246
|
-
authorizationType: authorizationType || 'NONE',
|
|
247
|
-
apiKeyRequired: apiKeyRequired || false,
|
|
248
|
-
requestParameters: requestParameters || {},
|
|
249
|
-
requestModels: requestModels || {},
|
|
250
|
-
authorizerId: authorizerId || null,
|
|
251
|
-
methodResponses: new Map(),
|
|
252
|
-
integration: null
|
|
253
|
-
};
|
|
254
|
-
|
|
255
|
-
resource.resourceMethods.set(httpMethod, method);
|
|
256
|
-
this.persistMethods(restApiId, resourceId);
|
|
257
|
-
|
|
258
|
-
logger.debug(`🔧 Método criado: ${httpMethod} ${resource.path}`);
|
|
259
|
-
|
|
260
|
-
return {
|
|
261
|
-
httpMethod: method.httpMethod,
|
|
262
|
-
authorizationType: method.authorizationType,
|
|
263
|
-
apiKeyRequired: method.apiKeyRequired
|
|
264
|
-
};
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
getMethod(params) {
|
|
268
|
-
const { restApiId, resourceId, httpMethod } = params;
|
|
269
|
-
const api = this.apis.get(restApiId);
|
|
270
|
-
|
|
271
|
-
if (!api) {
|
|
272
|
-
throw new Error(`API ${restApiId} not found`);
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
const resource = api.resources.get(resourceId);
|
|
276
|
-
if (!resource) {
|
|
277
|
-
throw new Error(`Resource ${resourceId} not found`);
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
const method = resource.resourceMethods.get(httpMethod);
|
|
281
|
-
if (!method) {
|
|
282
|
-
throw new Error(`Method ${httpMethod} not found`);
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
return {
|
|
286
|
-
httpMethod: method.httpMethod,
|
|
287
|
-
authorizationType: method.authorizationType,
|
|
288
|
-
apiKeyRequired: method.apiKeyRequired,
|
|
289
|
-
requestParameters: method.requestParameters,
|
|
290
|
-
requestModels: method.requestModels
|
|
291
|
-
};
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
deleteMethod(params) {
|
|
295
|
-
const { restApiId, resourceId, httpMethod } = params;
|
|
296
|
-
const api = this.apis.get(restApiId);
|
|
297
|
-
|
|
298
|
-
if (!api) {
|
|
299
|
-
throw new Error(`API ${restApiId} not found`);
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
const resource = api.resources.get(resourceId);
|
|
303
|
-
if (!resource) {
|
|
304
|
-
throw new Error(`Resource ${resourceId} not found`);
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
resource.resourceMethods.delete(httpMethod);
|
|
308
|
-
this.persistMethods(restApiId, resourceId);
|
|
309
|
-
|
|
310
|
-
return {};
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
// ============ Integration Operations ============
|
|
314
|
-
|
|
315
|
-
putIntegration(params) {
|
|
316
|
-
const { restApiId, resourceId, httpMethod, type, integrationHttpMethod, uri, credentials, requestParameters, requestTemplates, passthroughBehavior, timeoutInMillis, cacheNamespace, cacheKeyParameters, contentHandling } = params;
|
|
317
|
-
const api = this.apis.get(restApiId);
|
|
318
|
-
|
|
319
|
-
if (!api) {
|
|
320
|
-
throw new Error(`API ${restApiId} not found`);
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
const resource = api.resources.get(resourceId);
|
|
324
|
-
if (!resource) {
|
|
325
|
-
throw new Error(`Resource ${resourceId} not found`);
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
const method = resource.resourceMethods.get(httpMethod);
|
|
329
|
-
if (!method) {
|
|
330
|
-
throw new Error(`Method ${httpMethod} not found`);
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
const integration = {
|
|
334
|
-
type: type || 'HTTP',
|
|
335
|
-
integrationHttpMethod: integrationHttpMethod,
|
|
336
|
-
uri: uri,
|
|
337
|
-
credentials: credentials || null,
|
|
338
|
-
requestParameters: requestParameters || {},
|
|
339
|
-
requestTemplates: requestTemplates || {},
|
|
340
|
-
passthroughBehavior: passthroughBehavior || 'WHEN_NO_MATCH',
|
|
341
|
-
timeoutInMillis: timeoutInMillis || 29000,
|
|
342
|
-
cacheNamespace: cacheNamespace || '',
|
|
343
|
-
cacheKeyParameters: cacheKeyParameters || [],
|
|
344
|
-
contentHandling: contentHandling || null,
|
|
345
|
-
integrationResponses: new Map()
|
|
346
|
-
};
|
|
347
|
-
|
|
348
|
-
method.integration = integration;
|
|
349
|
-
this.persistIntegrations(restApiId, resourceId);
|
|
350
|
-
|
|
351
|
-
logger.debug(`🔌 Integração criada: ${type} -> ${uri}`);
|
|
352
|
-
|
|
353
|
-
return {
|
|
354
|
-
type: integration.type,
|
|
355
|
-
integrationHttpMethod: integration.integrationHttpMethod,
|
|
356
|
-
uri: integration.uri
|
|
357
|
-
};
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
getIntegration(params) {
|
|
361
|
-
const { restApiId, resourceId, httpMethod } = params;
|
|
362
|
-
const api = this.apis.get(restApiId);
|
|
363
|
-
|
|
364
|
-
if (!api) {
|
|
365
|
-
throw new Error(`API ${restApiId} not found`);
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
const resource = api.resources.get(resourceId);
|
|
369
|
-
if (!resource) {
|
|
370
|
-
throw new Error(`Resource ${resourceId} not found`);
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
const method = resource.resourceMethods.get(httpMethod);
|
|
374
|
-
if (!method || !method.integration) {
|
|
375
|
-
throw new Error(`Integration not found for ${httpMethod}`);
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
const integration = method.integration;
|
|
379
|
-
|
|
380
|
-
return {
|
|
381
|
-
type: integration.type,
|
|
382
|
-
integrationHttpMethod: integration.integrationHttpMethod,
|
|
383
|
-
uri: integration.uri,
|
|
384
|
-
credentials: integration.credentials,
|
|
385
|
-
requestParameters: integration.requestParameters,
|
|
386
|
-
requestTemplates: integration.requestTemplates,
|
|
387
|
-
passthroughBehavior: integration.passthroughBehavior,
|
|
388
|
-
timeoutInMillis: integration.timeoutInMillis
|
|
389
|
-
};
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
deleteIntegration(params) {
|
|
393
|
-
const { restApiId, resourceId, httpMethod } = params;
|
|
394
|
-
const api = this.apis.get(restApiId);
|
|
395
|
-
|
|
396
|
-
if (!api) {
|
|
397
|
-
throw new Error(`API ${restApiId} not found`);
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
const resource = api.resources.get(resourceId);
|
|
401
|
-
if (!resource) {
|
|
402
|
-
throw new Error(`Resource ${resourceId} not found`);
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
const method = resource.resourceMethods.get(httpMethod);
|
|
406
|
-
if (method) {
|
|
407
|
-
method.integration = null;
|
|
408
|
-
this.persistIntegrations(restApiId, resourceId);
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
return {};
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
// ============ Integration Response Operations ============
|
|
415
|
-
|
|
416
|
-
putIntegrationResponse(params) {
|
|
417
|
-
const { restApiId, resourceId, httpMethod, statusCode, selectionPattern, responseParameters, responseTemplates, contentHandling } = params;
|
|
418
|
-
const api = this.apis.get(restApiId);
|
|
419
|
-
|
|
420
|
-
if (!api) {
|
|
421
|
-
throw new Error(`API ${restApiId} not found`);
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
const resource = api.resources.get(resourceId);
|
|
425
|
-
if (!resource) {
|
|
426
|
-
throw new Error(`Resource ${resourceId} not found`);
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
const method = resource.resourceMethods.get(httpMethod);
|
|
430
|
-
if (!method || !method.integration) {
|
|
431
|
-
throw new Error(`Integration not found for ${httpMethod}`);
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
const integrationResponse = {
|
|
435
|
-
statusCode: statusCode,
|
|
436
|
-
selectionPattern: selectionPattern || '',
|
|
437
|
-
responseParameters: responseParameters || {},
|
|
438
|
-
responseTemplates: responseTemplates || {},
|
|
439
|
-
contentHandling: contentHandling || null
|
|
440
|
-
};
|
|
441
|
-
|
|
442
|
-
method.integration.integrationResponses.set(statusCode, integrationResponse);
|
|
443
|
-
this.persistIntegrations(restApiId, resourceId);
|
|
444
|
-
|
|
445
|
-
return {
|
|
446
|
-
statusCode: integrationResponse.statusCode,
|
|
447
|
-
selectionPattern: integrationResponse.selectionPattern
|
|
448
|
-
};
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
// ============ Deployment Operations ============
|
|
452
|
-
|
|
453
|
-
createDeployment(params) {
|
|
454
|
-
const { restApiId, stageName, stageDescription, description, variables } = params;
|
|
455
|
-
const api = this.apis.get(restApiId);
|
|
456
|
-
|
|
457
|
-
if (!api) {
|
|
458
|
-
throw new Error(`API ${restApiId} not found`);
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
const deploymentId = uuidv4();
|
|
462
|
-
const deployment = {
|
|
463
|
-
id: deploymentId,
|
|
464
|
-
description: description || '',
|
|
465
|
-
createdDate: new Date().toISOString(),
|
|
466
|
-
apiId: restApiId
|
|
467
|
-
};
|
|
468
|
-
|
|
469
|
-
api.deployments.set(deploymentId, deployment);
|
|
470
|
-
|
|
471
|
-
if (stageName) {
|
|
472
|
-
this.createStage({
|
|
473
|
-
restApiId,
|
|
474
|
-
stageName,
|
|
475
|
-
description: stageDescription,
|
|
476
|
-
variables
|
|
477
|
-
});
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
this.persistDeployments(restApiId);
|
|
481
|
-
|
|
482
|
-
logger.debug(`🚀 Deployment criado: ${deploymentId} para stage: ${stageName || 'N/A'}`);
|
|
483
|
-
|
|
484
|
-
return {
|
|
485
|
-
id: deploymentId,
|
|
486
|
-
createdDate: deployment.createdDate
|
|
487
|
-
};
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
// ============ Stage Operations ============
|
|
491
|
-
|
|
492
|
-
createStage(params) {
|
|
493
|
-
const { restApiId, stageName, description, variables, deploymentId, cacheClusterEnabled, cacheClusterSize, tracingEnabled } = params;
|
|
494
|
-
const api = this.apis.get(restApiId);
|
|
495
|
-
|
|
496
|
-
if (!api) {
|
|
497
|
-
throw new Error(`API ${restApiId} not found`);
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
const stage = {
|
|
501
|
-
stageName: stageName,
|
|
502
|
-
description: description || '',
|
|
503
|
-
createdDate: new Date().toISOString(),
|
|
504
|
-
lastUpdatedDate: new Date().toISOString(),
|
|
505
|
-
deploymentId: deploymentId,
|
|
506
|
-
variables: variables || {},
|
|
507
|
-
cacheClusterEnabled: cacheClusterEnabled || false,
|
|
508
|
-
cacheClusterSize: cacheClusterSize || null,
|
|
509
|
-
tracingEnabled: tracingEnabled || false,
|
|
510
|
-
methodSettings: new Map()
|
|
511
|
-
};
|
|
512
|
-
|
|
513
|
-
api.stages.set(stageName, stage);
|
|
514
|
-
this.persistStages(restApiId);
|
|
515
|
-
|
|
516
|
-
// Cria endpoint URL
|
|
517
|
-
const endpointUrl = `http://localhost:${this.config.ports.apigateway}/${restApiId}/${stageName}`;
|
|
518
|
-
|
|
519
|
-
logger.debug(`📡 Stage criado: ${stageName} em ${endpointUrl}`);
|
|
520
|
-
|
|
521
|
-
return {
|
|
522
|
-
stageName: stage.stageName,
|
|
523
|
-
createdDate: stage.createdDate,
|
|
524
|
-
deploymentId: stage.deploymentId
|
|
525
|
-
};
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
getStage(params) {
|
|
529
|
-
const { restApiId, stageName } = params;
|
|
530
|
-
const api = this.apis.get(restApiId);
|
|
531
|
-
|
|
532
|
-
if (!api) {
|
|
533
|
-
throw new Error(`API ${restApiId} not found`);
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
const stage = api.stages.get(stageName);
|
|
537
|
-
if (!stage) {
|
|
538
|
-
throw new Error(`Stage ${stageName} not found`);
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
return {
|
|
542
|
-
stageName: stage.stageName,
|
|
543
|
-
description: stage.description,
|
|
544
|
-
createdDate: stage.createdDate,
|
|
545
|
-
lastUpdatedDate: stage.lastUpdatedDate,
|
|
546
|
-
deploymentId: stage.deploymentId,
|
|
547
|
-
variables: stage.variables,
|
|
548
|
-
methodSettings: Object.fromEntries(stage.methodSettings),
|
|
549
|
-
cacheClusterEnabled: stage.cacheClusterEnabled,
|
|
550
|
-
tracingEnabled: stage.tracingEnabled
|
|
551
|
-
};
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
updateStage(params) {
|
|
555
|
-
const { restApiId, stageName, description, variables, deploymentId, tracingEnabled } = params;
|
|
556
|
-
const api = this.apis.get(restApiId);
|
|
557
|
-
|
|
558
|
-
if (!api) {
|
|
559
|
-
throw new Error(`API ${restApiId} not found`);
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
const stage = api.stages.get(stageName);
|
|
563
|
-
if (!stage) {
|
|
564
|
-
throw new Error(`Stage ${stageName} not found`);
|
|
565
|
-
}
|
|
566
|
-
|
|
567
|
-
if (description !== undefined) stage.description = description;
|
|
568
|
-
if (variables !== undefined) stage.variables = { ...stage.variables, ...variables };
|
|
569
|
-
if (deploymentId !== undefined) stage.deploymentId = deploymentId;
|
|
570
|
-
if (tracingEnabled !== undefined) stage.tracingEnabled = tracingEnabled;
|
|
571
|
-
|
|
572
|
-
stage.lastUpdatedDate = new Date().toISOString();
|
|
573
|
-
this.persistStages(restApiId);
|
|
574
|
-
|
|
575
|
-
return {
|
|
576
|
-
stageName: stage.stageName,
|
|
577
|
-
description: stage.description,
|
|
578
|
-
lastUpdatedDate: stage.lastUpdatedDate
|
|
579
|
-
};
|
|
580
|
-
}
|
|
581
|
-
|
|
582
|
-
deleteStage(params) {
|
|
583
|
-
const { restApiId, stageName } = params;
|
|
584
|
-
const api = this.apis.get(restApiId);
|
|
585
|
-
|
|
586
|
-
if (!api) {
|
|
587
|
-
throw new Error(`API ${restApiId} not found`);
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
api.stages.delete(stageName);
|
|
591
|
-
this.persistStages(restApiId);
|
|
592
|
-
|
|
593
|
-
return {};
|
|
594
|
-
}
|
|
595
|
-
|
|
596
|
-
// ============ API Key Operations ============
|
|
597
|
-
|
|
598
|
-
createApiKey(params) {
|
|
599
|
-
const { name, description, enabled, stageKeys, customerId } = params;
|
|
600
|
-
|
|
601
|
-
const apiKeyId = uuidv4();
|
|
602
|
-
const apiKey = {
|
|
603
|
-
id: apiKeyId,
|
|
604
|
-
name: name || '',
|
|
605
|
-
description: description || '',
|
|
606
|
-
enabled: enabled !== false,
|
|
607
|
-
value: crypto.randomBytes(20).toString('hex'),
|
|
608
|
-
stageKeys: stageKeys || [],
|
|
609
|
-
customerId: customerId || null,
|
|
610
|
-
createdDate: new Date().toISOString(),
|
|
611
|
-
lastUpdatedDate: new Date().toISOString()
|
|
612
|
-
};
|
|
613
|
-
|
|
614
|
-
this.apiKeys.set(apiKeyId, apiKey);
|
|
615
|
-
this.persistApiKeys();
|
|
616
|
-
|
|
617
|
-
logger.debug(`🔑 API Key criada: ${name || apiKeyId}`);
|
|
618
|
-
|
|
619
|
-
return {
|
|
620
|
-
id: apiKey.id,
|
|
621
|
-
name: apiKey.name,
|
|
622
|
-
value: apiKey.value,
|
|
623
|
-
enabled: apiKey.enabled
|
|
624
|
-
};
|
|
625
|
-
}
|
|
626
|
-
|
|
627
|
-
getApiKeys(params) {
|
|
628
|
-
const items = Array.from(this.apiKeys.values()).map(key => ({
|
|
629
|
-
id: key.id,
|
|
630
|
-
name: key.name,
|
|
631
|
-
description: key.description,
|
|
632
|
-
enabled: key.enabled,
|
|
633
|
-
createdDate: key.createdDate,
|
|
634
|
-
lastUpdatedDate: key.lastUpdatedDate
|
|
635
|
-
}));
|
|
636
|
-
|
|
637
|
-
return { items };
|
|
638
|
-
}
|
|
639
|
-
|
|
640
|
-
// ============ Usage Plan Operations ============
|
|
641
|
-
|
|
642
|
-
createUsagePlan(params) {
|
|
643
|
-
const { name, description, apiStages, throttle, quota } = params;
|
|
644
|
-
|
|
645
|
-
const usagePlanId = uuidv4();
|
|
646
|
-
const usagePlan = {
|
|
647
|
-
id: usagePlanId,
|
|
648
|
-
name: name,
|
|
649
|
-
description: description || '',
|
|
650
|
-
apiStages: apiStages || [],
|
|
651
|
-
throttle: throttle || {
|
|
652
|
-
burstLimit: 100,
|
|
653
|
-
rateLimit: 10
|
|
654
|
-
},
|
|
655
|
-
quota: quota || {
|
|
656
|
-
limit: 10000,
|
|
657
|
-
period: 'DAY',
|
|
658
|
-
offset: 0
|
|
659
|
-
},
|
|
660
|
-
createdDate: new Date().toISOString(),
|
|
661
|
-
lastUpdatedDate: new Date().toISOString()
|
|
662
|
-
};
|
|
663
|
-
|
|
664
|
-
this.usagePlans.set(usagePlanId, usagePlan);
|
|
665
|
-
this.persistUsagePlans();
|
|
666
|
-
|
|
667
|
-
logger.debug(`📊 Usage Plan criado: ${name}`);
|
|
668
|
-
|
|
669
|
-
return {
|
|
670
|
-
id: usagePlan.id,
|
|
671
|
-
name: usagePlan.name,
|
|
672
|
-
createdDate: usagePlan.createdDate
|
|
673
|
-
};
|
|
674
|
-
}
|
|
675
|
-
|
|
676
|
-
// ============ Request Execution ============
|
|
677
|
-
|
|
678
|
-
async executeRequest(apiId, stageName, method, path, headers, body, queryString) {
|
|
679
|
-
const api = this.apis.get(apiId);
|
|
680
|
-
if (!api) {
|
|
681
|
-
return this.createResponse(404, 'API not found');
|
|
682
|
-
}
|
|
683
|
-
|
|
684
|
-
const stage = api.stages.get(stageName);
|
|
685
|
-
if (!stage) {
|
|
686
|
-
return this.createResponse(404, 'Stage not found');
|
|
687
|
-
}
|
|
688
|
-
|
|
689
|
-
// Encontra o recurso e método correspondente
|
|
690
|
-
const { resource, methodDef } = this.findMatchingResource(api, method, path);
|
|
691
|
-
|
|
692
|
-
if (!resource || !methodDef) {
|
|
693
|
-
return this.createResponse(404, 'Resource or method not found');
|
|
694
|
-
}
|
|
695
|
-
|
|
696
|
-
// Verifica API Key
|
|
697
|
-
if (methodDef.apiKeyRequired) {
|
|
698
|
-
const apiKey = this.extractApiKey(headers);
|
|
699
|
-
if (!this.validateApiKey(apiKey)) {
|
|
700
|
-
return this.createResponse(403, 'Forbidden: Invalid API Key');
|
|
701
|
-
}
|
|
702
|
-
}
|
|
703
|
-
|
|
704
|
-
// Verifica throttling
|
|
705
|
-
const usagePlan = this.getUsagePlanForApi(apiId, stageName);
|
|
706
|
-
if (usagePlan && !this.checkThrottle(usagePlan)) {
|
|
707
|
-
return this.createResponse(429, 'Too Many Requests');
|
|
708
|
-
}
|
|
709
|
-
|
|
710
|
-
// Processa integração
|
|
711
|
-
const integration = methodDef.integration;
|
|
712
|
-
if (!integration) {
|
|
713
|
-
return this.createResponse(500, 'Integration not configured');
|
|
714
|
-
}
|
|
715
|
-
|
|
716
|
-
// Executa a integração baseada no tipo
|
|
717
|
-
const response = await this.executeIntegration(integration, {
|
|
718
|
-
method,
|
|
719
|
-
path,
|
|
720
|
-
headers,
|
|
721
|
-
body,
|
|
722
|
-
queryString,
|
|
723
|
-
resource,
|
|
724
|
-
stage
|
|
725
|
-
});
|
|
726
|
-
|
|
727
|
-
// Aplica resposta da integração
|
|
728
|
-
const integrationResponse = this.getMatchingIntegrationResponse(
|
|
729
|
-
integration.integrationResponses,
|
|
730
|
-
response.statusCode
|
|
731
|
-
);
|
|
732
|
-
|
|
733
|
-
if (integrationResponse) {
|
|
734
|
-
response.headers = { ...response.headers, ...integrationResponse.responseParameters };
|
|
735
|
-
response.body = this.applyResponseTemplate(integrationResponse, response.body);
|
|
736
|
-
}
|
|
737
|
-
|
|
738
|
-
return response;
|
|
739
|
-
}
|
|
740
|
-
|
|
741
|
-
findMatchingResource(api, method, path) {
|
|
742
|
-
// Busca recurso que corresponda ao path
|
|
743
|
-
const resources = Array.from(api.resources.values());
|
|
744
|
-
|
|
745
|
-
// Ordena por path mais específico primeiro
|
|
746
|
-
resources.sort((a, b) => b.path.length - a.path.length);
|
|
747
|
-
|
|
748
|
-
for (const resource of resources) {
|
|
749
|
-
if (this.matchPath(resource.path, path)) {
|
|
750
|
-
const methodDef = resource.resourceMethods.get(method);
|
|
751
|
-
if (methodDef) {
|
|
752
|
-
return { resource, methodDef };
|
|
753
|
-
}
|
|
754
|
-
}
|
|
755
|
-
}
|
|
756
|
-
|
|
757
|
-
return { resource: null, methodDef: null };
|
|
758
|
-
}
|
|
759
|
-
|
|
760
|
-
matchPath(pattern, path) {
|
|
761
|
-
// Converte path com parâmetros para regex
|
|
762
|
-
// Ex: /users/{userId}/posts/{postId}
|
|
763
|
-
const regexPattern = pattern
|
|
764
|
-
.replace(/\{([^}]+)\}/g, '([^/]+)')
|
|
765
|
-
.replace(/\//g, '\\/');
|
|
766
|
-
|
|
767
|
-
const regex = new RegExp(`^${regexPattern}$`);
|
|
768
|
-
return regex.test(path);
|
|
769
|
-
}
|
|
770
|
-
|
|
771
|
-
extractPathParams(pattern, path) {
|
|
772
|
-
const paramNames = [];
|
|
773
|
-
const regexPattern = pattern
|
|
774
|
-
.replace(/\{([^}]+)\}/g, (match, paramName) => {
|
|
775
|
-
paramNames.push(paramName);
|
|
776
|
-
return '([^/]+)';
|
|
777
|
-
})
|
|
778
|
-
.replace(/\//g, '\\/');
|
|
779
|
-
|
|
780
|
-
const regex = new RegExp(`^${regexPattern}$`);
|
|
781
|
-
const match = path.match(regex);
|
|
782
|
-
|
|
783
|
-
if (match) {
|
|
784
|
-
const params = {};
|
|
785
|
-
paramNames.forEach((name, index) => {
|
|
786
|
-
params[name] = match[index + 1];
|
|
787
|
-
});
|
|
788
|
-
return params;
|
|
789
|
-
}
|
|
790
|
-
|
|
791
|
-
return {};
|
|
792
|
-
}
|
|
793
|
-
|
|
794
|
-
async executeIntegration(integration, context) {
|
|
795
|
-
const { type, uri, integrationHttpMethod, requestTemplates, requestParameters } = integration;
|
|
796
|
-
|
|
797
|
-
// Prepara o request
|
|
798
|
-
let requestBody = context.body;
|
|
799
|
-
let requestHeaders = { ...context.headers };
|
|
800
|
-
|
|
801
|
-
// Aplica templates de request
|
|
802
|
-
if (requestTemplates && requestTemplates['application/json']) {
|
|
803
|
-
const template = requestTemplates['application/json'];
|
|
804
|
-
requestBody = this.applyRequestTemplate(template, context);
|
|
805
|
-
}
|
|
806
|
-
|
|
807
|
-
// Aplica mapeamento de parâmetros
|
|
808
|
-
if (requestParameters) {
|
|
809
|
-
for (const [key, value] of Object.entries(requestParameters)) {
|
|
810
|
-
requestHeaders[key] = this.resolveParameter(value, context);
|
|
811
|
-
}
|
|
812
|
-
}
|
|
813
|
-
|
|
814
|
-
switch(type) {
|
|
815
|
-
case 'AWS':
|
|
816
|
-
case 'AWS_PROXY':
|
|
817
|
-
return this.executeAWSIntegration(integration, context, requestBody, requestHeaders);
|
|
818
|
-
case 'HTTP':
|
|
819
|
-
case 'HTTP_PROXY':
|
|
820
|
-
return this.executeHTTPIntegration(integration, context, requestBody, requestHeaders);
|
|
821
|
-
case 'MOCK':
|
|
822
|
-
return this.executeMockIntegration(integration);
|
|
823
|
-
default:
|
|
824
|
-
return this.createResponse(501, `Integration type ${type} not supported`);
|
|
825
|
-
}
|
|
826
|
-
}
|
|
827
|
-
|
|
828
|
-
async executeAWSIntegration(integration, context, body, headers) {
|
|
829
|
-
// Simula integração com Lambda
|
|
830
|
-
const uriParts = integration.uri.split(':');
|
|
831
|
-
const functionName = uriParts[uriParts.length - 1];
|
|
832
|
-
|
|
833
|
-
// Aqui poderia chamar o simulador Lambda
|
|
834
|
-
logger.debug(`Invoking Lambda: ${functionName}`);
|
|
835
|
-
|
|
836
|
-
return this.createResponse(200, {
|
|
837
|
-
message: `Lambda ${functionName} invoked`,
|
|
838
|
-
event: context
|
|
839
|
-
});
|
|
840
|
-
}
|
|
841
|
-
|
|
842
|
-
async executeHTTPIntegration(integration, context, body, headers) {
|
|
843
|
-
const { uri, integrationHttpMethod } = integration;
|
|
844
|
-
|
|
845
|
-
logger.debug(`HTTP ${integrationHttpMethod} to ${uri}`);
|
|
846
|
-
|
|
847
|
-
// Simula chamada HTTP
|
|
848
|
-
try {
|
|
849
|
-
const response = await this.makeHttpRequest(uri, integrationHttpMethod, headers, body);
|
|
850
|
-
return {
|
|
851
|
-
statusCode: response.status,
|
|
852
|
-
headers: response.headers,
|
|
853
|
-
body: response.data
|
|
854
|
-
};
|
|
855
|
-
} catch (error) {
|
|
856
|
-
return this.createResponse(502, 'Bad Gateway');
|
|
857
|
-
}
|
|
858
|
-
}
|
|
859
|
-
|
|
860
|
-
async makeHttpRequest(url, method, headers, body) {
|
|
861
|
-
// Simulação - em implementação real, usaria axios ou fetch
|
|
862
|
-
return {
|
|
863
|
-
status: 200,
|
|
864
|
-
headers: {},
|
|
865
|
-
data: { message: 'Mock HTTP response' }
|
|
866
|
-
};
|
|
867
|
-
}
|
|
868
|
-
|
|
869
|
-
executeMockIntegration(integration) {
|
|
870
|
-
// Retorna resposta mock
|
|
871
|
-
const mockResponse = integration.requestTemplates?.mock || {};
|
|
872
|
-
return this.createResponse(200, mockResponse);
|
|
873
|
-
}
|
|
874
|
-
|
|
875
|
-
getMatchingIntegrationResponse(responses, statusCode) {
|
|
876
|
-
// Busca response que corresponda ao status code
|
|
877
|
-
if (responses.has(statusCode.toString())) {
|
|
878
|
-
return responses.get(statusCode.toString());
|
|
879
|
-
}
|
|
880
|
-
|
|
881
|
-
// Busca default
|
|
882
|
-
if (responses.has('default')) {
|
|
883
|
-
return responses.get('default');
|
|
884
|
-
}
|
|
885
|
-
|
|
886
|
-
return null;
|
|
887
|
-
}
|
|
888
|
-
|
|
889
|
-
applyRequestTemplate(template, context) {
|
|
890
|
-
// Implementação simples de template
|
|
891
|
-
try {
|
|
892
|
-
return JSON.stringify(context);
|
|
893
|
-
} catch (error) {
|
|
894
|
-
return template;
|
|
895
|
-
}
|
|
896
|
-
}
|
|
897
|
-
|
|
898
|
-
applyResponseTemplate(response, body) {
|
|
899
|
-
const templates = response.responseTemplates;
|
|
900
|
-
if (templates && templates['application/json']) {
|
|
901
|
-
// Aplica template de resposta
|
|
902
|
-
try {
|
|
903
|
-
return JSON.parse(templates['application/json'].replace(/\$\{([^}]+)\}/g, (match, path) => {
|
|
904
|
-
return this.getNestedValue(body, path);
|
|
905
|
-
}));
|
|
906
|
-
} catch (error) {
|
|
907
|
-
return body;
|
|
908
|
-
}
|
|
909
|
-
}
|
|
910
|
-
return body;
|
|
911
|
-
}
|
|
912
|
-
|
|
913
|
-
resolveParameter(value, context) {
|
|
914
|
-
// Resolve parâmetros como method.request.header.X-Header
|
|
915
|
-
if (value.includes('method.request.')) {
|
|
916
|
-
const parts = value.split('.');
|
|
917
|
-
const type = parts[2]; // header, querystring, path
|
|
918
|
-
const name = parts[3];
|
|
919
|
-
|
|
920
|
-
switch(type) {
|
|
921
|
-
case 'header':
|
|
922
|
-
return context.headers[name];
|
|
923
|
-
case 'querystring':
|
|
924
|
-
return context.queryString[name];
|
|
925
|
-
case 'path':
|
|
926
|
-
return context.pathParams[name];
|
|
927
|
-
default:
|
|
928
|
-
return null;
|
|
929
|
-
}
|
|
930
|
-
}
|
|
931
|
-
return value;
|
|
932
|
-
}
|
|
933
|
-
|
|
934
|
-
getNestedValue(obj, path) {
|
|
935
|
-
return path.split('.').reduce((current, key) => current?.[key], obj);
|
|
936
|
-
}
|
|
937
|
-
|
|
938
|
-
extractApiKey(headers) {
|
|
939
|
-
return headers['x-api-key'] || headers['X-Api-Key'];
|
|
940
|
-
}
|
|
941
|
-
|
|
942
|
-
validateApiKey(apiKeyValue) {
|
|
943
|
-
if (!apiKeyValue) return false;
|
|
944
|
-
|
|
945
|
-
for (const key of this.apiKeys.values()) {
|
|
946
|
-
if (key.value === apiKeyValue && key.enabled) {
|
|
947
|
-
return true;
|
|
948
|
-
}
|
|
949
|
-
}
|
|
950
|
-
return false;
|
|
951
|
-
}
|
|
952
|
-
|
|
953
|
-
getUsagePlanForApi(apiId, stageName) {
|
|
954
|
-
for (const plan of this.usagePlans.values()) {
|
|
955
|
-
const apiStage = plan.apiStages.find(
|
|
956
|
-
as => as.apiId === apiId && as.stage === stageName
|
|
957
|
-
);
|
|
958
|
-
if (apiStage) {
|
|
959
|
-
return plan;
|
|
960
|
-
}
|
|
961
|
-
}
|
|
962
|
-
return null;
|
|
963
|
-
}
|
|
964
|
-
|
|
965
|
-
checkThrottle(usagePlan) {
|
|
966
|
-
// Implementação simplificada de throttling
|
|
967
|
-
const { rateLimit, burstLimit } = usagePlan.throttle;
|
|
968
|
-
// Aqui poderia implementar contagem de requests
|
|
969
|
-
return true;
|
|
970
|
-
}
|
|
971
|
-
|
|
972
|
-
createResponse(statusCode, body) {
|
|
973
|
-
return {
|
|
974
|
-
statusCode: statusCode,
|
|
975
|
-
headers: {
|
|
976
|
-
'Content-Type': 'application/json',
|
|
977
|
-
'Access-Control-Allow-Origin': '*'
|
|
978
|
-
},
|
|
979
|
-
body: typeof body === 'string' ? body : JSON.stringify(body)
|
|
980
|
-
};
|
|
981
|
-
}
|
|
982
|
-
|
|
983
|
-
// ============ HTTP API Operations ============
|
|
984
|
-
|
|
985
|
-
createHttpApi(params) {
|
|
986
|
-
const { name, description, protocolType, routeSelectionExpression, corsConfiguration } = params;
|
|
987
|
-
|
|
988
|
-
const apiId = `http_${Date.now()}_${crypto.randomBytes(4).toString('hex')}`;
|
|
989
|
-
const api = {
|
|
990
|
-
id: apiId,
|
|
991
|
-
name: name,
|
|
992
|
-
description: description || '',
|
|
993
|
-
protocolType: protocolType || 'HTTP',
|
|
994
|
-
routeSelectionExpression: routeSelectionExpression || '$request.method $request.path',
|
|
995
|
-
corsConfiguration: corsConfiguration || {
|
|
996
|
-
allowOrigins: ['*'],
|
|
997
|
-
allowMethods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
|
|
998
|
-
allowHeaders: ['*'],
|
|
999
|
-
maxAge: 300
|
|
1000
|
-
},
|
|
1001
|
-
routes: new Map(),
|
|
1002
|
-
integrations: new Map(),
|
|
1003
|
-
stages: new Map(),
|
|
1004
|
-
createdDate: new Date().toISOString()
|
|
1005
|
-
};
|
|
1006
|
-
|
|
1007
|
-
this.apis.set(apiId, api);
|
|
1008
|
-
this.persistAPIs();
|
|
1009
|
-
|
|
1010
|
-
logger.debug(`✅ HTTP API criada: ${name} (${apiId})`);
|
|
1011
|
-
|
|
1012
|
-
return {
|
|
1013
|
-
ApiId: apiId,
|
|
1014
|
-
Name: api.name,
|
|
1015
|
-
ProtocolType: api.protocolType
|
|
1016
|
-
};
|
|
1017
|
-
}
|
|
1018
|
-
|
|
1019
|
-
createRoute(params) {
|
|
1020
|
-
const { apiId, routeKey, authorizationType, target } = params;
|
|
1021
|
-
const api = this.apis.get(apiId);
|
|
1022
|
-
|
|
1023
|
-
if (!api) {
|
|
1024
|
-
throw new Error(`API ${apiId} not found`);
|
|
1025
|
-
}
|
|
1026
|
-
|
|
1027
|
-
const route = {
|
|
1028
|
-
routeKey: routeKey,
|
|
1029
|
-
authorizationType: authorizationType || 'NONE',
|
|
1030
|
-
target: target,
|
|
1031
|
-
createdAt: new Date().toISOString()
|
|
1032
|
-
};
|
|
1033
|
-
|
|
1034
|
-
api.routes.set(routeKey, route);
|
|
1035
|
-
this.persistAPIs();
|
|
1036
|
-
|
|
1037
|
-
return { route };
|
|
1038
|
-
}
|
|
1039
|
-
|
|
1040
|
-
// ============ Persistence ============
|
|
1041
|
-
|
|
1042
|
-
loadAPIs() {
|
|
1043
|
-
const saved = this.store.read('__apis__');
|
|
1044
|
-
if (saved) {
|
|
1045
|
-
for (const [id, data] of Object.entries(saved)) {
|
|
1046
|
-
// Reconstitui Maps
|
|
1047
|
-
data.resources = new Map(Object.entries(data.resources || {}));
|
|
1048
|
-
data.stages = new Map(Object.entries(data.stages || {}));
|
|
1049
|
-
data.deployments = new Map(Object.entries(data.deployments || {}));
|
|
1050
|
-
data.models = new Map(Object.entries(data.models || {}));
|
|
1051
|
-
data.authorizers = new Map(Object.entries(data.authorizers || {}));
|
|
1052
|
-
this.apis.set(id, data);
|
|
1053
|
-
}
|
|
1054
|
-
}
|
|
1055
|
-
}
|
|
1056
|
-
|
|
1057
|
-
loadWebSocketAPIs() {
|
|
1058
|
-
const saved = this.store.read('__websocket_apis__');
|
|
1059
|
-
if (saved) {
|
|
1060
|
-
for (const [id, data] of Object.entries(saved)) {
|
|
1061
|
-
this.websocketApis.set(id, data);
|
|
1062
|
-
}
|
|
1063
|
-
}
|
|
1064
|
-
}
|
|
1065
|
-
|
|
1066
|
-
loadDeployments() {
|
|
1067
|
-
const saved = this.store.read('__deployments__');
|
|
1068
|
-
if (saved) {
|
|
1069
|
-
for (const [id, data] of Object.entries(saved)) {
|
|
1070
|
-
this.deployments.set(id, data);
|
|
1071
|
-
}
|
|
1072
|
-
}
|
|
1073
|
-
}
|
|
1074
|
-
|
|
1075
|
-
loadStages() {
|
|
1076
|
-
const saved = this.store.read('__stages__');
|
|
1077
|
-
if (saved) {
|
|
1078
|
-
for (const [id, data] of Object.entries(saved)) {
|
|
1079
|
-
this.stages.set(id, data);
|
|
1080
|
-
}
|
|
1081
|
-
}
|
|
1082
|
-
}
|
|
1083
|
-
|
|
1084
|
-
loadResources() {
|
|
1085
|
-
const saved = this.store.read('__resources__');
|
|
1086
|
-
if (saved) {
|
|
1087
|
-
for (const [id, data] of Object.entries(saved)) {
|
|
1088
|
-
this.resources.set(id, data);
|
|
1089
|
-
}
|
|
1090
|
-
}
|
|
1091
|
-
}
|
|
1092
|
-
|
|
1093
|
-
loadMethods() {
|
|
1094
|
-
const saved = this.store.read('__methods__');
|
|
1095
|
-
if (saved) {
|
|
1096
|
-
for (const [id, data] of Object.entries(saved)) {
|
|
1097
|
-
this.methods.set(id, data);
|
|
1098
|
-
}
|
|
1099
|
-
}
|
|
1100
|
-
}
|
|
1101
|
-
|
|
1102
|
-
loadIntegrations() {
|
|
1103
|
-
const saved = this.store.read('__integrations__');
|
|
1104
|
-
if (saved) {
|
|
1105
|
-
for (const [id, data] of Object.entries(saved)) {
|
|
1106
|
-
this.integrations.set(id, data);
|
|
1107
|
-
}
|
|
1108
|
-
}
|
|
1109
|
-
}
|
|
1110
|
-
|
|
1111
|
-
loadModels() {
|
|
1112
|
-
const saved = this.store.read('__models__');
|
|
1113
|
-
if (saved) {
|
|
1114
|
-
for (const [id, data] of Object.entries(saved)) {
|
|
1115
|
-
this.models.set(id, data);
|
|
1116
|
-
}
|
|
1117
|
-
}
|
|
1118
|
-
}
|
|
1119
|
-
|
|
1120
|
-
loadUsagePlans() {
|
|
1121
|
-
const saved = this.store.read('__usage_plans__');
|
|
1122
|
-
if (saved) {
|
|
1123
|
-
for (const [id, data] of Object.entries(saved)) {
|
|
1124
|
-
this.usagePlans.set(id, data);
|
|
1125
|
-
}
|
|
1126
|
-
}
|
|
1127
|
-
}
|
|
1128
|
-
|
|
1129
|
-
loadApiKeys() {
|
|
1130
|
-
const saved = this.store.read('__api_keys__');
|
|
1131
|
-
if (saved) {
|
|
1132
|
-
for (const [id, data] of Object.entries(saved)) {
|
|
1133
|
-
this.apiKeys.set(id, data);
|
|
1134
|
-
}
|
|
1135
|
-
}
|
|
1136
|
-
}
|
|
1137
|
-
|
|
1138
|
-
loadDomainNames() {
|
|
1139
|
-
const saved = this.store.read('__domain_names__');
|
|
1140
|
-
if (saved) {
|
|
1141
|
-
for (const [id, data] of Object.entries(saved)) {
|
|
1142
|
-
this.domainNames.set(id, data);
|
|
1143
|
-
}
|
|
1144
|
-
}
|
|
1145
|
-
}
|
|
1146
|
-
|
|
1147
|
-
persistAPIs() {
|
|
1148
|
-
const apisObj = {};
|
|
1149
|
-
for (const [id, api] of this.apis.entries()) {
|
|
1150
|
-
apisObj[id] = {
|
|
1151
|
-
...api,
|
|
1152
|
-
resources: Object.fromEntries(api.resources),
|
|
1153
|
-
stages: Object.fromEntries(api.stages),
|
|
1154
|
-
deployments: Object.fromEntries(api.deployments),
|
|
1155
|
-
models: Object.fromEntries(api.models),
|
|
1156
|
-
authorizers: Object.fromEntries(api.authorizers)
|
|
1157
|
-
};
|
|
1158
|
-
}
|
|
1159
|
-
this.store.write('__apis__', apisObj);
|
|
1160
|
-
}
|
|
1161
|
-
|
|
1162
|
-
persistResources(apiId) {
|
|
1163
|
-
const api = this.apis.get(apiId);
|
|
1164
|
-
if (api) {
|
|
1165
|
-
this.persistAPIs();
|
|
1166
|
-
}
|
|
1167
|
-
}
|
|
1168
|
-
|
|
1169
|
-
persistMethods(apiId, resourceId) {
|
|
1170
|
-
this.persistResources(apiId);
|
|
1171
|
-
}
|
|
1172
|
-
|
|
1173
|
-
persistIntegrations(apiId, resourceId) {
|
|
1174
|
-
this.persistMethods(apiId, resourceId);
|
|
1175
|
-
}
|
|
1176
|
-
|
|
1177
|
-
persistStages(apiId) {
|
|
1178
|
-
this.persistAPIs();
|
|
1179
|
-
}
|
|
1180
|
-
|
|
1181
|
-
persistDeployments(apiId) {
|
|
1182
|
-
this.persistAPIs();
|
|
1183
|
-
}
|
|
1184
|
-
|
|
1185
|
-
persistApiKeys() {
|
|
1186
|
-
const keysObj = {};
|
|
1187
|
-
for (const [id, key] of this.apiKeys.entries()) {
|
|
1188
|
-
keysObj[id] = key;
|
|
1189
|
-
}
|
|
1190
|
-
this.store.write('__api_keys__', keysObj);
|
|
1191
|
-
}
|
|
1192
|
-
|
|
1193
|
-
persistUsagePlans() {
|
|
1194
|
-
const plansObj = {};
|
|
1195
|
-
for (const [id, plan] of this.usagePlans.entries()) {
|
|
1196
|
-
plansObj[id] = plan;
|
|
1197
|
-
}
|
|
1198
|
-
this.store.write('__usage_plans__', plansObj);
|
|
1199
|
-
}
|
|
1200
|
-
|
|
1201
|
-
async reset() {
|
|
1202
|
-
this.apis.clear();
|
|
1203
|
-
this.websocketApis.clear();
|
|
1204
|
-
this.deployments.clear();
|
|
1205
|
-
this.stages.clear();
|
|
1206
|
-
this.resources.clear();
|
|
1207
|
-
this.methods.clear();
|
|
1208
|
-
this.integrations.clear();
|
|
1209
|
-
this.models.clear();
|
|
1210
|
-
this.usagePlans.clear();
|
|
1211
|
-
this.apiKeys.clear();
|
|
1212
|
-
this.domainNames.clear();
|
|
1213
|
-
|
|
1214
|
-
this.persistAPIs();
|
|
1215
|
-
this.persistApiKeys();
|
|
1216
|
-
this.persistUsagePlans();
|
|
1217
|
-
|
|
1218
|
-
logger.debug('API Gateway: Todos os dados resetados');
|
|
1219
|
-
}
|
|
1220
|
-
|
|
1221
|
-
// ============ Stats ============
|
|
1222
|
-
|
|
1223
|
-
getAPIsCount() {
|
|
1224
|
-
return this.apis.size;
|
|
1225
|
-
}
|
|
1226
|
-
|
|
1227
|
-
getDeploymentsCount() {
|
|
1228
|
-
let count = 0;
|
|
1229
|
-
for (const api of this.apis.values()) {
|
|
1230
|
-
count += api.deployments.size;
|
|
1231
|
-
}
|
|
1232
|
-
return count;
|
|
1233
|
-
}
|
|
1234
|
-
|
|
1235
|
-
getStagesCount() {
|
|
1236
|
-
let count = 0;
|
|
1237
|
-
for (const api of this.apis.values()) {
|
|
1238
|
-
count += api.stages.size;
|
|
1239
|
-
}
|
|
1240
|
-
return count;
|
|
1241
|
-
}
|
|
1242
|
-
|
|
1243
|
-
getResourcesCount() {
|
|
1244
|
-
let count = 0;
|
|
1245
|
-
for (const api of this.apis.values()) {
|
|
1246
|
-
count += api.resources.size;
|
|
1247
|
-
}
|
|
1248
|
-
return count;
|
|
1249
|
-
}
|
|
1250
|
-
}
|
|
1251
|
-
|
|
1252
|
-
module.exports = APIGatewaySimulator;
|