@unrdf/serverless 26.4.2

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.
@@ -0,0 +1,390 @@
1
+ /**
2
+ * @fileoverview API Gateway Configuration - REST API setup for UNRDF
3
+ *
4
+ * @description
5
+ * Configures API Gateway REST endpoints for UNRDF serverless deployments.
6
+ * Handles routing, authentication, rate limiting, and request/response transformations.
7
+ *
8
+ * @module serverless/api/api-gateway-config
9
+ * @version 1.0.0
10
+ * @license MIT
11
+ */
12
+
13
+ import { z } from 'zod';
14
+
15
+ /**
16
+ * API endpoint configuration
17
+ * @typedef {Object} EndpointConfig
18
+ * @property {string} path - API path
19
+ * @property {string} method - HTTP method
20
+ * @property {string} functionName - Lambda function name
21
+ * @property {boolean} authRequired - Require authentication
22
+ * @property {number} rateLimit - Requests per second limit
23
+ */
24
+ const EndpointConfigSchema = z.object({
25
+ path: z.string(),
26
+ method: z.enum(['GET', 'POST', 'PUT', 'DELETE', 'PATCH']),
27
+ functionName: z.string(),
28
+ authRequired: z.boolean().default(false),
29
+ rateLimit: z.number().min(1).default(100),
30
+ timeout: z.number().min(1).max(29).default(29),
31
+ });
32
+
33
+ /**
34
+ * API Gateway configuration
35
+ * @typedef {Object} ApiGatewayConfig
36
+ * @property {string} apiName - API name
37
+ * @property {string} stage - Deployment stage
38
+ * @property {EndpointConfig[]} endpoints - API endpoints
39
+ * @property {Object} cors - CORS configuration
40
+ */
41
+ const ApiGatewayConfigSchema = z.object({
42
+ apiName: z.string(),
43
+ stage: z.enum(['dev', 'staging', 'prod']).default('dev'),
44
+ endpoints: z.array(EndpointConfigSchema),
45
+ cors: z
46
+ .object({
47
+ allowOrigins: z.array(z.string()).default(['*']),
48
+ allowMethods: z.array(z.string()).default(['GET', 'POST', 'PUT', 'DELETE']),
49
+ allowHeaders: z.array(z.string()).default(['Content-Type', 'Authorization']),
50
+ maxAge: z.number().default(3600),
51
+ })
52
+ .default({}),
53
+ throttling: z
54
+ .object({
55
+ burstLimit: z.number().default(5000),
56
+ rateLimit: z.number().default(10000),
57
+ })
58
+ .default({}),
59
+ });
60
+
61
+ /**
62
+ * API Gateway Configuration Builder
63
+ *
64
+ * @class ApiGatewayConfig
65
+ *
66
+ * @description
67
+ * Fluent builder for API Gateway configurations with:
68
+ * - Endpoint routing and method mapping
69
+ * - CORS policy configuration
70
+ * - Rate limiting and throttling
71
+ * - Request/response validation
72
+ * - Authentication integration
73
+ *
74
+ * @example
75
+ * ```javascript
76
+ * const config = new ApiGatewayConfig('unrdf-api')
77
+ * .addEndpoint('/query', 'POST', 'queryFunction')
78
+ * .addEndpoint('/triples', 'GET', 'listFunction')
79
+ * .enableCors(['https://example.com'])
80
+ * .setRateLimit(1000)
81
+ * .build();
82
+ * ```
83
+ */
84
+ export class ApiGatewayConfig {
85
+ /**
86
+ * Configuration object
87
+ * @type {Object}
88
+ * @private
89
+ */
90
+ #config;
91
+
92
+ /**
93
+ * Create API Gateway configuration
94
+ *
95
+ * @param {string} apiName - API name
96
+ * @param {string} [stage='dev'] - Deployment stage
97
+ */
98
+ constructor(apiName, stage = 'dev') {
99
+ this.#config = {
100
+ apiName,
101
+ stage,
102
+ endpoints: [],
103
+ cors: {
104
+ allowOrigins: ['*'],
105
+ allowMethods: ['GET', 'POST', 'PUT', 'DELETE'],
106
+ allowHeaders: ['Content-Type', 'Authorization'],
107
+ maxAge: 3600,
108
+ },
109
+ throttling: {
110
+ burstLimit: 5000,
111
+ rateLimit: 10000,
112
+ },
113
+ };
114
+ }
115
+
116
+ /**
117
+ * Add API endpoint
118
+ *
119
+ * @param {string} path - Endpoint path
120
+ * @param {string} method - HTTP method
121
+ * @param {string} functionName - Lambda function name
122
+ * @param {Object} [options={}] - Additional options
123
+ * @returns {ApiGatewayConfig} This instance for chaining
124
+ *
125
+ * @example
126
+ * ```javascript
127
+ * config.addEndpoint('/query', 'POST', 'queryFunction', {
128
+ * authRequired: true,
129
+ * rateLimit: 100
130
+ * });
131
+ * ```
132
+ */
133
+ addEndpoint(path, method, functionName, options = {}) {
134
+ this.#config.endpoints.push({
135
+ path,
136
+ method,
137
+ functionName,
138
+ authRequired: options.authRequired || false,
139
+ rateLimit: options.rateLimit || 100,
140
+ timeout: options.timeout || 29,
141
+ });
142
+ return this;
143
+ }
144
+
145
+ /**
146
+ * Enable CORS with custom origins
147
+ *
148
+ * @param {string[]} allowOrigins - Allowed origins
149
+ * @param {Object} [options={}] - CORS options
150
+ * @returns {ApiGatewayConfig} This instance for chaining
151
+ *
152
+ * @example
153
+ * ```javascript
154
+ * config.enableCors(['https://example.com'], {
155
+ * allowMethods: ['GET', 'POST'],
156
+ * maxAge: 7200
157
+ * });
158
+ * ```
159
+ */
160
+ enableCors(allowOrigins, options = {}) {
161
+ this.#config.cors = {
162
+ allowOrigins,
163
+ allowMethods: options.allowMethods || this.#config.cors.allowMethods,
164
+ allowHeaders: options.allowHeaders || this.#config.cors.allowHeaders,
165
+ maxAge: options.maxAge || this.#config.cors.maxAge,
166
+ };
167
+ return this;
168
+ }
169
+
170
+ /**
171
+ * Set API-wide rate limiting
172
+ *
173
+ * @param {number} rateLimit - Requests per second
174
+ * @param {number} [burstLimit] - Burst capacity
175
+ * @returns {ApiGatewayConfig} This instance for chaining
176
+ *
177
+ * @example
178
+ * ```javascript
179
+ * config.setRateLimit(1000, 5000);
180
+ * ```
181
+ */
182
+ setRateLimit(rateLimit, burstLimit) {
183
+ this.#config.throttling = {
184
+ rateLimit,
185
+ burstLimit: burstLimit || rateLimit * 5,
186
+ };
187
+ return this;
188
+ }
189
+
190
+ /**
191
+ * Set deployment stage
192
+ *
193
+ * @param {string} stage - Stage name
194
+ * @returns {ApiGatewayConfig} This instance for chaining
195
+ */
196
+ setStage(stage) {
197
+ this.#config.stage = stage;
198
+ return this;
199
+ }
200
+
201
+ /**
202
+ * Build and validate configuration
203
+ *
204
+ * @returns {Object} Validated configuration
205
+ *
206
+ * @throws {Error} If configuration is invalid
207
+ *
208
+ * @example
209
+ * ```javascript
210
+ * const config = builder.build();
211
+ * ```
212
+ */
213
+ build() {
214
+ return ApiGatewayConfigSchema.parse(this.#config);
215
+ }
216
+
217
+ /**
218
+ * Export as OpenAPI 3.0 specification
219
+ *
220
+ * @returns {Object} OpenAPI specification
221
+ *
222
+ * @example
223
+ * ```javascript
224
+ * const openapi = config.toOpenAPI();
225
+ * await fs.writeFile('openapi.json', JSON.stringify(openapi, null, 2));
226
+ * ```
227
+ */
228
+ toOpenAPI() {
229
+ const paths = {};
230
+
231
+ for (const endpoint of this.#config.endpoints) {
232
+ if (!paths[endpoint.path]) {
233
+ paths[endpoint.path] = {};
234
+ }
235
+
236
+ paths[endpoint.path][endpoint.method.toLowerCase()] = {
237
+ summary: `${endpoint.method} ${endpoint.path}`,
238
+ operationId: endpoint.functionName,
239
+ security: endpoint.authRequired ? [{ apiKey: [] }] : [],
240
+ responses: {
241
+ 200: {
242
+ description: 'Successful response',
243
+ content: {
244
+ 'application/json': {
245
+ schema: { type: 'object' },
246
+ },
247
+ },
248
+ },
249
+ 400: { description: 'Bad request' },
250
+ 401: { description: 'Unauthorized' },
251
+ 429: { description: 'Too many requests' },
252
+ 500: { description: 'Internal server error' },
253
+ },
254
+ };
255
+ }
256
+
257
+ return {
258
+ openapi: '3.0.0',
259
+ info: {
260
+ title: this.#config.apiName,
261
+ version: '1.0.0',
262
+ description: 'UNRDF Serverless API',
263
+ },
264
+ servers: [
265
+ {
266
+ url: `https://api.example.com/${this.#config.stage}`,
267
+ description: `${this.#config.stage} environment`,
268
+ },
269
+ ],
270
+ paths,
271
+ components: {
272
+ securitySchemes: {
273
+ apiKey: {
274
+ type: 'apiKey',
275
+ in: 'header',
276
+ name: 'X-API-Key',
277
+ },
278
+ },
279
+ },
280
+ };
281
+ }
282
+ }
283
+
284
+ /**
285
+ * Create default UNRDF API configuration
286
+ *
287
+ * @param {string} [stage='dev'] - Deployment stage
288
+ * @returns {Object} API Gateway configuration
289
+ *
290
+ * @example
291
+ * ```javascript
292
+ * const config = createDefaultApiConfig('prod');
293
+ * ```
294
+ */
295
+ export function createDefaultApiConfig(stage = 'dev') {
296
+ return new ApiGatewayConfig(`unrdf-api-${stage}`, stage)
297
+ .addEndpoint('/query', 'POST', 'queryFunction', {
298
+ authRequired: stage === 'prod',
299
+ rateLimit: 100,
300
+ })
301
+ .addEndpoint('/triples', 'GET', 'queryFunction', {
302
+ rateLimit: 200,
303
+ })
304
+ .addEndpoint('/triples', 'POST', 'ingestFunction', {
305
+ authRequired: true,
306
+ rateLimit: 50,
307
+ })
308
+ .addEndpoint('/health', 'GET', 'queryFunction', {
309
+ rateLimit: 1000,
310
+ })
311
+ .enableCors(['*'])
312
+ .setRateLimit(10000, 50000)
313
+ .build();
314
+ }
315
+
316
+ /**
317
+ * Validate API Gateway request
318
+ *
319
+ * @param {Object} event - API Gateway event
320
+ * @param {Object} schema - Zod validation schema
321
+ * @returns {Object} Validated request body
322
+ *
323
+ * @throws {Error} If validation fails
324
+ *
325
+ * @example
326
+ * ```javascript
327
+ * export async function handler(event) {
328
+ * const body = validateApiRequest(event, z.object({
329
+ * query: z.string(),
330
+ * bindings: z.record(z.string()).optional()
331
+ * }));
332
+ * // ... handle request
333
+ * }
334
+ * ```
335
+ */
336
+ export function validateApiRequest(event, schema) {
337
+ try {
338
+ const body = JSON.parse(event.body || '{}');
339
+ return schema.parse(body);
340
+ } catch (error) {
341
+ throw new Error(`Request validation failed: ${error.message}`, { cause: error });
342
+ }
343
+ }
344
+
345
+ /**
346
+ * Create API Gateway response
347
+ *
348
+ * @param {number} statusCode - HTTP status code
349
+ * @param {Object} body - Response body
350
+ * @param {Object} [headers={}] - Additional headers
351
+ * @returns {Object} API Gateway response
352
+ *
353
+ * @example
354
+ * ```javascript
355
+ * return createApiResponse(200, { results: data });
356
+ * ```
357
+ */
358
+ export function createApiResponse(statusCode, body, headers = {}) {
359
+ return {
360
+ statusCode,
361
+ headers: {
362
+ 'Content-Type': 'application/json',
363
+ 'Access-Control-Allow-Origin': '*',
364
+ 'Access-Control-Allow-Methods': 'GET,POST,PUT,DELETE',
365
+ ...headers,
366
+ },
367
+ body: JSON.stringify(body),
368
+ };
369
+ }
370
+
371
+ /**
372
+ * Create error response
373
+ *
374
+ * @param {Error} error - Error object
375
+ * @param {number} [statusCode=500] - HTTP status code
376
+ * @returns {Object} Error response
377
+ *
378
+ * @example
379
+ * ```javascript
380
+ * catch (error) {
381
+ * return createErrorResponse(error, 400);
382
+ * }
383
+ * ```
384
+ */
385
+ export function createErrorResponse(error, statusCode = 500) {
386
+ return createApiResponse(statusCode, {
387
+ error: error.message,
388
+ type: error.constructor.name,
389
+ });
390
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * @fileoverview API Gateway Module Exports
3
+ * @module serverless/api
4
+ */
5
+
6
+ export {
7
+ ApiGatewayConfig,
8
+ createDefaultApiConfig,
9
+ validateApiRequest,
10
+ createApiResponse,
11
+ createErrorResponse,
12
+ } from './api-gateway-config.mjs';
@@ -0,0 +1,6 @@
1
+ /**
2
+ * @fileoverview CDK Infrastructure Module Exports
3
+ * @module serverless/cdk
4
+ */
5
+
6
+ export { UNRDFStack, createUNRDFStack } from './unrdf-stack.mjs';