@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.
- package/LICENSE-THIRD-PARTY +531 -21
- package/README.md +3 -9
- package/generators.json +14 -0
- package/package.json +7 -7
- package/src/cloudscape-website/cognito-auth/__snapshots__/generator.spec.ts.snap +1 -0
- package/src/cloudscape-website/cognito-auth/files/common/constructs/src/core/user-identity.ts.template +1 -0
- package/src/infra/app/__snapshots__/generator.spec.ts.snap +54 -12
- package/src/infra/app/generator.js +14 -0
- package/src/infra/app/generator.js.map +1 -1
- package/src/open-api/ts-metadata/__snapshots__/generator.spec.ts.snap +49 -0
- package/src/open-api/ts-metadata/files/metadata.gen.ts.template +17 -0
- package/src/open-api/ts-metadata/generator.d.ts +16 -0
- package/src/open-api/ts-metadata/generator.js +32 -0
- package/src/open-api/ts-metadata/generator.js.map +1 -0
- package/src/{utils/http-api.d.ts → open-api/ts-metadata/schema.d.ts} +5 -2
- package/src/open-api/ts-metadata/schema.json +20 -0
- package/src/open-api/utils/codegen-data.js +3 -0
- package/src/open-api/utils/codegen-data.js.map +1 -1
- package/src/open-api/utils/normalise.js +6 -0
- package/src/open-api/utils/normalise.js.map +1 -1
- package/src/preset/__snapshots__/generator.spec.ts.snap +199 -0
- package/src/preset/files/README.md +107 -0
- package/src/preset/generator.d.ts +10 -0
- package/src/preset/generator.js +63 -0
- package/src/preset/generator.js.map +1 -0
- package/src/preset/schema.d.ts +7 -0
- package/src/preset/schema.json +14 -0
- package/src/py/fast-api/__snapshots__/generator.spec.ts.snap +972 -0
- package/src/py/fast-api/files/app/__name__/init.py.template +8 -0
- package/src/py/fast-api/generator.js +53 -15
- package/src/py/fast-api/generator.js.map +1 -1
- package/src/py/fast-api/react/__snapshots__/generator.spec.ts.snap +2 -2
- package/src/py/fast-api/react/files/website/components/__apiNameClassName__Provider.tsx.template +1 -1
- package/src/py/fast-api/react/generator.js +6 -23
- package/src/py/fast-api/react/generator.js.map +1 -1
- package/src/py/fast-api/react/open-api.d.ts +14 -0
- package/src/py/fast-api/react/open-api.js +53 -0
- package/src/py/fast-api/react/open-api.js.map +1 -0
- package/src/py/fast-api/schema.d.ts +3 -0
- package/src/py/fast-api/schema.json +8 -0
- package/src/setup-tests.d.ts +2 -0
- package/src/setup-tests.js +14 -0
- package/src/setup-tests.js.map +1 -0
- package/src/trpc/backend/__snapshots__/generator.spec.ts.snap +1061 -88
- package/src/trpc/backend/files/backend/src/client/index.ts.template +2 -13
- package/src/trpc/backend/files/backend/src/index.ts.template +1 -0
- package/src/trpc/backend/files/backend/src/init.ts.template +4 -4
- package/src/trpc/backend/files/backend/src/middleware/index.ts.template +3 -2
- package/src/trpc/backend/files/backend/src/router.ts.template +11 -2
- package/src/trpc/backend/generator.js +12 -10
- package/src/trpc/backend/generator.js.map +1 -1
- package/src/trpc/backend/schema.d.ts +1 -1
- package/src/trpc/backend/schema.json +8 -0
- package/src/trpc/react/__snapshots__/generator.spec.ts.snap +4 -19
- package/src/trpc/react/files/src/components/TrpcClients/TrpcProvider.tsx.template +2 -13
- package/src/trpc/react/generator.js +1 -1
- package/src/trpc/react/generator.js.map +1 -1
- package/src/ts/lib/__snapshots__/generator.spec.ts.snap +26 -9
- package/src/ts/lib/generator.js +9 -0
- package/src/ts/lib/generator.js.map +1 -1
- package/src/ts/mcp-server/__snapshots__/generator.spec.ts.snap +1 -4
- package/src/ts/nx-generator/generator.js +4 -3
- package/src/ts/nx-generator/generator.js.map +1 -1
- package/src/utils/api-constructs/api-constructs.d.ts +29 -0
- package/src/utils/api-constructs/api-constructs.js +65 -0
- package/src/utils/api-constructs/api-constructs.js.map +1 -0
- package/src/utils/api-constructs/files/app/apis/http/__apiNameKebabCase__.ts.template +135 -0
- package/src/utils/api-constructs/files/app/apis/rest/__apiNameKebabCase__.ts.template +156 -0
- package/src/utils/api-constructs/files/core/api/http/http-api.ts.template +112 -0
- package/src/utils/api-constructs/files/core/api/rest/rest-api.ts.template +137 -0
- package/src/utils/api-constructs/files/core/api/trpc/trpc-utils.ts.template +67 -0
- package/src/utils/api-constructs/files/core/api/utils/utils.ts.template +223 -0
- package/src/utils/versions.d.ts +3 -3
- package/src/utils/versions.js +2 -2
- package/src/py/fast-api/files/common/constructs/src/app/http-apis/__apiNameKebabCase__.ts.template +0 -22
- package/src/trpc/backend/files/common/constructs/src/app/http-apis/__apiNameKebabCase__.ts.template +0 -22
- package/src/utils/files/http-api/common/constructs/src/core/http-api.ts.template +0 -87
- package/src/utils/http-api.js +0 -51
- package/src/utils/http-api.js.map +0 -1
|
@@ -1,14 +1,7 @@
|
|
|
1
1
|
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
2
2
|
|
|
3
3
|
exports[`trpc backend generator > should generate backend and schema projects > apps/test-api/backend/src/client/index.ts 1`] = `
|
|
4
|
-
"import {
|
|
5
|
-
createTRPCClient,
|
|
6
|
-
httpBatchLink,
|
|
7
|
-
httpLink,
|
|
8
|
-
HTTPBatchLinkOptions,
|
|
9
|
-
HTTPLinkOptions,
|
|
10
|
-
splitLink,
|
|
11
|
-
} from '@trpc/client';
|
|
4
|
+
"import { createTRPCClient, httpLink, HTTPLinkOptions } from '@trpc/client';
|
|
12
5
|
|
|
13
6
|
import { AppRouter } from '../router.js';
|
|
14
7
|
import { sigv4Fetch } from './sigv4.js';
|
|
@@ -18,20 +11,12 @@ export interface TestApiClientConfig {
|
|
|
18
11
|
}
|
|
19
12
|
|
|
20
13
|
export const createTestApiClient = (config: TestApiClientConfig) => {
|
|
21
|
-
const linkOptions: HTTPLinkOptions<any>
|
|
14
|
+
const linkOptions: HTTPLinkOptions<any> = {
|
|
22
15
|
url: config.url,
|
|
23
16
|
fetch: sigv4Fetch,
|
|
24
17
|
};
|
|
25
18
|
return createTRPCClient<AppRouter>({
|
|
26
|
-
links: [
|
|
27
|
-
splitLink({
|
|
28
|
-
condition(op) {
|
|
29
|
-
return op.context.skipBatch === true;
|
|
30
|
-
},
|
|
31
|
-
true: httpLink(linkOptions),
|
|
32
|
-
false: httpBatchLink(linkOptions),
|
|
33
|
-
}),
|
|
34
|
-
],
|
|
19
|
+
links: [httpLink(linkOptions)],
|
|
35
20
|
});
|
|
36
21
|
};
|
|
37
22
|
"
|
|
@@ -52,6 +37,7 @@ export const sigv4Fetch = (async (...args) => {
|
|
|
52
37
|
|
|
53
38
|
exports[`trpc backend generator > should generate backend and schema projects > apps/test-api/backend/src/index.ts 1`] = `
|
|
54
39
|
"export type { AppRouter } from './router.js';
|
|
40
|
+
export { appRouter } from './router.js';
|
|
55
41
|
export type { Context } from './init.js';
|
|
56
42
|
export * from './client/index.js';
|
|
57
43
|
"
|
|
@@ -75,10 +61,10 @@ export type Context = IMiddlewareContext;
|
|
|
75
61
|
export const t = initTRPC.context<Context>().create();
|
|
76
62
|
|
|
77
63
|
export const publicProcedure = t.procedure
|
|
78
|
-
.
|
|
79
|
-
.
|
|
80
|
-
.
|
|
81
|
-
.
|
|
64
|
+
.concat(createLoggerPlugin())
|
|
65
|
+
.concat(createTracerPlugin())
|
|
66
|
+
.concat(createMetricsPlugin())
|
|
67
|
+
.concat(createErrorPlugin());
|
|
82
68
|
"
|
|
83
69
|
`;
|
|
84
70
|
|
|
@@ -292,7 +278,7 @@ exports[`trpc backend generator > should generate backend and schema projects >
|
|
|
292
278
|
} from '@trpc/server/adapters/aws-lambda';
|
|
293
279
|
import { echo } from './procedures/echo.js';
|
|
294
280
|
import { t } from './init.js';
|
|
295
|
-
import { APIGatewayProxyEventV2WithIAMAuthorizer } from 'aws-lambda';
|
|
281
|
+
import type { APIGatewayProxyEventV2WithIAMAuthorizer } from 'aws-lambda';
|
|
296
282
|
|
|
297
283
|
export const router = t.router;
|
|
298
284
|
|
|
@@ -333,50 +319,218 @@ export type IEchoOutput = z.TypeOf<typeof EchoOutputSchema>;
|
|
|
333
319
|
"
|
|
334
320
|
`;
|
|
335
321
|
|
|
336
|
-
exports[`trpc backend generator > should set up shared constructs > http-api.ts 1`] = `
|
|
322
|
+
exports[`trpc backend generator > should set up shared constructs for http > http-api.ts 1`] = `
|
|
337
323
|
"import { Construct } from 'constructs';
|
|
338
|
-
import {
|
|
324
|
+
import { RuntimeConfig } from '../runtime-config.js';
|
|
325
|
+
import { Grant, IGrantable } from 'aws-cdk-lib/aws-iam';
|
|
326
|
+
import { HttpApiIntegration, OperationDetails } from './utils.js';
|
|
327
|
+
import { CfnOutput } from 'aws-cdk-lib';
|
|
339
328
|
import {
|
|
340
|
-
CorsHttpMethod,
|
|
341
329
|
HttpApi as _HttpApi,
|
|
330
|
+
HttpApiProps as _HttpApiProps,
|
|
342
331
|
HttpMethod,
|
|
343
|
-
IHttpRouteAuthorizer,
|
|
344
332
|
} from 'aws-cdk-lib/aws-apigatewayv2';
|
|
345
|
-
import { HttpLambdaIntegration } from 'aws-cdk-lib/aws-apigatewayv2-integrations';
|
|
346
|
-
import { Code, Function, Runtime, Tracing } from 'aws-cdk-lib/aws-lambda';
|
|
347
|
-
import { Grant, IGrantable } from 'aws-cdk-lib/aws-iam';
|
|
348
|
-
import { RuntimeConfig } from './runtime-config.js';
|
|
349
333
|
|
|
350
|
-
|
|
334
|
+
/**
|
|
335
|
+
* Properties for creating an HttpApi construct.
|
|
336
|
+
*
|
|
337
|
+
* @template TIntegrations - Record mapping operation names to their integrations
|
|
338
|
+
* @template TOperation - String literal type representing operation names
|
|
339
|
+
*/
|
|
340
|
+
export interface HttpApiProps<
|
|
341
|
+
TIntegrations extends Record<TOperation, HttpApiIntegration>,
|
|
342
|
+
TOperation extends string,
|
|
343
|
+
> extends _HttpApiProps {
|
|
344
|
+
/**
|
|
345
|
+
* Unique name for the API, used in runtime configuration
|
|
346
|
+
*/
|
|
351
347
|
readonly apiName: string;
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
readonly
|
|
356
|
-
|
|
348
|
+
/**
|
|
349
|
+
* Map of operation names to their API path and HTTP method details
|
|
350
|
+
*/
|
|
351
|
+
readonly operations: Record<TOperation, OperationDetails>;
|
|
352
|
+
/**
|
|
353
|
+
* Map of operation names to their API Gateway integrations
|
|
354
|
+
*/
|
|
355
|
+
readonly integrations: TIntegrations;
|
|
357
356
|
}
|
|
358
357
|
|
|
359
|
-
|
|
358
|
+
/**
|
|
359
|
+
* A CDK construct that creates and configures an AWS API Gateway HTTP API.
|
|
360
|
+
*
|
|
361
|
+
* This class extends the base CDK HttpApi with additional functionality:
|
|
362
|
+
* - Type-safe operation and integration management
|
|
363
|
+
* - Automatic resource creation based on path patterns
|
|
364
|
+
* - Integration with runtime configuration for client discovery
|
|
365
|
+
*
|
|
366
|
+
* @template TOperation - String literal type representing operation names
|
|
367
|
+
* @template TIntegrations - Record mapping operation names to their integrations
|
|
368
|
+
*/
|
|
369
|
+
export class HttpApi<
|
|
370
|
+
TOperation extends string,
|
|
371
|
+
TIntegrations extends Record<TOperation, HttpApiIntegration>,
|
|
372
|
+
> extends Construct {
|
|
373
|
+
/** The underlying CDK HttpApi instance */
|
|
360
374
|
public readonly api: _HttpApi;
|
|
361
|
-
public readonly routerFunction: Function;
|
|
362
375
|
|
|
363
|
-
|
|
376
|
+
/** Map of operation names to their API Gateway integrations */
|
|
377
|
+
public readonly integrations: TIntegrations;
|
|
378
|
+
|
|
379
|
+
constructor(
|
|
380
|
+
scope: Construct,
|
|
381
|
+
id: string,
|
|
382
|
+
{
|
|
383
|
+
apiName,
|
|
384
|
+
operations,
|
|
385
|
+
integrations,
|
|
386
|
+
...props
|
|
387
|
+
}: HttpApiProps<TIntegrations, TOperation>,
|
|
388
|
+
) {
|
|
364
389
|
super(scope, id);
|
|
390
|
+
this.integrations = integrations;
|
|
365
391
|
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
392
|
+
// Create the API Gateway REST API with logging enabled
|
|
393
|
+
this.api = new _HttpApi(this, 'Api', {
|
|
394
|
+
...props,
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
// Create API resources and methods for each operation
|
|
398
|
+
(Object.entries(operations) as [TOperation, OperationDetails][]).map(
|
|
399
|
+
([op, details]) => {
|
|
400
|
+
this.api.addRoutes({
|
|
401
|
+
path: details.path.startsWith('/')
|
|
402
|
+
? details.path
|
|
403
|
+
: \`/\${details.path}\`,
|
|
404
|
+
methods: [details.method as HttpMethod],
|
|
405
|
+
integration: integrations[op].integration,
|
|
406
|
+
...integrations[op].options,
|
|
407
|
+
});
|
|
408
|
+
},
|
|
409
|
+
);
|
|
410
|
+
|
|
411
|
+
new CfnOutput(this, \`\${apiName}Url\`, {
|
|
412
|
+
value: this.api.url!,
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
// Register the API URL in runtime configuration for client discovery
|
|
416
|
+
RuntimeConfig.ensure(this).config.apis = {
|
|
417
|
+
...RuntimeConfig.ensure(this).config.apis!,
|
|
418
|
+
[apiName]: this.api.url!,
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
/**
|
|
423
|
+
* Grants IAM permissions to invoke any method on this API.
|
|
424
|
+
*
|
|
425
|
+
* @param grantee - The IAM principal to grant permissions to
|
|
426
|
+
*/
|
|
427
|
+
public grantInvokeAccess(grantee: IGrantable) {
|
|
428
|
+
Grant.addToPrincipal({
|
|
429
|
+
grantee,
|
|
430
|
+
actions: ['execute-api:Invoke'],
|
|
431
|
+
resourceArns: [this.api.arnForExecuteApi('*', '/*', '*')],
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
"
|
|
436
|
+
`;
|
|
437
|
+
|
|
438
|
+
exports[`trpc backend generator > should set up shared constructs for http > test-api.ts 1`] = `
|
|
439
|
+
"import { Construct } from 'constructs';
|
|
440
|
+
import * as url from 'url';
|
|
441
|
+
import {
|
|
442
|
+
Code,
|
|
443
|
+
Runtime,
|
|
444
|
+
Function,
|
|
445
|
+
FunctionProps,
|
|
446
|
+
Tracing,
|
|
447
|
+
} from 'aws-cdk-lib/aws-lambda';
|
|
448
|
+
import { Duration } from 'aws-cdk-lib';
|
|
449
|
+
import { CorsHttpMethod } from 'aws-cdk-lib/aws-apigatewayv2';
|
|
450
|
+
import { HttpIamAuthorizer } from 'aws-cdk-lib/aws-apigatewayv2-authorizers';
|
|
451
|
+
import { HttpLambdaIntegration } from 'aws-cdk-lib/aws-apigatewayv2-integrations';
|
|
452
|
+
import {
|
|
453
|
+
HttpApiIntegration,
|
|
454
|
+
IntegrationBuilder,
|
|
455
|
+
} from '../../core/api/utils.js';
|
|
456
|
+
import { HttpApi } from '../../core/api/http-api.js';
|
|
457
|
+
import { Procedures, routerToOperations } from '../../core/api/trpc-utils.js';
|
|
458
|
+
import { AppRouter, appRouter } from ':proj/test-api-backend';
|
|
459
|
+
|
|
460
|
+
// String union type for all API operation names
|
|
461
|
+
type Operations = Procedures<AppRouter>;
|
|
462
|
+
|
|
463
|
+
/**
|
|
464
|
+
* Properties for creating a TestApi construct
|
|
465
|
+
*
|
|
466
|
+
* @template TIntegrations - Map of operation names to their integrations
|
|
467
|
+
*/
|
|
468
|
+
export interface TestApiProps<
|
|
469
|
+
TIntegrations extends Record<Operations, HttpApiIntegration>,
|
|
470
|
+
> {
|
|
471
|
+
/**
|
|
472
|
+
* Map of operation names to their API Gateway integrations
|
|
473
|
+
*/
|
|
474
|
+
integrations: TIntegrations;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
/**
|
|
478
|
+
* A CDK construct that creates and configures an AWS API Gateway HTTP API
|
|
479
|
+
* specifically for TestApi.
|
|
480
|
+
* @template TIntegrations - Map of operation names to their integrations
|
|
481
|
+
*/
|
|
482
|
+
export class TestApi<
|
|
483
|
+
TIntegrations extends Record<Operations, HttpApiIntegration>,
|
|
484
|
+
> extends HttpApi<Operations, TIntegrations> {
|
|
485
|
+
/**
|
|
486
|
+
* Creates default integrations for all operations, which implement each operation as
|
|
487
|
+
* its own individual lambda function.
|
|
488
|
+
*
|
|
489
|
+
* @param scope - The CDK construct scope
|
|
490
|
+
* @returns An IntegrationBuilder with default lambda integrations
|
|
491
|
+
*/
|
|
492
|
+
public static defaultIntegrations = (scope: Construct) => {
|
|
493
|
+
return IntegrationBuilder.http({
|
|
494
|
+
operations: routerToOperations(appRouter),
|
|
495
|
+
defaultIntegrationOptions: {
|
|
496
|
+
runtime: Runtime.NODEJS_LATEST,
|
|
497
|
+
handler: 'index.handler',
|
|
498
|
+
code: Code.fromAsset(
|
|
499
|
+
url.fileURLToPath(
|
|
500
|
+
new URL(
|
|
501
|
+
'../../../../../../dist/apps/test-api/backend/bundle',
|
|
502
|
+
import.meta.url,
|
|
503
|
+
),
|
|
504
|
+
),
|
|
505
|
+
),
|
|
506
|
+
timeout: Duration.seconds(30),
|
|
507
|
+
tracing: Tracing.ACTIVE,
|
|
508
|
+
environment: {
|
|
509
|
+
AWS_CONNECTION_REUSE_ENABLED: '1',
|
|
510
|
+
},
|
|
511
|
+
} satisfies FunctionProps,
|
|
512
|
+
buildDefaultIntegration: (op, props: FunctionProps) => {
|
|
513
|
+
const handler = new Function(scope, \`TestApi\${op}Handler\`, props);
|
|
514
|
+
return {
|
|
515
|
+
handler,
|
|
516
|
+
integration: new HttpLambdaIntegration(
|
|
517
|
+
\`TestApi\${op}Integration\`,
|
|
518
|
+
handler,
|
|
519
|
+
),
|
|
520
|
+
};
|
|
374
521
|
},
|
|
375
522
|
});
|
|
523
|
+
};
|
|
376
524
|
|
|
377
|
-
|
|
525
|
+
constructor(
|
|
526
|
+
scope: Construct,
|
|
527
|
+
id: string,
|
|
528
|
+
props: TestApiProps<TIntegrations>,
|
|
529
|
+
) {
|
|
530
|
+
super(scope, id, {
|
|
531
|
+
apiName: 'TestApi',
|
|
378
532
|
corsPreflight: {
|
|
379
|
-
allowOrigins:
|
|
533
|
+
allowOrigins: ['*'],
|
|
380
534
|
allowMethods: [CorsHttpMethod.ANY],
|
|
381
535
|
allowHeaders: [
|
|
382
536
|
'authorization',
|
|
@@ -386,33 +540,450 @@ export class HttpApi extends Construct {
|
|
|
386
540
|
'x-amz-security-token',
|
|
387
541
|
],
|
|
388
542
|
},
|
|
389
|
-
defaultAuthorizer:
|
|
543
|
+
defaultAuthorizer: new HttpIamAuthorizer(),
|
|
544
|
+
operations: routerToOperations(appRouter),
|
|
545
|
+
...props,
|
|
390
546
|
});
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
"
|
|
550
|
+
`;
|
|
391
551
|
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
552
|
+
exports[`trpc backend generator > should set up shared constructs for http > trpc-utils.ts 1`] = `
|
|
553
|
+
"import { TRPCRouterRecord, AnyTRPCRouter } from '@trpc/server';
|
|
554
|
+
import { OperationDetails } from './utils.js';
|
|
555
|
+
import { HttpMethod } from 'aws-cdk-lib/aws-apigatewayv2';
|
|
556
|
+
|
|
557
|
+
/**
|
|
558
|
+
* Helper type that recursively extracts procedure names from a tRPC router.
|
|
559
|
+
* This type traverses the router structure and builds fully qualified procedure names
|
|
560
|
+
* with dot notation for nested routers.
|
|
561
|
+
*
|
|
562
|
+
* @template T - The tRPC router record type
|
|
563
|
+
* @template Prefix - The current path prefix for nested procedures
|
|
564
|
+
*/
|
|
565
|
+
type _Procedures<T extends TRPCRouterRecord, Prefix extends string = ''> = {
|
|
566
|
+
[K in keyof T]: K extends string
|
|
567
|
+
? T[K] extends TRPCRouterRecord
|
|
568
|
+
? _Procedures<T[K], \`\${Prefix}\${K}.\`>
|
|
569
|
+
: \`\${Prefix}\${K}\`
|
|
570
|
+
: never;
|
|
571
|
+
}[keyof T];
|
|
572
|
+
|
|
573
|
+
/**
|
|
574
|
+
* Extracts all procedure names from a tRPC router as a union of string literals.
|
|
575
|
+
* This type is used to provide type-safe access to procedure names throughout the API.
|
|
576
|
+
*
|
|
577
|
+
* @template TRouter - The tRPC router type
|
|
578
|
+
*/
|
|
579
|
+
export type Procedures<TRouter extends AnyTRPCRouter> = _Procedures<
|
|
580
|
+
TRouter['_def']['record']
|
|
581
|
+
>;
|
|
582
|
+
|
|
583
|
+
/**
|
|
584
|
+
* Converts a tRPC router to a map of API operations.
|
|
585
|
+
* This method recursively traverses the router structure and creates operation details
|
|
586
|
+
* for each procedure, mapping queries to GET methods and mutations to POST methods.
|
|
587
|
+
*
|
|
588
|
+
* @param router - The tRPC router to convert
|
|
589
|
+
* @param prefix - The current path prefix for nested procedures
|
|
590
|
+
* @returns A map of procedure names to their API operation details
|
|
591
|
+
*/
|
|
592
|
+
export const routerToOperations = <TRouter extends AnyTRPCRouter>(
|
|
593
|
+
router: TRouter,
|
|
594
|
+
prefix = '',
|
|
595
|
+
): Record<Procedures<TRouter>, OperationDetails> => {
|
|
596
|
+
return Object.fromEntries(
|
|
597
|
+
Object.entries(router._def.procedures).flatMap(
|
|
598
|
+
([op, procedureOrRouter]: [string, any]) => {
|
|
599
|
+
const fullPath = prefix ? \`\${prefix}.\${op}\` : op;
|
|
600
|
+
return procedureOrRouter._def?.router
|
|
601
|
+
? Object.entries(
|
|
602
|
+
routerToOperations<TRouter>(procedureOrRouter, fullPath),
|
|
603
|
+
)
|
|
604
|
+
: [
|
|
605
|
+
[
|
|
606
|
+
fullPath,
|
|
607
|
+
{
|
|
608
|
+
path: fullPath,
|
|
609
|
+
method:
|
|
610
|
+
procedureOrRouter._def.type === 'query'
|
|
611
|
+
? HttpMethod.GET
|
|
612
|
+
: HttpMethod.POST,
|
|
613
|
+
},
|
|
614
|
+
],
|
|
615
|
+
];
|
|
616
|
+
},
|
|
617
|
+
),
|
|
618
|
+
) as Record<Procedures<TRouter>, OperationDetails>;
|
|
619
|
+
};
|
|
620
|
+
"
|
|
621
|
+
`;
|
|
622
|
+
|
|
623
|
+
exports[`trpc backend generator > should set up shared constructs for http > utils.ts 1`] = `
|
|
624
|
+
"import { Integration, MethodOptions } from 'aws-cdk-lib/aws-apigateway';
|
|
625
|
+
import {
|
|
626
|
+
HttpRouteIntegration,
|
|
627
|
+
AddRoutesOptions,
|
|
628
|
+
} from 'aws-cdk-lib/aws-apigatewayv2';
|
|
629
|
+
|
|
630
|
+
/**
|
|
631
|
+
* Type representing applicable HTTP Methods in API Gateway
|
|
632
|
+
*/
|
|
633
|
+
export type HttpMethod =
|
|
634
|
+
| 'ANY'
|
|
635
|
+
| 'DELETE'
|
|
636
|
+
| 'GET'
|
|
637
|
+
| 'HEAD'
|
|
638
|
+
| 'OPTIONS'
|
|
639
|
+
| 'PATCH'
|
|
640
|
+
| 'POST'
|
|
641
|
+
| 'PUT';
|
|
642
|
+
|
|
643
|
+
/**
|
|
644
|
+
* Defines the details of an API operation.
|
|
645
|
+
*/
|
|
646
|
+
export interface OperationDetails {
|
|
647
|
+
/**
|
|
648
|
+
* The URL path for the operation
|
|
649
|
+
*/
|
|
650
|
+
path: string;
|
|
651
|
+
|
|
652
|
+
/**
|
|
653
|
+
* The HTTP method for the operation
|
|
654
|
+
*/
|
|
655
|
+
method: HttpMethod;
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
/**
|
|
659
|
+
* Represents an API Gateway REST API integration that can be attached to API methods.
|
|
660
|
+
*/
|
|
661
|
+
export interface RestApiIntegration {
|
|
662
|
+
integration: Integration;
|
|
663
|
+
options?: MethodOptions;
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
/**
|
|
667
|
+
* Represents an API Gateway HTTP API that can be attached to API methods.
|
|
668
|
+
*/
|
|
669
|
+
export interface HttpApiIntegration {
|
|
670
|
+
integration: HttpRouteIntegration;
|
|
671
|
+
options?: Omit<AddRoutesOptions, 'path' | 'methods' | 'integration'>;
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
/**
|
|
675
|
+
* Options for constructing an IntegrationBuilder
|
|
676
|
+
*/
|
|
677
|
+
export interface IntegrationBuilderProps<
|
|
678
|
+
TOperation extends string,
|
|
679
|
+
TBaseIntegration,
|
|
680
|
+
TDefaultIntegrationProps extends object,
|
|
681
|
+
TDefaultIntegration extends TBaseIntegration,
|
|
682
|
+
> {
|
|
683
|
+
/** Map of operation names to their API path and HTTP method details */
|
|
684
|
+
operations: Record<TOperation, OperationDetails>;
|
|
685
|
+
|
|
686
|
+
/** Default configuration options for integrations */
|
|
687
|
+
defaultIntegrationOptions: TDefaultIntegrationProps;
|
|
688
|
+
|
|
689
|
+
/** Function to create a default integration for an operation */
|
|
690
|
+
buildDefaultIntegration: (
|
|
691
|
+
op: TOperation,
|
|
692
|
+
props: TDefaultIntegrationProps,
|
|
693
|
+
) => TDefaultIntegration;
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
/**
|
|
697
|
+
* A builder class for creating API integrations with flexible configuration options.
|
|
698
|
+
*
|
|
699
|
+
* This class implements the builder pattern to create a set of API integrations
|
|
700
|
+
* with support for default configurations and selective overrides.
|
|
701
|
+
*
|
|
702
|
+
* @template TOperation - String literal type representing operation names
|
|
703
|
+
* @template TBaseIntegration - Base type for all integrations
|
|
704
|
+
* @template TIntegrations - Record mapping operation names to their integrations
|
|
705
|
+
* @template TDefaultIntegrationProps - Type for default integration properties
|
|
706
|
+
* @template TDefaultIntegration - Type for default integration implementation
|
|
707
|
+
*/
|
|
708
|
+
export class IntegrationBuilder<
|
|
709
|
+
TOperation extends string,
|
|
710
|
+
TBaseIntegration,
|
|
711
|
+
TIntegrations extends Record<TOperation, TBaseIntegration>,
|
|
712
|
+
TDefaultIntegrationProps extends object,
|
|
713
|
+
TDefaultIntegration extends TBaseIntegration,
|
|
714
|
+
> {
|
|
715
|
+
/** Options for the integration builder */
|
|
716
|
+
private options: IntegrationBuilderProps<
|
|
717
|
+
TOperation,
|
|
718
|
+
TBaseIntegration,
|
|
719
|
+
TDefaultIntegrationProps,
|
|
720
|
+
TDefaultIntegration
|
|
721
|
+
>;
|
|
722
|
+
|
|
723
|
+
/** Map of operation names to their custom integrations */
|
|
724
|
+
private integrations: Partial<TIntegrations> = {};
|
|
725
|
+
|
|
726
|
+
/**
|
|
727
|
+
* Create an Integration Builder for an HTTP API
|
|
728
|
+
*/
|
|
729
|
+
public static http = <
|
|
730
|
+
TOperation extends string,
|
|
731
|
+
TIntegrations extends Record<TOperation, TDefaultIntegration>,
|
|
732
|
+
TDefaultIntegrationProps extends object,
|
|
733
|
+
TDefaultIntegration extends HttpApiIntegration,
|
|
734
|
+
>(
|
|
735
|
+
options: IntegrationBuilderProps<
|
|
736
|
+
TOperation,
|
|
737
|
+
HttpApiIntegration,
|
|
738
|
+
TDefaultIntegrationProps,
|
|
739
|
+
TDefaultIntegration
|
|
740
|
+
>,
|
|
741
|
+
) => {
|
|
742
|
+
return new IntegrationBuilder<
|
|
743
|
+
TOperation,
|
|
744
|
+
HttpApiIntegration,
|
|
745
|
+
TIntegrations,
|
|
746
|
+
TDefaultIntegrationProps,
|
|
747
|
+
TDefaultIntegration
|
|
748
|
+
>(options);
|
|
749
|
+
};
|
|
750
|
+
|
|
751
|
+
/**
|
|
752
|
+
* Create an Integration Builder for a REST API
|
|
753
|
+
*/
|
|
754
|
+
public static rest = <
|
|
755
|
+
TOperation extends string,
|
|
756
|
+
TIntegrations extends Record<TOperation, TDefaultIntegration>,
|
|
757
|
+
TDefaultIntegrationProps extends object,
|
|
758
|
+
TDefaultIntegration extends RestApiIntegration,
|
|
759
|
+
>(
|
|
760
|
+
options: IntegrationBuilderProps<
|
|
761
|
+
TOperation,
|
|
762
|
+
RestApiIntegration,
|
|
763
|
+
TDefaultIntegrationProps,
|
|
764
|
+
TDefaultIntegration
|
|
765
|
+
>,
|
|
766
|
+
) => {
|
|
767
|
+
return new IntegrationBuilder<
|
|
768
|
+
TOperation,
|
|
769
|
+
RestApiIntegration,
|
|
770
|
+
TIntegrations,
|
|
771
|
+
TDefaultIntegrationProps,
|
|
772
|
+
TDefaultIntegration
|
|
773
|
+
>(options);
|
|
774
|
+
};
|
|
775
|
+
|
|
776
|
+
private constructor(
|
|
777
|
+
options: IntegrationBuilderProps<
|
|
778
|
+
TOperation,
|
|
779
|
+
TBaseIntegration,
|
|
780
|
+
TDefaultIntegrationProps,
|
|
781
|
+
TDefaultIntegration
|
|
782
|
+
>,
|
|
783
|
+
) {
|
|
784
|
+
this.options = options;
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
/**
|
|
788
|
+
* Overrides default integrations with custom implementations for specific operations.
|
|
789
|
+
*
|
|
790
|
+
* @param overrides - Map of operation names to their custom integration implementations
|
|
791
|
+
* @returns The builder instance with updated type information reflecting the overrides
|
|
792
|
+
*/
|
|
793
|
+
public withOverrides<
|
|
794
|
+
TOverrideIntegrations extends Partial<Record<TOperation, TBaseIntegration>>,
|
|
795
|
+
>(overrides: TOverrideIntegrations) {
|
|
796
|
+
this.integrations = { ...this.integrations, ...overrides };
|
|
797
|
+
// Re-type to include the overridden integration types
|
|
798
|
+
return this as unknown as IntegrationBuilder<
|
|
799
|
+
TOperation,
|
|
800
|
+
TBaseIntegration,
|
|
801
|
+
Omit<TIntegrations, keyof TOverrideIntegrations> & TOverrideIntegrations,
|
|
802
|
+
TDefaultIntegrationProps,
|
|
803
|
+
TDefaultIntegration
|
|
804
|
+
>;
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
/**
|
|
808
|
+
* Updates the default integration options that will be used for operations
|
|
809
|
+
* without custom overrides.
|
|
810
|
+
*
|
|
811
|
+
* @param options - Partial default integration options to merge with existing defaults
|
|
812
|
+
* @returns The builder instance
|
|
813
|
+
*/
|
|
814
|
+
public withDefaultOptions(options: Partial<TDefaultIntegrationProps>) {
|
|
815
|
+
this.options.defaultIntegrationOptions = {
|
|
816
|
+
...this.options.defaultIntegrationOptions,
|
|
817
|
+
...options,
|
|
818
|
+
};
|
|
819
|
+
return this;
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
/**
|
|
823
|
+
* Builds and returns the complete set of integrations.
|
|
824
|
+
*
|
|
825
|
+
* This method creates the final integration map by:
|
|
826
|
+
* 1. Including all custom overrides provided via withOverrides()
|
|
827
|
+
* 2. Creating default integrations for any operations without custom overrides
|
|
828
|
+
*
|
|
829
|
+
* @returns A complete map of operation names to their integrations
|
|
830
|
+
*/
|
|
831
|
+
public build(): TIntegrations {
|
|
832
|
+
return {
|
|
833
|
+
...this.integrations,
|
|
834
|
+
...Object.fromEntries(
|
|
835
|
+
(Object.keys(this.options.operations) as TOperation[])
|
|
836
|
+
.filter(
|
|
837
|
+
(op) => !this.integrations[op as keyof typeof this.integrations],
|
|
838
|
+
)
|
|
839
|
+
.map((op) => [
|
|
840
|
+
op,
|
|
841
|
+
this.options.buildDefaultIntegration(
|
|
842
|
+
op,
|
|
843
|
+
this.options.defaultIntegrationOptions,
|
|
844
|
+
),
|
|
845
|
+
]),
|
|
405
846
|
),
|
|
847
|
+
} as unknown as TIntegrations;
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
"
|
|
851
|
+
`;
|
|
852
|
+
|
|
853
|
+
exports[`trpc backend generator > should set up shared constructs for rest > rest-api.ts 1`] = `
|
|
854
|
+
"import { Construct } from 'constructs';
|
|
855
|
+
import {
|
|
856
|
+
RestApi as _RestApi,
|
|
857
|
+
RestApiProps as _RestApiProps,
|
|
858
|
+
AccessLogFormat,
|
|
859
|
+
IResource,
|
|
860
|
+
LogGroupLogDestination,
|
|
861
|
+
MethodLoggingLevel,
|
|
862
|
+
} from 'aws-cdk-lib/aws-apigateway';
|
|
863
|
+
import { LogGroup } from 'aws-cdk-lib/aws-logs';
|
|
864
|
+
import { RuntimeConfig } from '../runtime-config.js';
|
|
865
|
+
import { Grant, IGrantable } from 'aws-cdk-lib/aws-iam';
|
|
866
|
+
import { OperationDetails, RestApiIntegration } from './utils.js';
|
|
867
|
+
|
|
868
|
+
/**
|
|
869
|
+
* Properties for creating a RestApi construct.
|
|
870
|
+
*
|
|
871
|
+
* @template TIntegrations - Record mapping operation names to their integrations
|
|
872
|
+
* @template TOperation - String literal type representing operation names
|
|
873
|
+
*/
|
|
874
|
+
export interface RestApiProps<
|
|
875
|
+
TIntegrations extends Record<TOperation, RestApiIntegration>,
|
|
876
|
+
TOperation extends string,
|
|
877
|
+
> extends _RestApiProps {
|
|
878
|
+
/**
|
|
879
|
+
* Unique name for the API, used in runtime configuration
|
|
880
|
+
*/
|
|
881
|
+
readonly apiName: string;
|
|
882
|
+
/**
|
|
883
|
+
* Map of operation names to their API path and HTTP method details
|
|
884
|
+
*/
|
|
885
|
+
readonly operations: Record<TOperation, OperationDetails>;
|
|
886
|
+
/**
|
|
887
|
+
* Map of operation names to their API Gateway integrations
|
|
888
|
+
*/
|
|
889
|
+
readonly integrations: TIntegrations;
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
/**
|
|
893
|
+
* A CDK construct that creates and configures an AWS API Gateway REST API.
|
|
894
|
+
*
|
|
895
|
+
* This class extends the base CDK RestApi with additional functionality:
|
|
896
|
+
* - Type-safe operation and integration management
|
|
897
|
+
* - Automatic resource creation based on path patterns
|
|
898
|
+
* - Integration with runtime configuration for client discovery
|
|
899
|
+
*
|
|
900
|
+
* @template TOperation - String literal type representing operation names
|
|
901
|
+
* @template TIntegrations - Record mapping operation names to their integrations
|
|
902
|
+
*/
|
|
903
|
+
export class RestApi<
|
|
904
|
+
TOperation extends string,
|
|
905
|
+
TIntegrations extends Record<TOperation, RestApiIntegration>,
|
|
906
|
+
> extends Construct {
|
|
907
|
+
/** The underlying CDK RestApi instance */
|
|
908
|
+
public readonly api: _RestApi;
|
|
909
|
+
|
|
910
|
+
/** Map of operation names to their API Gateway integrations */
|
|
911
|
+
public readonly integrations: TIntegrations;
|
|
912
|
+
|
|
913
|
+
constructor(
|
|
914
|
+
scope: Construct,
|
|
915
|
+
id: string,
|
|
916
|
+
{
|
|
917
|
+
apiName,
|
|
918
|
+
operations,
|
|
919
|
+
integrations,
|
|
920
|
+
...props
|
|
921
|
+
}: RestApiProps<TIntegrations, TOperation>,
|
|
922
|
+
) {
|
|
923
|
+
super(scope, id);
|
|
924
|
+
this.integrations = integrations;
|
|
925
|
+
|
|
926
|
+
// Create the API Gateway REST API with logging enabled
|
|
927
|
+
this.api = new _RestApi(this, 'Api', {
|
|
928
|
+
deployOptions: {
|
|
929
|
+
accessLogDestination: new LogGroupLogDestination(
|
|
930
|
+
new LogGroup(this, 'AccessLogs'),
|
|
931
|
+
),
|
|
932
|
+
accessLogFormat: AccessLogFormat.clf(),
|
|
933
|
+
loggingLevel: MethodLoggingLevel.INFO,
|
|
934
|
+
},
|
|
935
|
+
...props,
|
|
406
936
|
});
|
|
407
937
|
|
|
408
|
-
|
|
938
|
+
// Create API resources and methods for each operation
|
|
939
|
+
(Object.entries(operations) as [TOperation, OperationDetails][]).map(
|
|
940
|
+
([op, details]) => {
|
|
941
|
+
const resource = this.getOrCreateResource(
|
|
942
|
+
this.api.root,
|
|
943
|
+
(details.path.startsWith('/')
|
|
944
|
+
? details.path.slice(1)
|
|
945
|
+
: details.path
|
|
946
|
+
).split('/'),
|
|
947
|
+
);
|
|
948
|
+
resource.addMethod(
|
|
949
|
+
details.method,
|
|
950
|
+
integrations[op].integration,
|
|
951
|
+
integrations[op].options,
|
|
952
|
+
);
|
|
953
|
+
},
|
|
954
|
+
);
|
|
409
955
|
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
956
|
+
// Register the API URL in runtime configuration for client discovery
|
|
957
|
+
RuntimeConfig.ensure(this).config.apis = {
|
|
958
|
+
...RuntimeConfig.ensure(this).config.apis!,
|
|
959
|
+
[apiName]: this.api.url!,
|
|
413
960
|
};
|
|
414
961
|
}
|
|
415
962
|
|
|
963
|
+
/**
|
|
964
|
+
* Recursively creates or retrieves API Gateway resources based on a path pattern.
|
|
965
|
+
*
|
|
966
|
+
* @param resource - The parent API Gateway resource
|
|
967
|
+
* @param pathParts - Array of path segments to create or retrieve
|
|
968
|
+
* @returns The API Gateway resource at the end of the path
|
|
969
|
+
*/
|
|
970
|
+
private getOrCreateResource(
|
|
971
|
+
resource: IResource,
|
|
972
|
+
[nextPathPart, ...pathParts]: string[],
|
|
973
|
+
): IResource {
|
|
974
|
+
if (!nextPathPart) {
|
|
975
|
+
return resource;
|
|
976
|
+
}
|
|
977
|
+
const childResource =
|
|
978
|
+
resource.getResource(nextPathPart) ?? resource.addResource(nextPathPart);
|
|
979
|
+
return this.getOrCreateResource(childResource, pathParts);
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
/**
|
|
983
|
+
* Grants IAM permissions to invoke any method on this API.
|
|
984
|
+
*
|
|
985
|
+
* @param grantee - The IAM principal to grant permissions to
|
|
986
|
+
*/
|
|
416
987
|
public grantInvokeAccess(grantee: IGrantable) {
|
|
417
988
|
Grant.addToPrincipal({
|
|
418
989
|
grantee,
|
|
@@ -424,33 +995,435 @@ export class HttpApi extends Construct {
|
|
|
424
995
|
"
|
|
425
996
|
`;
|
|
426
997
|
|
|
427
|
-
exports[`trpc backend generator > should set up shared constructs >
|
|
428
|
-
"export * from './test-api.js';
|
|
429
|
-
"
|
|
430
|
-
`;
|
|
431
|
-
|
|
432
|
-
exports[`trpc backend generator > should set up shared constructs > test-api.ts 1`] = `
|
|
998
|
+
exports[`trpc backend generator > should set up shared constructs for rest > test-api.ts 1`] = `
|
|
433
999
|
"import { Construct } from 'constructs';
|
|
434
1000
|
import * as url from 'url';
|
|
435
|
-
import {
|
|
436
|
-
|
|
437
|
-
|
|
1001
|
+
import {
|
|
1002
|
+
Code,
|
|
1003
|
+
Runtime,
|
|
1004
|
+
Function,
|
|
1005
|
+
FunctionProps,
|
|
1006
|
+
Tracing,
|
|
1007
|
+
} from 'aws-cdk-lib/aws-lambda';
|
|
1008
|
+
import {
|
|
1009
|
+
AuthorizationType,
|
|
1010
|
+
Cors,
|
|
1011
|
+
LambdaIntegration,
|
|
1012
|
+
} from 'aws-cdk-lib/aws-apigateway';
|
|
1013
|
+
import { Duration, Stack } from 'aws-cdk-lib';
|
|
1014
|
+
import {
|
|
1015
|
+
PolicyDocument,
|
|
1016
|
+
PolicyStatement,
|
|
1017
|
+
Effect,
|
|
1018
|
+
AccountPrincipal,
|
|
1019
|
+
AnyPrincipal,
|
|
1020
|
+
} from 'aws-cdk-lib/aws-iam';
|
|
1021
|
+
import {
|
|
1022
|
+
IntegrationBuilder,
|
|
1023
|
+
RestApiIntegration,
|
|
1024
|
+
} from '../../core/api/utils.js';
|
|
1025
|
+
import { RestApi } from '../../core/api/rest-api.js';
|
|
1026
|
+
import { Procedures, routerToOperations } from '../../core/api/trpc-utils.js';
|
|
1027
|
+
import { AppRouter, appRouter } from ':proj/test-api-backend';
|
|
1028
|
+
|
|
1029
|
+
// String union type for all API operation names
|
|
1030
|
+
type Operations = Procedures<AppRouter>;
|
|
1031
|
+
|
|
1032
|
+
/**
|
|
1033
|
+
* Properties for creating a TestApi construct
|
|
1034
|
+
*
|
|
1035
|
+
* @template TIntegrations - Map of operation names to their integrations
|
|
1036
|
+
*/
|
|
1037
|
+
export interface TestApiProps<
|
|
1038
|
+
TIntegrations extends Record<Operations, RestApiIntegration>,
|
|
1039
|
+
> {
|
|
1040
|
+
/**
|
|
1041
|
+
* Map of operation names to their API Gateway integrations
|
|
1042
|
+
*/
|
|
1043
|
+
integrations: TIntegrations;
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
/**
|
|
1047
|
+
* A CDK construct that creates and configures an AWS API Gateway REST API
|
|
1048
|
+
* specifically for TestApi.
|
|
1049
|
+
* @template TIntegrations - Map of operation names to their integrations
|
|
1050
|
+
*/
|
|
1051
|
+
export class TestApi<
|
|
1052
|
+
TIntegrations extends Record<Operations, RestApiIntegration>,
|
|
1053
|
+
> extends RestApi<Operations, TIntegrations> {
|
|
1054
|
+
/**
|
|
1055
|
+
* Creates default integrations for all operations, which implement each operation as
|
|
1056
|
+
* its own individual lambda function.
|
|
1057
|
+
*
|
|
1058
|
+
* @param scope - The CDK construct scope
|
|
1059
|
+
* @returns An IntegrationBuilder with default lambda integrations
|
|
1060
|
+
*/
|
|
1061
|
+
public static defaultIntegrations = (scope: Construct) => {
|
|
1062
|
+
return IntegrationBuilder.rest({
|
|
1063
|
+
operations: routerToOperations(appRouter),
|
|
1064
|
+
defaultIntegrationOptions: {
|
|
1065
|
+
runtime: Runtime.NODEJS_LATEST,
|
|
1066
|
+
handler: 'index.handler',
|
|
1067
|
+
code: Code.fromAsset(
|
|
1068
|
+
url.fileURLToPath(
|
|
1069
|
+
new URL(
|
|
1070
|
+
'../../../../../../dist/apps/test-api/backend/bundle',
|
|
1071
|
+
import.meta.url,
|
|
1072
|
+
),
|
|
1073
|
+
),
|
|
1074
|
+
),
|
|
1075
|
+
timeout: Duration.seconds(30),
|
|
1076
|
+
tracing: Tracing.ACTIVE,
|
|
1077
|
+
environment: {
|
|
1078
|
+
AWS_CONNECTION_REUSE_ENABLED: '1',
|
|
1079
|
+
},
|
|
1080
|
+
} satisfies FunctionProps,
|
|
1081
|
+
buildDefaultIntegration: (op, props: FunctionProps) => {
|
|
1082
|
+
const handler = new Function(scope, \`TestApi\${op}Handler\`, props);
|
|
1083
|
+
return { handler, integration: new LambdaIntegration(handler) };
|
|
1084
|
+
},
|
|
1085
|
+
});
|
|
1086
|
+
};
|
|
438
1087
|
|
|
439
|
-
|
|
440
|
-
|
|
1088
|
+
constructor(
|
|
1089
|
+
scope: Construct,
|
|
1090
|
+
id: string,
|
|
1091
|
+
props: TestApiProps<TIntegrations>,
|
|
1092
|
+
) {
|
|
441
1093
|
super(scope, id, {
|
|
442
|
-
defaultAuthorizer: new HttpIamAuthorizer(),
|
|
443
1094
|
apiName: 'TestApi',
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
1095
|
+
defaultMethodOptions: {
|
|
1096
|
+
authorizationType: AuthorizationType.IAM,
|
|
1097
|
+
},
|
|
1098
|
+
defaultCorsPreflightOptions: {
|
|
1099
|
+
allowOrigins: Cors.ALL_ORIGINS,
|
|
1100
|
+
allowMethods: Cors.ALL_METHODS,
|
|
1101
|
+
},
|
|
1102
|
+
policy: new PolicyDocument({
|
|
1103
|
+
statements: [
|
|
1104
|
+
// Here we grant any AWS credentials from the account that the project is deployed in to call the api.
|
|
1105
|
+
// Machine to machine fine-grained access can be defined here using more specific principals (eg roles or
|
|
1106
|
+
// users) and resources (eg which api paths may be invoked by which principal) if required.
|
|
1107
|
+
new PolicyStatement({
|
|
1108
|
+
effect: Effect.ALLOW,
|
|
1109
|
+
principals: [new AccountPrincipal(Stack.of(scope).account)],
|
|
1110
|
+
actions: ['execute-api:Invoke'],
|
|
1111
|
+
resources: ['execute-api:/*'],
|
|
1112
|
+
}),
|
|
1113
|
+
// Open up OPTIONS to allow browsers to make unauthenticated preflight requests
|
|
1114
|
+
new PolicyStatement({
|
|
1115
|
+
effect: Effect.ALLOW,
|
|
1116
|
+
principals: [new AnyPrincipal()],
|
|
1117
|
+
actions: ['execute-api:Invoke'],
|
|
1118
|
+
resources: ['execute-api:/*/OPTIONS/*'],
|
|
1119
|
+
}),
|
|
1120
|
+
],
|
|
1121
|
+
}),
|
|
1122
|
+
operations: routerToOperations(appRouter),
|
|
1123
|
+
...props,
|
|
452
1124
|
});
|
|
453
1125
|
}
|
|
454
1126
|
}
|
|
455
1127
|
"
|
|
456
1128
|
`;
|
|
1129
|
+
|
|
1130
|
+
exports[`trpc backend generator > should set up shared constructs for rest > trpc-utils.ts 1`] = `
|
|
1131
|
+
"import { TRPCRouterRecord, AnyTRPCRouter } from '@trpc/server';
|
|
1132
|
+
import { OperationDetails } from './utils.js';
|
|
1133
|
+
import { HttpMethod } from 'aws-cdk-lib/aws-apigatewayv2';
|
|
1134
|
+
|
|
1135
|
+
/**
|
|
1136
|
+
* Helper type that recursively extracts procedure names from a tRPC router.
|
|
1137
|
+
* This type traverses the router structure and builds fully qualified procedure names
|
|
1138
|
+
* with dot notation for nested routers.
|
|
1139
|
+
*
|
|
1140
|
+
* @template T - The tRPC router record type
|
|
1141
|
+
* @template Prefix - The current path prefix for nested procedures
|
|
1142
|
+
*/
|
|
1143
|
+
type _Procedures<T extends TRPCRouterRecord, Prefix extends string = ''> = {
|
|
1144
|
+
[K in keyof T]: K extends string
|
|
1145
|
+
? T[K] extends TRPCRouterRecord
|
|
1146
|
+
? _Procedures<T[K], \`\${Prefix}\${K}.\`>
|
|
1147
|
+
: \`\${Prefix}\${K}\`
|
|
1148
|
+
: never;
|
|
1149
|
+
}[keyof T];
|
|
1150
|
+
|
|
1151
|
+
/**
|
|
1152
|
+
* Extracts all procedure names from a tRPC router as a union of string literals.
|
|
1153
|
+
* This type is used to provide type-safe access to procedure names throughout the API.
|
|
1154
|
+
*
|
|
1155
|
+
* @template TRouter - The tRPC router type
|
|
1156
|
+
*/
|
|
1157
|
+
export type Procedures<TRouter extends AnyTRPCRouter> = _Procedures<
|
|
1158
|
+
TRouter['_def']['record']
|
|
1159
|
+
>;
|
|
1160
|
+
|
|
1161
|
+
/**
|
|
1162
|
+
* Converts a tRPC router to a map of API operations.
|
|
1163
|
+
* This method recursively traverses the router structure and creates operation details
|
|
1164
|
+
* for each procedure, mapping queries to GET methods and mutations to POST methods.
|
|
1165
|
+
*
|
|
1166
|
+
* @param router - The tRPC router to convert
|
|
1167
|
+
* @param prefix - The current path prefix for nested procedures
|
|
1168
|
+
* @returns A map of procedure names to their API operation details
|
|
1169
|
+
*/
|
|
1170
|
+
export const routerToOperations = <TRouter extends AnyTRPCRouter>(
|
|
1171
|
+
router: TRouter,
|
|
1172
|
+
prefix = '',
|
|
1173
|
+
): Record<Procedures<TRouter>, OperationDetails> => {
|
|
1174
|
+
return Object.fromEntries(
|
|
1175
|
+
Object.entries(router._def.procedures).flatMap(
|
|
1176
|
+
([op, procedureOrRouter]: [string, any]) => {
|
|
1177
|
+
const fullPath = prefix ? \`\${prefix}.\${op}\` : op;
|
|
1178
|
+
return procedureOrRouter._def?.router
|
|
1179
|
+
? Object.entries(
|
|
1180
|
+
routerToOperations<TRouter>(procedureOrRouter, fullPath),
|
|
1181
|
+
)
|
|
1182
|
+
: [
|
|
1183
|
+
[
|
|
1184
|
+
fullPath,
|
|
1185
|
+
{
|
|
1186
|
+
path: fullPath,
|
|
1187
|
+
method:
|
|
1188
|
+
procedureOrRouter._def.type === 'query'
|
|
1189
|
+
? HttpMethod.GET
|
|
1190
|
+
: HttpMethod.POST,
|
|
1191
|
+
},
|
|
1192
|
+
],
|
|
1193
|
+
];
|
|
1194
|
+
},
|
|
1195
|
+
),
|
|
1196
|
+
) as Record<Procedures<TRouter>, OperationDetails>;
|
|
1197
|
+
};
|
|
1198
|
+
"
|
|
1199
|
+
`;
|
|
1200
|
+
|
|
1201
|
+
exports[`trpc backend generator > should set up shared constructs for rest > utils.ts 1`] = `
|
|
1202
|
+
"import { Integration, MethodOptions } from 'aws-cdk-lib/aws-apigateway';
|
|
1203
|
+
import {
|
|
1204
|
+
HttpRouteIntegration,
|
|
1205
|
+
AddRoutesOptions,
|
|
1206
|
+
} from 'aws-cdk-lib/aws-apigatewayv2';
|
|
1207
|
+
|
|
1208
|
+
/**
|
|
1209
|
+
* Type representing applicable HTTP Methods in API Gateway
|
|
1210
|
+
*/
|
|
1211
|
+
export type HttpMethod =
|
|
1212
|
+
| 'ANY'
|
|
1213
|
+
| 'DELETE'
|
|
1214
|
+
| 'GET'
|
|
1215
|
+
| 'HEAD'
|
|
1216
|
+
| 'OPTIONS'
|
|
1217
|
+
| 'PATCH'
|
|
1218
|
+
| 'POST'
|
|
1219
|
+
| 'PUT';
|
|
1220
|
+
|
|
1221
|
+
/**
|
|
1222
|
+
* Defines the details of an API operation.
|
|
1223
|
+
*/
|
|
1224
|
+
export interface OperationDetails {
|
|
1225
|
+
/**
|
|
1226
|
+
* The URL path for the operation
|
|
1227
|
+
*/
|
|
1228
|
+
path: string;
|
|
1229
|
+
|
|
1230
|
+
/**
|
|
1231
|
+
* The HTTP method for the operation
|
|
1232
|
+
*/
|
|
1233
|
+
method: HttpMethod;
|
|
1234
|
+
}
|
|
1235
|
+
|
|
1236
|
+
/**
|
|
1237
|
+
* Represents an API Gateway REST API integration that can be attached to API methods.
|
|
1238
|
+
*/
|
|
1239
|
+
export interface RestApiIntegration {
|
|
1240
|
+
integration: Integration;
|
|
1241
|
+
options?: MethodOptions;
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1244
|
+
/**
|
|
1245
|
+
* Represents an API Gateway HTTP API that can be attached to API methods.
|
|
1246
|
+
*/
|
|
1247
|
+
export interface HttpApiIntegration {
|
|
1248
|
+
integration: HttpRouteIntegration;
|
|
1249
|
+
options?: Omit<AddRoutesOptions, 'path' | 'methods' | 'integration'>;
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1252
|
+
/**
|
|
1253
|
+
* Options for constructing an IntegrationBuilder
|
|
1254
|
+
*/
|
|
1255
|
+
export interface IntegrationBuilderProps<
|
|
1256
|
+
TOperation extends string,
|
|
1257
|
+
TBaseIntegration,
|
|
1258
|
+
TDefaultIntegrationProps extends object,
|
|
1259
|
+
TDefaultIntegration extends TBaseIntegration,
|
|
1260
|
+
> {
|
|
1261
|
+
/** Map of operation names to their API path and HTTP method details */
|
|
1262
|
+
operations: Record<TOperation, OperationDetails>;
|
|
1263
|
+
|
|
1264
|
+
/** Default configuration options for integrations */
|
|
1265
|
+
defaultIntegrationOptions: TDefaultIntegrationProps;
|
|
1266
|
+
|
|
1267
|
+
/** Function to create a default integration for an operation */
|
|
1268
|
+
buildDefaultIntegration: (
|
|
1269
|
+
op: TOperation,
|
|
1270
|
+
props: TDefaultIntegrationProps,
|
|
1271
|
+
) => TDefaultIntegration;
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
/**
|
|
1275
|
+
* A builder class for creating API integrations with flexible configuration options.
|
|
1276
|
+
*
|
|
1277
|
+
* This class implements the builder pattern to create a set of API integrations
|
|
1278
|
+
* with support for default configurations and selective overrides.
|
|
1279
|
+
*
|
|
1280
|
+
* @template TOperation - String literal type representing operation names
|
|
1281
|
+
* @template TBaseIntegration - Base type for all integrations
|
|
1282
|
+
* @template TIntegrations - Record mapping operation names to their integrations
|
|
1283
|
+
* @template TDefaultIntegrationProps - Type for default integration properties
|
|
1284
|
+
* @template TDefaultIntegration - Type for default integration implementation
|
|
1285
|
+
*/
|
|
1286
|
+
export class IntegrationBuilder<
|
|
1287
|
+
TOperation extends string,
|
|
1288
|
+
TBaseIntegration,
|
|
1289
|
+
TIntegrations extends Record<TOperation, TBaseIntegration>,
|
|
1290
|
+
TDefaultIntegrationProps extends object,
|
|
1291
|
+
TDefaultIntegration extends TBaseIntegration,
|
|
1292
|
+
> {
|
|
1293
|
+
/** Options for the integration builder */
|
|
1294
|
+
private options: IntegrationBuilderProps<
|
|
1295
|
+
TOperation,
|
|
1296
|
+
TBaseIntegration,
|
|
1297
|
+
TDefaultIntegrationProps,
|
|
1298
|
+
TDefaultIntegration
|
|
1299
|
+
>;
|
|
1300
|
+
|
|
1301
|
+
/** Map of operation names to their custom integrations */
|
|
1302
|
+
private integrations: Partial<TIntegrations> = {};
|
|
1303
|
+
|
|
1304
|
+
/**
|
|
1305
|
+
* Create an Integration Builder for an HTTP API
|
|
1306
|
+
*/
|
|
1307
|
+
public static http = <
|
|
1308
|
+
TOperation extends string,
|
|
1309
|
+
TIntegrations extends Record<TOperation, TDefaultIntegration>,
|
|
1310
|
+
TDefaultIntegrationProps extends object,
|
|
1311
|
+
TDefaultIntegration extends HttpApiIntegration,
|
|
1312
|
+
>(
|
|
1313
|
+
options: IntegrationBuilderProps<
|
|
1314
|
+
TOperation,
|
|
1315
|
+
HttpApiIntegration,
|
|
1316
|
+
TDefaultIntegrationProps,
|
|
1317
|
+
TDefaultIntegration
|
|
1318
|
+
>,
|
|
1319
|
+
) => {
|
|
1320
|
+
return new IntegrationBuilder<
|
|
1321
|
+
TOperation,
|
|
1322
|
+
HttpApiIntegration,
|
|
1323
|
+
TIntegrations,
|
|
1324
|
+
TDefaultIntegrationProps,
|
|
1325
|
+
TDefaultIntegration
|
|
1326
|
+
>(options);
|
|
1327
|
+
};
|
|
1328
|
+
|
|
1329
|
+
/**
|
|
1330
|
+
* Create an Integration Builder for a REST API
|
|
1331
|
+
*/
|
|
1332
|
+
public static rest = <
|
|
1333
|
+
TOperation extends string,
|
|
1334
|
+
TIntegrations extends Record<TOperation, TDefaultIntegration>,
|
|
1335
|
+
TDefaultIntegrationProps extends object,
|
|
1336
|
+
TDefaultIntegration extends RestApiIntegration,
|
|
1337
|
+
>(
|
|
1338
|
+
options: IntegrationBuilderProps<
|
|
1339
|
+
TOperation,
|
|
1340
|
+
RestApiIntegration,
|
|
1341
|
+
TDefaultIntegrationProps,
|
|
1342
|
+
TDefaultIntegration
|
|
1343
|
+
>,
|
|
1344
|
+
) => {
|
|
1345
|
+
return new IntegrationBuilder<
|
|
1346
|
+
TOperation,
|
|
1347
|
+
RestApiIntegration,
|
|
1348
|
+
TIntegrations,
|
|
1349
|
+
TDefaultIntegrationProps,
|
|
1350
|
+
TDefaultIntegration
|
|
1351
|
+
>(options);
|
|
1352
|
+
};
|
|
1353
|
+
|
|
1354
|
+
private constructor(
|
|
1355
|
+
options: IntegrationBuilderProps<
|
|
1356
|
+
TOperation,
|
|
1357
|
+
TBaseIntegration,
|
|
1358
|
+
TDefaultIntegrationProps,
|
|
1359
|
+
TDefaultIntegration
|
|
1360
|
+
>,
|
|
1361
|
+
) {
|
|
1362
|
+
this.options = options;
|
|
1363
|
+
}
|
|
1364
|
+
|
|
1365
|
+
/**
|
|
1366
|
+
* Overrides default integrations with custom implementations for specific operations.
|
|
1367
|
+
*
|
|
1368
|
+
* @param overrides - Map of operation names to their custom integration implementations
|
|
1369
|
+
* @returns The builder instance with updated type information reflecting the overrides
|
|
1370
|
+
*/
|
|
1371
|
+
public withOverrides<
|
|
1372
|
+
TOverrideIntegrations extends Partial<Record<TOperation, TBaseIntegration>>,
|
|
1373
|
+
>(overrides: TOverrideIntegrations) {
|
|
1374
|
+
this.integrations = { ...this.integrations, ...overrides };
|
|
1375
|
+
// Re-type to include the overridden integration types
|
|
1376
|
+
return this as unknown as IntegrationBuilder<
|
|
1377
|
+
TOperation,
|
|
1378
|
+
TBaseIntegration,
|
|
1379
|
+
Omit<TIntegrations, keyof TOverrideIntegrations> & TOverrideIntegrations,
|
|
1380
|
+
TDefaultIntegrationProps,
|
|
1381
|
+
TDefaultIntegration
|
|
1382
|
+
>;
|
|
1383
|
+
}
|
|
1384
|
+
|
|
1385
|
+
/**
|
|
1386
|
+
* Updates the default integration options that will be used for operations
|
|
1387
|
+
* without custom overrides.
|
|
1388
|
+
*
|
|
1389
|
+
* @param options - Partial default integration options to merge with existing defaults
|
|
1390
|
+
* @returns The builder instance
|
|
1391
|
+
*/
|
|
1392
|
+
public withDefaultOptions(options: Partial<TDefaultIntegrationProps>) {
|
|
1393
|
+
this.options.defaultIntegrationOptions = {
|
|
1394
|
+
...this.options.defaultIntegrationOptions,
|
|
1395
|
+
...options,
|
|
1396
|
+
};
|
|
1397
|
+
return this;
|
|
1398
|
+
}
|
|
1399
|
+
|
|
1400
|
+
/**
|
|
1401
|
+
* Builds and returns the complete set of integrations.
|
|
1402
|
+
*
|
|
1403
|
+
* This method creates the final integration map by:
|
|
1404
|
+
* 1. Including all custom overrides provided via withOverrides()
|
|
1405
|
+
* 2. Creating default integrations for any operations without custom overrides
|
|
1406
|
+
*
|
|
1407
|
+
* @returns A complete map of operation names to their integrations
|
|
1408
|
+
*/
|
|
1409
|
+
public build(): TIntegrations {
|
|
1410
|
+
return {
|
|
1411
|
+
...this.integrations,
|
|
1412
|
+
...Object.fromEntries(
|
|
1413
|
+
(Object.keys(this.options.operations) as TOperation[])
|
|
1414
|
+
.filter(
|
|
1415
|
+
(op) => !this.integrations[op as keyof typeof this.integrations],
|
|
1416
|
+
)
|
|
1417
|
+
.map((op) => [
|
|
1418
|
+
op,
|
|
1419
|
+
this.options.buildDefaultIntegration(
|
|
1420
|
+
op,
|
|
1421
|
+
this.options.defaultIntegrationOptions,
|
|
1422
|
+
),
|
|
1423
|
+
]),
|
|
1424
|
+
),
|
|
1425
|
+
} as unknown as TIntegrations;
|
|
1426
|
+
}
|
|
1427
|
+
}
|
|
1428
|
+
"
|
|
1429
|
+
`;
|