@aws/nx-plugin 0.20.0 → 0.22.0

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 (79) hide show
  1. package/LICENSE-THIRD-PARTY +531 -21
  2. package/README.md +3 -9
  3. package/generators.json +14 -0
  4. package/package.json +7 -7
  5. package/src/cloudscape-website/cognito-auth/__snapshots__/generator.spec.ts.snap +1 -0
  6. package/src/cloudscape-website/cognito-auth/files/common/constructs/src/core/user-identity.ts.template +1 -0
  7. package/src/infra/app/__snapshots__/generator.spec.ts.snap +54 -12
  8. package/src/infra/app/generator.js +14 -0
  9. package/src/infra/app/generator.js.map +1 -1
  10. package/src/open-api/ts-metadata/__snapshots__/generator.spec.ts.snap +49 -0
  11. package/src/open-api/ts-metadata/files/metadata.gen.ts.template +17 -0
  12. package/src/open-api/ts-metadata/generator.d.ts +16 -0
  13. package/src/open-api/ts-metadata/generator.js +32 -0
  14. package/src/open-api/ts-metadata/generator.js.map +1 -0
  15. package/src/{utils/http-api.d.ts → open-api/ts-metadata/schema.d.ts} +5 -2
  16. package/src/open-api/ts-metadata/schema.json +20 -0
  17. package/src/open-api/utils/codegen-data.js +3 -0
  18. package/src/open-api/utils/codegen-data.js.map +1 -1
  19. package/src/open-api/utils/normalise.js +6 -0
  20. package/src/open-api/utils/normalise.js.map +1 -1
  21. package/src/preset/__snapshots__/generator.spec.ts.snap +199 -0
  22. package/src/preset/files/README.md +107 -0
  23. package/src/preset/generator.d.ts +10 -0
  24. package/src/preset/generator.js +63 -0
  25. package/src/preset/generator.js.map +1 -0
  26. package/src/preset/schema.d.ts +7 -0
  27. package/src/preset/schema.json +14 -0
  28. package/src/py/fast-api/__snapshots__/generator.spec.ts.snap +972 -0
  29. package/src/py/fast-api/files/app/__name__/init.py.template +8 -0
  30. package/src/py/fast-api/generator.js +53 -15
  31. package/src/py/fast-api/generator.js.map +1 -1
  32. package/src/py/fast-api/react/__snapshots__/generator.spec.ts.snap +2 -2
  33. package/src/py/fast-api/react/files/website/components/__apiNameClassName__Provider.tsx.template +1 -1
  34. package/src/py/fast-api/react/generator.js +6 -23
  35. package/src/py/fast-api/react/generator.js.map +1 -1
  36. package/src/py/fast-api/react/open-api.d.ts +14 -0
  37. package/src/py/fast-api/react/open-api.js +53 -0
  38. package/src/py/fast-api/react/open-api.js.map +1 -0
  39. package/src/py/fast-api/schema.d.ts +3 -0
  40. package/src/py/fast-api/schema.json +8 -0
  41. package/src/setup-tests.d.ts +2 -0
  42. package/src/setup-tests.js +14 -0
  43. package/src/setup-tests.js.map +1 -0
  44. package/src/trpc/backend/__snapshots__/generator.spec.ts.snap +1061 -88
  45. package/src/trpc/backend/files/backend/src/client/index.ts.template +2 -13
  46. package/src/trpc/backend/files/backend/src/index.ts.template +1 -0
  47. package/src/trpc/backend/files/backend/src/init.ts.template +4 -4
  48. package/src/trpc/backend/files/backend/src/middleware/index.ts.template +3 -2
  49. package/src/trpc/backend/files/backend/src/router.ts.template +11 -2
  50. package/src/trpc/backend/generator.js +12 -10
  51. package/src/trpc/backend/generator.js.map +1 -1
  52. package/src/trpc/backend/schema.d.ts +1 -1
  53. package/src/trpc/backend/schema.json +8 -0
  54. package/src/trpc/react/__snapshots__/generator.spec.ts.snap +4 -19
  55. package/src/trpc/react/files/src/components/TrpcClients/TrpcProvider.tsx.template +2 -13
  56. package/src/trpc/react/generator.js +1 -1
  57. package/src/trpc/react/generator.js.map +1 -1
  58. package/src/ts/lib/__snapshots__/generator.spec.ts.snap +26 -9
  59. package/src/ts/lib/generator.js +9 -0
  60. package/src/ts/lib/generator.js.map +1 -1
  61. package/src/ts/mcp-server/__snapshots__/generator.spec.ts.snap +1 -4
  62. package/src/ts/nx-generator/generator.js +4 -3
  63. package/src/ts/nx-generator/generator.js.map +1 -1
  64. package/src/utils/api-constructs/api-constructs.d.ts +29 -0
  65. package/src/utils/api-constructs/api-constructs.js +65 -0
  66. package/src/utils/api-constructs/api-constructs.js.map +1 -0
  67. package/src/utils/api-constructs/files/app/apis/http/__apiNameKebabCase__.ts.template +135 -0
  68. package/src/utils/api-constructs/files/app/apis/rest/__apiNameKebabCase__.ts.template +156 -0
  69. package/src/utils/api-constructs/files/core/api/http/http-api.ts.template +112 -0
  70. package/src/utils/api-constructs/files/core/api/rest/rest-api.ts.template +137 -0
  71. package/src/utils/api-constructs/files/core/api/trpc/trpc-utils.ts.template +67 -0
  72. package/src/utils/api-constructs/files/core/api/utils/utils.ts.template +223 -0
  73. package/src/utils/versions.d.ts +3 -3
  74. package/src/utils/versions.js +2 -2
  75. package/src/py/fast-api/files/common/constructs/src/app/http-apis/__apiNameKebabCase__.ts.template +0 -22
  76. package/src/trpc/backend/files/common/constructs/src/app/http-apis/__apiNameKebabCase__.ts.template +0 -22
  77. package/src/utils/files/http-api/common/constructs/src/core/http-api.ts.template +0 -87
  78. package/src/utils/http-api.js +0 -51
  79. package/src/utils/http-api.js.map +0 -1
@@ -2,6 +2,13 @@
2
2
 
3
3
  exports[`fastapi project generator > should match snapshot > main-snapshot 1`] = `
4
4
  {
5
+ "apps/test_api/scripts/generate_open_api.py": "from test_api.main import app
6
+ import json, os, sys
7
+
8
+ os.makedirs(os.path.dirname(sys.argv[1]), exist_ok=True)
9
+ with open(sys.argv[1], 'w') as f:
10
+ json.dump(app.openapi(), f)
11
+ ",
5
12
  "apps/test_api/test_api/__init__.py": """"Automatically generated by Nx."""
6
13
  ",
7
14
  "apps/test_api/test_api/init.py": "import os
@@ -156,3 +163,968 @@ def test_main():
156
163
  ",
157
164
  }
158
165
  `;
166
+
167
+ exports[`fastapi project generator > should set up shared constructs for http > http-api.ts 1`] = `
168
+ "import { Construct } from 'constructs';
169
+ import { RuntimeConfig } from '../runtime-config.js';
170
+ import { Grant, IGrantable } from 'aws-cdk-lib/aws-iam';
171
+ import { HttpApiIntegration, OperationDetails } from './utils.js';
172
+ import { CfnOutput } from 'aws-cdk-lib';
173
+ import {
174
+ HttpApi as _HttpApi,
175
+ HttpApiProps as _HttpApiProps,
176
+ HttpMethod,
177
+ } from 'aws-cdk-lib/aws-apigatewayv2';
178
+
179
+ /**
180
+ * Properties for creating an HttpApi construct.
181
+ *
182
+ * @template TIntegrations - Record mapping operation names to their integrations
183
+ * @template TOperation - String literal type representing operation names
184
+ */
185
+ export interface HttpApiProps<
186
+ TIntegrations extends Record<TOperation, HttpApiIntegration>,
187
+ TOperation extends string,
188
+ > extends _HttpApiProps {
189
+ /**
190
+ * Unique name for the API, used in runtime configuration
191
+ */
192
+ readonly apiName: string;
193
+ /**
194
+ * Map of operation names to their API path and HTTP method details
195
+ */
196
+ readonly operations: Record<TOperation, OperationDetails>;
197
+ /**
198
+ * Map of operation names to their API Gateway integrations
199
+ */
200
+ readonly integrations: TIntegrations;
201
+ }
202
+
203
+ /**
204
+ * A CDK construct that creates and configures an AWS API Gateway HTTP API.
205
+ *
206
+ * This class extends the base CDK HttpApi with additional functionality:
207
+ * - Type-safe operation and integration management
208
+ * - Automatic resource creation based on path patterns
209
+ * - Integration with runtime configuration for client discovery
210
+ *
211
+ * @template TOperation - String literal type representing operation names
212
+ * @template TIntegrations - Record mapping operation names to their integrations
213
+ */
214
+ export class HttpApi<
215
+ TOperation extends string,
216
+ TIntegrations extends Record<TOperation, HttpApiIntegration>,
217
+ > extends Construct {
218
+ /** The underlying CDK HttpApi instance */
219
+ public readonly api: _HttpApi;
220
+
221
+ /** Map of operation names to their API Gateway integrations */
222
+ public readonly integrations: TIntegrations;
223
+
224
+ constructor(
225
+ scope: Construct,
226
+ id: string,
227
+ {
228
+ apiName,
229
+ operations,
230
+ integrations,
231
+ ...props
232
+ }: HttpApiProps<TIntegrations, TOperation>,
233
+ ) {
234
+ super(scope, id);
235
+ this.integrations = integrations;
236
+
237
+ // Create the API Gateway REST API with logging enabled
238
+ this.api = new _HttpApi(this, 'Api', {
239
+ ...props,
240
+ });
241
+
242
+ // Create API resources and methods for each operation
243
+ (Object.entries(operations) as [TOperation, OperationDetails][]).map(
244
+ ([op, details]) => {
245
+ this.api.addRoutes({
246
+ path: details.path.startsWith('/')
247
+ ? details.path
248
+ : \`/\${details.path}\`,
249
+ methods: [details.method as HttpMethod],
250
+ integration: integrations[op].integration,
251
+ ...integrations[op].options,
252
+ });
253
+ },
254
+ );
255
+
256
+ new CfnOutput(this, \`\${apiName}Url\`, {
257
+ value: this.api.url!,
258
+ });
259
+
260
+ // Register the API URL in runtime configuration for client discovery
261
+ RuntimeConfig.ensure(this).config.apis = {
262
+ ...RuntimeConfig.ensure(this).config.apis!,
263
+ [apiName]: this.api.url!,
264
+ };
265
+ }
266
+
267
+ /**
268
+ * Grants IAM permissions to invoke any method on this API.
269
+ *
270
+ * @param grantee - The IAM principal to grant permissions to
271
+ */
272
+ public grantInvokeAccess(grantee: IGrantable) {
273
+ Grant.addToPrincipal({
274
+ grantee,
275
+ actions: ['execute-api:Invoke'],
276
+ resourceArns: [this.api.arnForExecuteApi('*', '/*', '*')],
277
+ });
278
+ }
279
+ }
280
+ "
281
+ `;
282
+
283
+ exports[`fastapi project generator > should set up shared constructs for http > test-api.ts 1`] = `
284
+ "import { Construct } from 'constructs';
285
+ import * as url from 'url';
286
+ import {
287
+ Code,
288
+ Runtime,
289
+ Function,
290
+ FunctionProps,
291
+ Tracing,
292
+ } from 'aws-cdk-lib/aws-lambda';
293
+ import { Duration } from 'aws-cdk-lib';
294
+ import { CorsHttpMethod } from 'aws-cdk-lib/aws-apigatewayv2';
295
+ import { HttpIamAuthorizer } from 'aws-cdk-lib/aws-apigatewayv2-authorizers';
296
+ import { HttpLambdaIntegration } from 'aws-cdk-lib/aws-apigatewayv2-integrations';
297
+ import {
298
+ HttpApiIntegration,
299
+ IntegrationBuilder,
300
+ } from '../../core/api/utils.js';
301
+ import { HttpApi } from '../../core/api/http-api.js';
302
+ import {
303
+ OPERATION_DETAILS,
304
+ Operations,
305
+ } from '../../generated/test-api/metadata.gen.js';
306
+
307
+ /**
308
+ * Properties for creating a TestApi construct
309
+ *
310
+ * @template TIntegrations - Map of operation names to their integrations
311
+ */
312
+ export interface TestApiProps<
313
+ TIntegrations extends Record<Operations, HttpApiIntegration>,
314
+ > {
315
+ /**
316
+ * Map of operation names to their API Gateway integrations
317
+ */
318
+ integrations: TIntegrations;
319
+ }
320
+
321
+ /**
322
+ * A CDK construct that creates and configures an AWS API Gateway HTTP API
323
+ * specifically for TestApi.
324
+ * @template TIntegrations - Map of operation names to their integrations
325
+ */
326
+ export class TestApi<
327
+ TIntegrations extends Record<Operations, HttpApiIntegration>,
328
+ > extends HttpApi<Operations, TIntegrations> {
329
+ /**
330
+ * Creates default integrations for all operations, which implement each operation as
331
+ * its own individual lambda function.
332
+ *
333
+ * @param scope - The CDK construct scope
334
+ * @returns An IntegrationBuilder with default lambda integrations
335
+ */
336
+ public static defaultIntegrations = (scope: Construct) => {
337
+ return IntegrationBuilder.http({
338
+ operations: OPERATION_DETAILS,
339
+ defaultIntegrationOptions: {
340
+ runtime: Runtime.PYTHON_3_12,
341
+ handler: 'test_api.main.handler',
342
+ code: Code.fromAsset(
343
+ url.fileURLToPath(
344
+ new URL(
345
+ '../../../../../../dist/apps/nested/path/test_api/bundle',
346
+ import.meta.url,
347
+ ),
348
+ ),
349
+ ),
350
+ timeout: Duration.seconds(30),
351
+ tracing: Tracing.ACTIVE,
352
+ environment: {
353
+ AWS_CONNECTION_REUSE_ENABLED: '1',
354
+ },
355
+ } satisfies FunctionProps,
356
+ buildDefaultIntegration: (op, props: FunctionProps) => {
357
+ const handler = new Function(scope, \`TestApi\${op}Handler\`, props);
358
+ return {
359
+ handler,
360
+ integration: new HttpLambdaIntegration(
361
+ \`TestApi\${op}Integration\`,
362
+ handler,
363
+ ),
364
+ };
365
+ },
366
+ });
367
+ };
368
+
369
+ constructor(
370
+ scope: Construct,
371
+ id: string,
372
+ props: TestApiProps<TIntegrations>,
373
+ ) {
374
+ super(scope, id, {
375
+ apiName: 'TestApi',
376
+ corsPreflight: {
377
+ allowOrigins: ['*'],
378
+ allowMethods: [CorsHttpMethod.ANY],
379
+ allowHeaders: [
380
+ 'authorization',
381
+ 'content-type',
382
+ 'x-amz-content-sha256',
383
+ 'x-amz-date',
384
+ 'x-amz-security-token',
385
+ ],
386
+ },
387
+ defaultAuthorizer: new HttpIamAuthorizer(),
388
+ operations: OPERATION_DETAILS,
389
+ ...props,
390
+ });
391
+ }
392
+ }
393
+ "
394
+ `;
395
+
396
+ exports[`fastapi project generator > should set up shared constructs for http > utils.ts 1`] = `
397
+ "import { Integration, MethodOptions } from 'aws-cdk-lib/aws-apigateway';
398
+ import {
399
+ HttpRouteIntegration,
400
+ AddRoutesOptions,
401
+ } from 'aws-cdk-lib/aws-apigatewayv2';
402
+
403
+ /**
404
+ * Type representing applicable HTTP Methods in API Gateway
405
+ */
406
+ export type HttpMethod =
407
+ | 'ANY'
408
+ | 'DELETE'
409
+ | 'GET'
410
+ | 'HEAD'
411
+ | 'OPTIONS'
412
+ | 'PATCH'
413
+ | 'POST'
414
+ | 'PUT';
415
+
416
+ /**
417
+ * Defines the details of an API operation.
418
+ */
419
+ export interface OperationDetails {
420
+ /**
421
+ * The URL path for the operation
422
+ */
423
+ path: string;
424
+
425
+ /**
426
+ * The HTTP method for the operation
427
+ */
428
+ method: HttpMethod;
429
+ }
430
+
431
+ /**
432
+ * Represents an API Gateway REST API integration that can be attached to API methods.
433
+ */
434
+ export interface RestApiIntegration {
435
+ integration: Integration;
436
+ options?: MethodOptions;
437
+ }
438
+
439
+ /**
440
+ * Represents an API Gateway HTTP API that can be attached to API methods.
441
+ */
442
+ export interface HttpApiIntegration {
443
+ integration: HttpRouteIntegration;
444
+ options?: Omit<AddRoutesOptions, 'path' | 'methods' | 'integration'>;
445
+ }
446
+
447
+ /**
448
+ * Options for constructing an IntegrationBuilder
449
+ */
450
+ export interface IntegrationBuilderProps<
451
+ TOperation extends string,
452
+ TBaseIntegration,
453
+ TDefaultIntegrationProps extends object,
454
+ TDefaultIntegration extends TBaseIntegration,
455
+ > {
456
+ /** Map of operation names to their API path and HTTP method details */
457
+ operations: Record<TOperation, OperationDetails>;
458
+
459
+ /** Default configuration options for integrations */
460
+ defaultIntegrationOptions: TDefaultIntegrationProps;
461
+
462
+ /** Function to create a default integration for an operation */
463
+ buildDefaultIntegration: (
464
+ op: TOperation,
465
+ props: TDefaultIntegrationProps,
466
+ ) => TDefaultIntegration;
467
+ }
468
+
469
+ /**
470
+ * A builder class for creating API integrations with flexible configuration options.
471
+ *
472
+ * This class implements the builder pattern to create a set of API integrations
473
+ * with support for default configurations and selective overrides.
474
+ *
475
+ * @template TOperation - String literal type representing operation names
476
+ * @template TBaseIntegration - Base type for all integrations
477
+ * @template TIntegrations - Record mapping operation names to their integrations
478
+ * @template TDefaultIntegrationProps - Type for default integration properties
479
+ * @template TDefaultIntegration - Type for default integration implementation
480
+ */
481
+ export class IntegrationBuilder<
482
+ TOperation extends string,
483
+ TBaseIntegration,
484
+ TIntegrations extends Record<TOperation, TBaseIntegration>,
485
+ TDefaultIntegrationProps extends object,
486
+ TDefaultIntegration extends TBaseIntegration,
487
+ > {
488
+ /** Options for the integration builder */
489
+ private options: IntegrationBuilderProps<
490
+ TOperation,
491
+ TBaseIntegration,
492
+ TDefaultIntegrationProps,
493
+ TDefaultIntegration
494
+ >;
495
+
496
+ /** Map of operation names to their custom integrations */
497
+ private integrations: Partial<TIntegrations> = {};
498
+
499
+ /**
500
+ * Create an Integration Builder for an HTTP API
501
+ */
502
+ public static http = <
503
+ TOperation extends string,
504
+ TIntegrations extends Record<TOperation, TDefaultIntegration>,
505
+ TDefaultIntegrationProps extends object,
506
+ TDefaultIntegration extends HttpApiIntegration,
507
+ >(
508
+ options: IntegrationBuilderProps<
509
+ TOperation,
510
+ HttpApiIntegration,
511
+ TDefaultIntegrationProps,
512
+ TDefaultIntegration
513
+ >,
514
+ ) => {
515
+ return new IntegrationBuilder<
516
+ TOperation,
517
+ HttpApiIntegration,
518
+ TIntegrations,
519
+ TDefaultIntegrationProps,
520
+ TDefaultIntegration
521
+ >(options);
522
+ };
523
+
524
+ /**
525
+ * Create an Integration Builder for a REST API
526
+ */
527
+ public static rest = <
528
+ TOperation extends string,
529
+ TIntegrations extends Record<TOperation, TDefaultIntegration>,
530
+ TDefaultIntegrationProps extends object,
531
+ TDefaultIntegration extends RestApiIntegration,
532
+ >(
533
+ options: IntegrationBuilderProps<
534
+ TOperation,
535
+ RestApiIntegration,
536
+ TDefaultIntegrationProps,
537
+ TDefaultIntegration
538
+ >,
539
+ ) => {
540
+ return new IntegrationBuilder<
541
+ TOperation,
542
+ RestApiIntegration,
543
+ TIntegrations,
544
+ TDefaultIntegrationProps,
545
+ TDefaultIntegration
546
+ >(options);
547
+ };
548
+
549
+ private constructor(
550
+ options: IntegrationBuilderProps<
551
+ TOperation,
552
+ TBaseIntegration,
553
+ TDefaultIntegrationProps,
554
+ TDefaultIntegration
555
+ >,
556
+ ) {
557
+ this.options = options;
558
+ }
559
+
560
+ /**
561
+ * Overrides default integrations with custom implementations for specific operations.
562
+ *
563
+ * @param overrides - Map of operation names to their custom integration implementations
564
+ * @returns The builder instance with updated type information reflecting the overrides
565
+ */
566
+ public withOverrides<
567
+ TOverrideIntegrations extends Partial<Record<TOperation, TBaseIntegration>>,
568
+ >(overrides: TOverrideIntegrations) {
569
+ this.integrations = { ...this.integrations, ...overrides };
570
+ // Re-type to include the overridden integration types
571
+ return this as unknown as IntegrationBuilder<
572
+ TOperation,
573
+ TBaseIntegration,
574
+ Omit<TIntegrations, keyof TOverrideIntegrations> & TOverrideIntegrations,
575
+ TDefaultIntegrationProps,
576
+ TDefaultIntegration
577
+ >;
578
+ }
579
+
580
+ /**
581
+ * Updates the default integration options that will be used for operations
582
+ * without custom overrides.
583
+ *
584
+ * @param options - Partial default integration options to merge with existing defaults
585
+ * @returns The builder instance
586
+ */
587
+ public withDefaultOptions(options: Partial<TDefaultIntegrationProps>) {
588
+ this.options.defaultIntegrationOptions = {
589
+ ...this.options.defaultIntegrationOptions,
590
+ ...options,
591
+ };
592
+ return this;
593
+ }
594
+
595
+ /**
596
+ * Builds and returns the complete set of integrations.
597
+ *
598
+ * This method creates the final integration map by:
599
+ * 1. Including all custom overrides provided via withOverrides()
600
+ * 2. Creating default integrations for any operations without custom overrides
601
+ *
602
+ * @returns A complete map of operation names to their integrations
603
+ */
604
+ public build(): TIntegrations {
605
+ return {
606
+ ...this.integrations,
607
+ ...Object.fromEntries(
608
+ (Object.keys(this.options.operations) as TOperation[])
609
+ .filter(
610
+ (op) => !this.integrations[op as keyof typeof this.integrations],
611
+ )
612
+ .map((op) => [
613
+ op,
614
+ this.options.buildDefaultIntegration(
615
+ op,
616
+ this.options.defaultIntegrationOptions,
617
+ ),
618
+ ]),
619
+ ),
620
+ } as unknown as TIntegrations;
621
+ }
622
+ }
623
+ "
624
+ `;
625
+
626
+ exports[`fastapi project generator > should set up shared constructs for rest > rest-api.ts 1`] = `
627
+ "import { Construct } from 'constructs';
628
+ import {
629
+ RestApi as _RestApi,
630
+ RestApiProps as _RestApiProps,
631
+ AccessLogFormat,
632
+ IResource,
633
+ LogGroupLogDestination,
634
+ MethodLoggingLevel,
635
+ } from 'aws-cdk-lib/aws-apigateway';
636
+ import { LogGroup } from 'aws-cdk-lib/aws-logs';
637
+ import { RuntimeConfig } from '../runtime-config.js';
638
+ import { Grant, IGrantable } from 'aws-cdk-lib/aws-iam';
639
+ import { OperationDetails, RestApiIntegration } from './utils.js';
640
+
641
+ /**
642
+ * Properties for creating a RestApi construct.
643
+ *
644
+ * @template TIntegrations - Record mapping operation names to their integrations
645
+ * @template TOperation - String literal type representing operation names
646
+ */
647
+ export interface RestApiProps<
648
+ TIntegrations extends Record<TOperation, RestApiIntegration>,
649
+ TOperation extends string,
650
+ > extends _RestApiProps {
651
+ /**
652
+ * Unique name for the API, used in runtime configuration
653
+ */
654
+ readonly apiName: string;
655
+ /**
656
+ * Map of operation names to their API path and HTTP method details
657
+ */
658
+ readonly operations: Record<TOperation, OperationDetails>;
659
+ /**
660
+ * Map of operation names to their API Gateway integrations
661
+ */
662
+ readonly integrations: TIntegrations;
663
+ }
664
+
665
+ /**
666
+ * A CDK construct that creates and configures an AWS API Gateway REST API.
667
+ *
668
+ * This class extends the base CDK RestApi with additional functionality:
669
+ * - Type-safe operation and integration management
670
+ * - Automatic resource creation based on path patterns
671
+ * - Integration with runtime configuration for client discovery
672
+ *
673
+ * @template TOperation - String literal type representing operation names
674
+ * @template TIntegrations - Record mapping operation names to their integrations
675
+ */
676
+ export class RestApi<
677
+ TOperation extends string,
678
+ TIntegrations extends Record<TOperation, RestApiIntegration>,
679
+ > extends Construct {
680
+ /** The underlying CDK RestApi instance */
681
+ public readonly api: _RestApi;
682
+
683
+ /** Map of operation names to their API Gateway integrations */
684
+ public readonly integrations: TIntegrations;
685
+
686
+ constructor(
687
+ scope: Construct,
688
+ id: string,
689
+ {
690
+ apiName,
691
+ operations,
692
+ integrations,
693
+ ...props
694
+ }: RestApiProps<TIntegrations, TOperation>,
695
+ ) {
696
+ super(scope, id);
697
+ this.integrations = integrations;
698
+
699
+ // Create the API Gateway REST API with logging enabled
700
+ this.api = new _RestApi(this, 'Api', {
701
+ deployOptions: {
702
+ accessLogDestination: new LogGroupLogDestination(
703
+ new LogGroup(this, 'AccessLogs'),
704
+ ),
705
+ accessLogFormat: AccessLogFormat.clf(),
706
+ loggingLevel: MethodLoggingLevel.INFO,
707
+ },
708
+ ...props,
709
+ });
710
+
711
+ // Create API resources and methods for each operation
712
+ (Object.entries(operations) as [TOperation, OperationDetails][]).map(
713
+ ([op, details]) => {
714
+ const resource = this.getOrCreateResource(
715
+ this.api.root,
716
+ (details.path.startsWith('/')
717
+ ? details.path.slice(1)
718
+ : details.path
719
+ ).split('/'),
720
+ );
721
+ resource.addMethod(
722
+ details.method,
723
+ integrations[op].integration,
724
+ integrations[op].options,
725
+ );
726
+ },
727
+ );
728
+
729
+ // Register the API URL in runtime configuration for client discovery
730
+ RuntimeConfig.ensure(this).config.apis = {
731
+ ...RuntimeConfig.ensure(this).config.apis!,
732
+ [apiName]: this.api.url!,
733
+ };
734
+ }
735
+
736
+ /**
737
+ * Recursively creates or retrieves API Gateway resources based on a path pattern.
738
+ *
739
+ * @param resource - The parent API Gateway resource
740
+ * @param pathParts - Array of path segments to create or retrieve
741
+ * @returns The API Gateway resource at the end of the path
742
+ */
743
+ private getOrCreateResource(
744
+ resource: IResource,
745
+ [nextPathPart, ...pathParts]: string[],
746
+ ): IResource {
747
+ if (!nextPathPart) {
748
+ return resource;
749
+ }
750
+ const childResource =
751
+ resource.getResource(nextPathPart) ?? resource.addResource(nextPathPart);
752
+ return this.getOrCreateResource(childResource, pathParts);
753
+ }
754
+
755
+ /**
756
+ * Grants IAM permissions to invoke any method on this API.
757
+ *
758
+ * @param grantee - The IAM principal to grant permissions to
759
+ */
760
+ public grantInvokeAccess(grantee: IGrantable) {
761
+ Grant.addToPrincipal({
762
+ grantee,
763
+ actions: ['execute-api:Invoke'],
764
+ resourceArns: [this.api.arnForExecuteApi('*', '/*', '*')],
765
+ });
766
+ }
767
+ }
768
+ "
769
+ `;
770
+
771
+ exports[`fastapi project generator > should set up shared constructs for rest > test-api.ts 1`] = `
772
+ "import { Construct } from 'constructs';
773
+ import * as url from 'url';
774
+ import {
775
+ Code,
776
+ Runtime,
777
+ Function,
778
+ FunctionProps,
779
+ Tracing,
780
+ } from 'aws-cdk-lib/aws-lambda';
781
+ import {
782
+ AuthorizationType,
783
+ Cors,
784
+ LambdaIntegration,
785
+ } from 'aws-cdk-lib/aws-apigateway';
786
+ import { Duration, Stack } from 'aws-cdk-lib';
787
+ import {
788
+ PolicyDocument,
789
+ PolicyStatement,
790
+ Effect,
791
+ AccountPrincipal,
792
+ AnyPrincipal,
793
+ } from 'aws-cdk-lib/aws-iam';
794
+ import {
795
+ IntegrationBuilder,
796
+ RestApiIntegration,
797
+ } from '../../core/api/utils.js';
798
+ import { RestApi } from '../../core/api/rest-api.js';
799
+ import {
800
+ OPERATION_DETAILS,
801
+ Operations,
802
+ } from '../../generated/test-api/metadata.gen.js';
803
+
804
+ /**
805
+ * Properties for creating a TestApi construct
806
+ *
807
+ * @template TIntegrations - Map of operation names to their integrations
808
+ */
809
+ export interface TestApiProps<
810
+ TIntegrations extends Record<Operations, RestApiIntegration>,
811
+ > {
812
+ /**
813
+ * Map of operation names to their API Gateway integrations
814
+ */
815
+ integrations: TIntegrations;
816
+ }
817
+
818
+ /**
819
+ * A CDK construct that creates and configures an AWS API Gateway REST API
820
+ * specifically for TestApi.
821
+ * @template TIntegrations - Map of operation names to their integrations
822
+ */
823
+ export class TestApi<
824
+ TIntegrations extends Record<Operations, RestApiIntegration>,
825
+ > extends RestApi<Operations, TIntegrations> {
826
+ /**
827
+ * Creates default integrations for all operations, which implement each operation as
828
+ * its own individual lambda function.
829
+ *
830
+ * @param scope - The CDK construct scope
831
+ * @returns An IntegrationBuilder with default lambda integrations
832
+ */
833
+ public static defaultIntegrations = (scope: Construct) => {
834
+ return IntegrationBuilder.rest({
835
+ operations: OPERATION_DETAILS,
836
+ defaultIntegrationOptions: {
837
+ runtime: Runtime.PYTHON_3_12,
838
+ handler: 'test_api.main.handler',
839
+ code: Code.fromAsset(
840
+ url.fileURLToPath(
841
+ new URL(
842
+ '../../../../../../dist/apps/nested/path/test_api/bundle',
843
+ import.meta.url,
844
+ ),
845
+ ),
846
+ ),
847
+ timeout: Duration.seconds(30),
848
+ tracing: Tracing.ACTIVE,
849
+ environment: {
850
+ AWS_CONNECTION_REUSE_ENABLED: '1',
851
+ },
852
+ } satisfies FunctionProps,
853
+ buildDefaultIntegration: (op, props: FunctionProps) => {
854
+ const handler = new Function(scope, \`TestApi\${op}Handler\`, props);
855
+ return { handler, integration: new LambdaIntegration(handler) };
856
+ },
857
+ });
858
+ };
859
+
860
+ constructor(
861
+ scope: Construct,
862
+ id: string,
863
+ props: TestApiProps<TIntegrations>,
864
+ ) {
865
+ super(scope, id, {
866
+ apiName: 'TestApi',
867
+ defaultMethodOptions: {
868
+ authorizationType: AuthorizationType.IAM,
869
+ },
870
+ defaultCorsPreflightOptions: {
871
+ allowOrigins: Cors.ALL_ORIGINS,
872
+ allowMethods: Cors.ALL_METHODS,
873
+ },
874
+ policy: new PolicyDocument({
875
+ statements: [
876
+ // Here we grant any AWS credentials from the account that the project is deployed in to call the api.
877
+ // Machine to machine fine-grained access can be defined here using more specific principals (eg roles or
878
+ // users) and resources (eg which api paths may be invoked by which principal) if required.
879
+ new PolicyStatement({
880
+ effect: Effect.ALLOW,
881
+ principals: [new AccountPrincipal(Stack.of(scope).account)],
882
+ actions: ['execute-api:Invoke'],
883
+ resources: ['execute-api:/*'],
884
+ }),
885
+ // Open up OPTIONS to allow browsers to make unauthenticated preflight requests
886
+ new PolicyStatement({
887
+ effect: Effect.ALLOW,
888
+ principals: [new AnyPrincipal()],
889
+ actions: ['execute-api:Invoke'],
890
+ resources: ['execute-api:/*/OPTIONS/*'],
891
+ }),
892
+ ],
893
+ }),
894
+ operations: OPERATION_DETAILS,
895
+ ...props,
896
+ });
897
+ }
898
+ }
899
+ "
900
+ `;
901
+
902
+ exports[`fastapi project generator > should set up shared constructs for rest > utils.ts 1`] = `
903
+ "import { Integration, MethodOptions } from 'aws-cdk-lib/aws-apigateway';
904
+ import {
905
+ HttpRouteIntegration,
906
+ AddRoutesOptions,
907
+ } from 'aws-cdk-lib/aws-apigatewayv2';
908
+
909
+ /**
910
+ * Type representing applicable HTTP Methods in API Gateway
911
+ */
912
+ export type HttpMethod =
913
+ | 'ANY'
914
+ | 'DELETE'
915
+ | 'GET'
916
+ | 'HEAD'
917
+ | 'OPTIONS'
918
+ | 'PATCH'
919
+ | 'POST'
920
+ | 'PUT';
921
+
922
+ /**
923
+ * Defines the details of an API operation.
924
+ */
925
+ export interface OperationDetails {
926
+ /**
927
+ * The URL path for the operation
928
+ */
929
+ path: string;
930
+
931
+ /**
932
+ * The HTTP method for the operation
933
+ */
934
+ method: HttpMethod;
935
+ }
936
+
937
+ /**
938
+ * Represents an API Gateway REST API integration that can be attached to API methods.
939
+ */
940
+ export interface RestApiIntegration {
941
+ integration: Integration;
942
+ options?: MethodOptions;
943
+ }
944
+
945
+ /**
946
+ * Represents an API Gateway HTTP API that can be attached to API methods.
947
+ */
948
+ export interface HttpApiIntegration {
949
+ integration: HttpRouteIntegration;
950
+ options?: Omit<AddRoutesOptions, 'path' | 'methods' | 'integration'>;
951
+ }
952
+
953
+ /**
954
+ * Options for constructing an IntegrationBuilder
955
+ */
956
+ export interface IntegrationBuilderProps<
957
+ TOperation extends string,
958
+ TBaseIntegration,
959
+ TDefaultIntegrationProps extends object,
960
+ TDefaultIntegration extends TBaseIntegration,
961
+ > {
962
+ /** Map of operation names to their API path and HTTP method details */
963
+ operations: Record<TOperation, OperationDetails>;
964
+
965
+ /** Default configuration options for integrations */
966
+ defaultIntegrationOptions: TDefaultIntegrationProps;
967
+
968
+ /** Function to create a default integration for an operation */
969
+ buildDefaultIntegration: (
970
+ op: TOperation,
971
+ props: TDefaultIntegrationProps,
972
+ ) => TDefaultIntegration;
973
+ }
974
+
975
+ /**
976
+ * A builder class for creating API integrations with flexible configuration options.
977
+ *
978
+ * This class implements the builder pattern to create a set of API integrations
979
+ * with support for default configurations and selective overrides.
980
+ *
981
+ * @template TOperation - String literal type representing operation names
982
+ * @template TBaseIntegration - Base type for all integrations
983
+ * @template TIntegrations - Record mapping operation names to their integrations
984
+ * @template TDefaultIntegrationProps - Type for default integration properties
985
+ * @template TDefaultIntegration - Type for default integration implementation
986
+ */
987
+ export class IntegrationBuilder<
988
+ TOperation extends string,
989
+ TBaseIntegration,
990
+ TIntegrations extends Record<TOperation, TBaseIntegration>,
991
+ TDefaultIntegrationProps extends object,
992
+ TDefaultIntegration extends TBaseIntegration,
993
+ > {
994
+ /** Options for the integration builder */
995
+ private options: IntegrationBuilderProps<
996
+ TOperation,
997
+ TBaseIntegration,
998
+ TDefaultIntegrationProps,
999
+ TDefaultIntegration
1000
+ >;
1001
+
1002
+ /** Map of operation names to their custom integrations */
1003
+ private integrations: Partial<TIntegrations> = {};
1004
+
1005
+ /**
1006
+ * Create an Integration Builder for an HTTP API
1007
+ */
1008
+ public static http = <
1009
+ TOperation extends string,
1010
+ TIntegrations extends Record<TOperation, TDefaultIntegration>,
1011
+ TDefaultIntegrationProps extends object,
1012
+ TDefaultIntegration extends HttpApiIntegration,
1013
+ >(
1014
+ options: IntegrationBuilderProps<
1015
+ TOperation,
1016
+ HttpApiIntegration,
1017
+ TDefaultIntegrationProps,
1018
+ TDefaultIntegration
1019
+ >,
1020
+ ) => {
1021
+ return new IntegrationBuilder<
1022
+ TOperation,
1023
+ HttpApiIntegration,
1024
+ TIntegrations,
1025
+ TDefaultIntegrationProps,
1026
+ TDefaultIntegration
1027
+ >(options);
1028
+ };
1029
+
1030
+ /**
1031
+ * Create an Integration Builder for a REST API
1032
+ */
1033
+ public static rest = <
1034
+ TOperation extends string,
1035
+ TIntegrations extends Record<TOperation, TDefaultIntegration>,
1036
+ TDefaultIntegrationProps extends object,
1037
+ TDefaultIntegration extends RestApiIntegration,
1038
+ >(
1039
+ options: IntegrationBuilderProps<
1040
+ TOperation,
1041
+ RestApiIntegration,
1042
+ TDefaultIntegrationProps,
1043
+ TDefaultIntegration
1044
+ >,
1045
+ ) => {
1046
+ return new IntegrationBuilder<
1047
+ TOperation,
1048
+ RestApiIntegration,
1049
+ TIntegrations,
1050
+ TDefaultIntegrationProps,
1051
+ TDefaultIntegration
1052
+ >(options);
1053
+ };
1054
+
1055
+ private constructor(
1056
+ options: IntegrationBuilderProps<
1057
+ TOperation,
1058
+ TBaseIntegration,
1059
+ TDefaultIntegrationProps,
1060
+ TDefaultIntegration
1061
+ >,
1062
+ ) {
1063
+ this.options = options;
1064
+ }
1065
+
1066
+ /**
1067
+ * Overrides default integrations with custom implementations for specific operations.
1068
+ *
1069
+ * @param overrides - Map of operation names to their custom integration implementations
1070
+ * @returns The builder instance with updated type information reflecting the overrides
1071
+ */
1072
+ public withOverrides<
1073
+ TOverrideIntegrations extends Partial<Record<TOperation, TBaseIntegration>>,
1074
+ >(overrides: TOverrideIntegrations) {
1075
+ this.integrations = { ...this.integrations, ...overrides };
1076
+ // Re-type to include the overridden integration types
1077
+ return this as unknown as IntegrationBuilder<
1078
+ TOperation,
1079
+ TBaseIntegration,
1080
+ Omit<TIntegrations, keyof TOverrideIntegrations> & TOverrideIntegrations,
1081
+ TDefaultIntegrationProps,
1082
+ TDefaultIntegration
1083
+ >;
1084
+ }
1085
+
1086
+ /**
1087
+ * Updates the default integration options that will be used for operations
1088
+ * without custom overrides.
1089
+ *
1090
+ * @param options - Partial default integration options to merge with existing defaults
1091
+ * @returns The builder instance
1092
+ */
1093
+ public withDefaultOptions(options: Partial<TDefaultIntegrationProps>) {
1094
+ this.options.defaultIntegrationOptions = {
1095
+ ...this.options.defaultIntegrationOptions,
1096
+ ...options,
1097
+ };
1098
+ return this;
1099
+ }
1100
+
1101
+ /**
1102
+ * Builds and returns the complete set of integrations.
1103
+ *
1104
+ * This method creates the final integration map by:
1105
+ * 1. Including all custom overrides provided via withOverrides()
1106
+ * 2. Creating default integrations for any operations without custom overrides
1107
+ *
1108
+ * @returns A complete map of operation names to their integrations
1109
+ */
1110
+ public build(): TIntegrations {
1111
+ return {
1112
+ ...this.integrations,
1113
+ ...Object.fromEntries(
1114
+ (Object.keys(this.options.operations) as TOperation[])
1115
+ .filter(
1116
+ (op) => !this.integrations[op as keyof typeof this.integrations],
1117
+ )
1118
+ .map((op) => [
1119
+ op,
1120
+ this.options.buildDefaultIntegration(
1121
+ op,
1122
+ this.options.defaultIntegrationOptions,
1123
+ ),
1124
+ ]),
1125
+ ),
1126
+ } as unknown as TIntegrations;
1127
+ }
1128
+ }
1129
+ "
1130
+ `;