@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
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { Construct } from 'constructs';
|
|
2
|
+
import * as url from 'url';
|
|
3
|
+
import {
|
|
4
|
+
Code,
|
|
5
|
+
Runtime,
|
|
6
|
+
Function,
|
|
7
|
+
FunctionProps,
|
|
8
|
+
Tracing,
|
|
9
|
+
} from 'aws-cdk-lib/aws-lambda';
|
|
10
|
+
import { Duration } from 'aws-cdk-lib';
|
|
11
|
+
import { CorsHttpMethod } from 'aws-cdk-lib/aws-apigatewayv2';
|
|
12
|
+
import { HttpIamAuthorizer } from 'aws-cdk-lib/aws-apigatewayv2-authorizers';
|
|
13
|
+
import { HttpLambdaIntegration } from 'aws-cdk-lib/aws-apigatewayv2-integrations';
|
|
14
|
+
import {
|
|
15
|
+
HttpApiIntegration,
|
|
16
|
+
IntegrationBuilder,
|
|
17
|
+
} from '../../core/api/utils.js';
|
|
18
|
+
import { HttpApi } from '../../core/api/http-api.js';
|
|
19
|
+
<%_ if (backend.type === 'trpc') { _%>
|
|
20
|
+
import { Procedures, routerToOperations } from '../../core/api/trpc-utils.js';
|
|
21
|
+
import { AppRouter, appRouter } from '<%= backend.projectAlias %>';
|
|
22
|
+
|
|
23
|
+
// String union type for all API operation names
|
|
24
|
+
type Operations = Procedures<AppRouter>;
|
|
25
|
+
<%_ } else if (backend.type === 'fastapi') { _%>
|
|
26
|
+
import {
|
|
27
|
+
OPERATION_DETAILS,
|
|
28
|
+
Operations,
|
|
29
|
+
} from '../../generated/<%- apiNameKebabCase %>/metadata.gen.js';
|
|
30
|
+
<%_ } _%>
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Properties for creating a <%= apiNameClassName %> construct
|
|
34
|
+
*
|
|
35
|
+
* @template TIntegrations - Map of operation names to their integrations
|
|
36
|
+
*/
|
|
37
|
+
export interface <%= apiNameClassName %>Props<
|
|
38
|
+
TIntegrations extends Record<Operations, HttpApiIntegration>,
|
|
39
|
+
> {
|
|
40
|
+
/**
|
|
41
|
+
* Map of operation names to their API Gateway integrations
|
|
42
|
+
*/
|
|
43
|
+
integrations: TIntegrations;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* A CDK construct that creates and configures an AWS API Gateway HTTP API
|
|
48
|
+
* specifically for <%= apiNameClassName %>.
|
|
49
|
+
* @template TIntegrations - Map of operation names to their integrations
|
|
50
|
+
*/
|
|
51
|
+
export class <%= apiNameClassName %><
|
|
52
|
+
TIntegrations extends Record<Operations, HttpApiIntegration>,
|
|
53
|
+
> extends HttpApi<Operations, TIntegrations> {
|
|
54
|
+
/**
|
|
55
|
+
* Creates default integrations for all operations, which implement each operation as
|
|
56
|
+
* its own individual lambda function.
|
|
57
|
+
*
|
|
58
|
+
* @param scope - The CDK construct scope
|
|
59
|
+
* @returns An IntegrationBuilder with default lambda integrations
|
|
60
|
+
*/
|
|
61
|
+
public static defaultIntegrations = (scope: Construct) => {
|
|
62
|
+
return IntegrationBuilder.http({
|
|
63
|
+
<%_ if (backend.type === 'trpc') { _%>
|
|
64
|
+
operations: routerToOperations(appRouter),
|
|
65
|
+
<%_ } else if (backend.type === 'fastapi') { _%>
|
|
66
|
+
operations: OPERATION_DETAILS,
|
|
67
|
+
<%_ } _%>
|
|
68
|
+
defaultIntegrationOptions: {
|
|
69
|
+
<%_ if (backend.type === 'trpc') { _%>
|
|
70
|
+
runtime: Runtime.NODEJS_LATEST,
|
|
71
|
+
handler: 'index.handler',
|
|
72
|
+
code: Code.fromAsset(
|
|
73
|
+
url.fileURLToPath(
|
|
74
|
+
new URL(
|
|
75
|
+
'../../../../../../dist/<%- backend.dir %>/bundle',
|
|
76
|
+
import.meta.url,
|
|
77
|
+
),
|
|
78
|
+
),
|
|
79
|
+
),
|
|
80
|
+
<%_ } else if (backend.type === 'fastapi') { _%>
|
|
81
|
+
runtime: Runtime.PYTHON_3_12,
|
|
82
|
+
handler: '<%= backend.apiNameSnakeCase %>.main.handler',
|
|
83
|
+
code: Code.fromAsset(
|
|
84
|
+
url.fileURLToPath(
|
|
85
|
+
new URL(
|
|
86
|
+
'../../../../../../dist/<%- backend.dir %>/bundle',
|
|
87
|
+
import.meta.url,
|
|
88
|
+
),
|
|
89
|
+
),
|
|
90
|
+
),
|
|
91
|
+
<%_ } _%>
|
|
92
|
+
timeout: Duration.seconds(30),
|
|
93
|
+
tracing: Tracing.ACTIVE,
|
|
94
|
+
environment: {
|
|
95
|
+
AWS_CONNECTION_REUSE_ENABLED: '1',
|
|
96
|
+
},
|
|
97
|
+
} satisfies FunctionProps,
|
|
98
|
+
buildDefaultIntegration: (op, props: FunctionProps) => {
|
|
99
|
+
const handler = new Function(scope, `<%= apiNameClassName %>${op}Handler`, props);
|
|
100
|
+
return {
|
|
101
|
+
handler,
|
|
102
|
+
integration: new HttpLambdaIntegration(`<%= apiNameClassName %>${op}Integration`, handler),
|
|
103
|
+
};
|
|
104
|
+
},
|
|
105
|
+
});
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
constructor(
|
|
109
|
+
scope: Construct,
|
|
110
|
+
id: string,
|
|
111
|
+
props: <%= apiNameClassName %>Props<TIntegrations>,
|
|
112
|
+
) {
|
|
113
|
+
super(scope, id, {
|
|
114
|
+
apiName: '<%= apiNameClassName %>',
|
|
115
|
+
corsPreflight: {
|
|
116
|
+
allowOrigins: ['*'],
|
|
117
|
+
allowMethods: [CorsHttpMethod.ANY],
|
|
118
|
+
allowHeaders: [
|
|
119
|
+
'authorization',
|
|
120
|
+
'content-type',
|
|
121
|
+
'x-amz-content-sha256',
|
|
122
|
+
'x-amz-date',
|
|
123
|
+
'x-amz-security-token',
|
|
124
|
+
],
|
|
125
|
+
},
|
|
126
|
+
defaultAuthorizer: new HttpIamAuthorizer(),
|
|
127
|
+
<%_ if (backend.type === 'trpc') { _%>
|
|
128
|
+
operations: routerToOperations(appRouter),
|
|
129
|
+
<%_ } else if (backend.type === 'fastapi') { _%>
|
|
130
|
+
operations: OPERATION_DETAILS,
|
|
131
|
+
<%_ } _%>
|
|
132
|
+
...props,
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { Construct } from 'constructs';
|
|
2
|
+
import * as url from 'url';
|
|
3
|
+
import {
|
|
4
|
+
Code,
|
|
5
|
+
Runtime,
|
|
6
|
+
Function,
|
|
7
|
+
FunctionProps,
|
|
8
|
+
Tracing,
|
|
9
|
+
} from 'aws-cdk-lib/aws-lambda';
|
|
10
|
+
import {
|
|
11
|
+
AuthorizationType,
|
|
12
|
+
Cors,
|
|
13
|
+
LambdaIntegration,
|
|
14
|
+
} from 'aws-cdk-lib/aws-apigateway';
|
|
15
|
+
import { Duration, Stack } from 'aws-cdk-lib';
|
|
16
|
+
import {
|
|
17
|
+
PolicyDocument,
|
|
18
|
+
PolicyStatement,
|
|
19
|
+
Effect,
|
|
20
|
+
AccountPrincipal,
|
|
21
|
+
AnyPrincipal,
|
|
22
|
+
} from 'aws-cdk-lib/aws-iam';
|
|
23
|
+
import {
|
|
24
|
+
IntegrationBuilder,
|
|
25
|
+
RestApiIntegration,
|
|
26
|
+
} from '../../core/api/utils.js';
|
|
27
|
+
import { RestApi } from '../../core/api/rest-api.js';
|
|
28
|
+
<%_ if (backend.type === 'trpc') { _%>
|
|
29
|
+
import { Procedures, routerToOperations } from '../../core/api/trpc-utils.js';
|
|
30
|
+
import { AppRouter, appRouter } from '<%= backend.projectAlias %>';
|
|
31
|
+
|
|
32
|
+
// String union type for all API operation names
|
|
33
|
+
type Operations = Procedures<AppRouter>;
|
|
34
|
+
<%_ } else if (backend.type === 'fastapi') { _%>
|
|
35
|
+
import {
|
|
36
|
+
OPERATION_DETAILS,
|
|
37
|
+
Operations,
|
|
38
|
+
} from '../../generated/<%- apiNameKebabCase %>/metadata.gen.js';
|
|
39
|
+
<%_ } _%>
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Properties for creating a <%= apiNameClassName %> construct
|
|
43
|
+
*
|
|
44
|
+
* @template TIntegrations - Map of operation names to their integrations
|
|
45
|
+
*/
|
|
46
|
+
export interface <%= apiNameClassName %>Props<
|
|
47
|
+
TIntegrations extends Record<Operations, RestApiIntegration>,
|
|
48
|
+
> {
|
|
49
|
+
/**
|
|
50
|
+
* Map of operation names to their API Gateway integrations
|
|
51
|
+
*/
|
|
52
|
+
integrations: TIntegrations;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* A CDK construct that creates and configures an AWS API Gateway REST API
|
|
57
|
+
* specifically for <%= apiNameClassName %>.
|
|
58
|
+
* @template TIntegrations - Map of operation names to their integrations
|
|
59
|
+
*/
|
|
60
|
+
export class <%= apiNameClassName %><
|
|
61
|
+
TIntegrations extends Record<Operations, RestApiIntegration>,
|
|
62
|
+
> extends RestApi<Operations, TIntegrations> {
|
|
63
|
+
/**
|
|
64
|
+
* Creates default integrations for all operations, which implement each operation as
|
|
65
|
+
* its own individual lambda function.
|
|
66
|
+
*
|
|
67
|
+
* @param scope - The CDK construct scope
|
|
68
|
+
* @returns An IntegrationBuilder with default lambda integrations
|
|
69
|
+
*/
|
|
70
|
+
public static defaultIntegrations = (scope: Construct) => {
|
|
71
|
+
return IntegrationBuilder.rest({
|
|
72
|
+
<%_ if (backend.type === 'trpc') { _%>
|
|
73
|
+
operations: routerToOperations(appRouter),
|
|
74
|
+
<%_ } else if (backend.type === 'fastapi') { _%>
|
|
75
|
+
operations: OPERATION_DETAILS,
|
|
76
|
+
<%_ } _%>
|
|
77
|
+
defaultIntegrationOptions: {
|
|
78
|
+
<%_ if (backend.type === 'trpc') { _%>
|
|
79
|
+
runtime: Runtime.NODEJS_LATEST,
|
|
80
|
+
handler: 'index.handler',
|
|
81
|
+
code: Code.fromAsset(
|
|
82
|
+
url.fileURLToPath(
|
|
83
|
+
new URL(
|
|
84
|
+
'../../../../../../dist/<%- backend.dir %>/bundle',
|
|
85
|
+
import.meta.url,
|
|
86
|
+
),
|
|
87
|
+
),
|
|
88
|
+
),
|
|
89
|
+
<%_ } else if (backend.type === 'fastapi') { _%>
|
|
90
|
+
runtime: Runtime.PYTHON_3_12,
|
|
91
|
+
handler: '<%= backend.apiNameSnakeCase %>.main.handler',
|
|
92
|
+
code: Code.fromAsset(
|
|
93
|
+
url.fileURLToPath(
|
|
94
|
+
new URL(
|
|
95
|
+
'../../../../../../dist/<%- backend.dir %>/bundle',
|
|
96
|
+
import.meta.url,
|
|
97
|
+
),
|
|
98
|
+
),
|
|
99
|
+
),
|
|
100
|
+
<%_ } _%>
|
|
101
|
+
timeout: Duration.seconds(30),
|
|
102
|
+
tracing: Tracing.ACTIVE,
|
|
103
|
+
environment: {
|
|
104
|
+
AWS_CONNECTION_REUSE_ENABLED: '1',
|
|
105
|
+
},
|
|
106
|
+
} satisfies FunctionProps,
|
|
107
|
+
buildDefaultIntegration: (op, props: FunctionProps) => {
|
|
108
|
+
const handler = new Function(scope, `<%= apiNameClassName %>${op}Handler`, props);
|
|
109
|
+
return { handler, integration: new LambdaIntegration(handler) };
|
|
110
|
+
},
|
|
111
|
+
});
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
constructor(
|
|
115
|
+
scope: Construct,
|
|
116
|
+
id: string,
|
|
117
|
+
props: <%= apiNameClassName %>Props<TIntegrations>,
|
|
118
|
+
) {
|
|
119
|
+
super(scope, id, {
|
|
120
|
+
apiName: '<%= apiNameClassName %>',
|
|
121
|
+
defaultMethodOptions: {
|
|
122
|
+
authorizationType: AuthorizationType.IAM,
|
|
123
|
+
},
|
|
124
|
+
defaultCorsPreflightOptions: {
|
|
125
|
+
allowOrigins: Cors.ALL_ORIGINS,
|
|
126
|
+
allowMethods: Cors.ALL_METHODS,
|
|
127
|
+
},
|
|
128
|
+
policy: new PolicyDocument({
|
|
129
|
+
statements: [
|
|
130
|
+
// Here we grant any AWS credentials from the account that the project is deployed in to call the api.
|
|
131
|
+
// Machine to machine fine-grained access can be defined here using more specific principals (eg roles or
|
|
132
|
+
// users) and resources (eg which api paths may be invoked by which principal) if required.
|
|
133
|
+
new PolicyStatement({
|
|
134
|
+
effect: Effect.ALLOW,
|
|
135
|
+
principals: [new AccountPrincipal(Stack.of(scope).account)],
|
|
136
|
+
actions: ['execute-api:Invoke'],
|
|
137
|
+
resources: ['execute-api:/*'],
|
|
138
|
+
}),
|
|
139
|
+
// Open up OPTIONS to allow browsers to make unauthenticated preflight requests
|
|
140
|
+
new PolicyStatement({
|
|
141
|
+
effect: Effect.ALLOW,
|
|
142
|
+
principals: [new AnyPrincipal()],
|
|
143
|
+
actions: ['execute-api:Invoke'],
|
|
144
|
+
resources: ['execute-api:/*/OPTIONS/*'],
|
|
145
|
+
}),
|
|
146
|
+
],
|
|
147
|
+
}),
|
|
148
|
+
<%_ if (backend.type === 'trpc') { _%>
|
|
149
|
+
operations: routerToOperations(appRouter),
|
|
150
|
+
<%_ } else if (backend.type === 'fastapi') { _%>
|
|
151
|
+
operations: OPERATION_DETAILS,
|
|
152
|
+
<%_ } _%>
|
|
153
|
+
...props,
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { Construct } from 'constructs';
|
|
2
|
+
import { RuntimeConfig } from '../runtime-config.js';
|
|
3
|
+
import { Grant, IGrantable } from 'aws-cdk-lib/aws-iam';
|
|
4
|
+
import { HttpApiIntegration, OperationDetails } from './utils.js';
|
|
5
|
+
import { CfnOutput } from 'aws-cdk-lib';
|
|
6
|
+
import {
|
|
7
|
+
HttpApi as _HttpApi,
|
|
8
|
+
HttpApiProps as _HttpApiProps,
|
|
9
|
+
HttpMethod,
|
|
10
|
+
} from 'aws-cdk-lib/aws-apigatewayv2';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Properties for creating an HttpApi construct.
|
|
14
|
+
*
|
|
15
|
+
* @template TIntegrations - Record mapping operation names to their integrations
|
|
16
|
+
* @template TOperation - String literal type representing operation names
|
|
17
|
+
*/
|
|
18
|
+
export interface HttpApiProps<
|
|
19
|
+
TIntegrations extends Record<TOperation, HttpApiIntegration>,
|
|
20
|
+
TOperation extends string,
|
|
21
|
+
> extends _HttpApiProps {
|
|
22
|
+
/**
|
|
23
|
+
* Unique name for the API, used in runtime configuration
|
|
24
|
+
*/
|
|
25
|
+
readonly apiName: string;
|
|
26
|
+
/**
|
|
27
|
+
* Map of operation names to their API path and HTTP method details
|
|
28
|
+
*/
|
|
29
|
+
readonly operations: Record<TOperation, OperationDetails>;
|
|
30
|
+
/**
|
|
31
|
+
* Map of operation names to their API Gateway integrations
|
|
32
|
+
*/
|
|
33
|
+
readonly integrations: TIntegrations;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* A CDK construct that creates and configures an AWS API Gateway HTTP API.
|
|
38
|
+
*
|
|
39
|
+
* This class extends the base CDK HttpApi with additional functionality:
|
|
40
|
+
* - Type-safe operation and integration management
|
|
41
|
+
* - Automatic resource creation based on path patterns
|
|
42
|
+
* - Integration with runtime configuration for client discovery
|
|
43
|
+
*
|
|
44
|
+
* @template TOperation - String literal type representing operation names
|
|
45
|
+
* @template TIntegrations - Record mapping operation names to their integrations
|
|
46
|
+
*/
|
|
47
|
+
export class HttpApi<
|
|
48
|
+
TOperation extends string,
|
|
49
|
+
TIntegrations extends Record<TOperation, HttpApiIntegration>,
|
|
50
|
+
> extends Construct {
|
|
51
|
+
/** The underlying CDK HttpApi instance */
|
|
52
|
+
public readonly api: _HttpApi;
|
|
53
|
+
|
|
54
|
+
/** Map of operation names to their API Gateway integrations */
|
|
55
|
+
public readonly integrations: TIntegrations;
|
|
56
|
+
|
|
57
|
+
constructor(
|
|
58
|
+
scope: Construct,
|
|
59
|
+
id: string,
|
|
60
|
+
{
|
|
61
|
+
apiName,
|
|
62
|
+
operations,
|
|
63
|
+
integrations,
|
|
64
|
+
...props
|
|
65
|
+
}: HttpApiProps<TIntegrations, TOperation>,
|
|
66
|
+
) {
|
|
67
|
+
super(scope, id);
|
|
68
|
+
this.integrations = integrations;
|
|
69
|
+
|
|
70
|
+
// Create the API Gateway REST API with logging enabled
|
|
71
|
+
this.api = new _HttpApi(this, 'Api', {
|
|
72
|
+
...props,
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// Create API resources and methods for each operation
|
|
76
|
+
(Object.entries(operations) as [TOperation, OperationDetails][]).map(
|
|
77
|
+
([op, details]) => {
|
|
78
|
+
this.api.addRoutes({
|
|
79
|
+
path: details.path.startsWith('/')
|
|
80
|
+
? details.path
|
|
81
|
+
: `/${details.path}`,
|
|
82
|
+
methods: [details.method as HttpMethod],
|
|
83
|
+
integration: integrations[op].integration,
|
|
84
|
+
...integrations[op].options,
|
|
85
|
+
});
|
|
86
|
+
},
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
new CfnOutput(this, `${apiName}Url`, {
|
|
90
|
+
value: this.api.url!,
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
// Register the API URL in runtime configuration for client discovery
|
|
94
|
+
RuntimeConfig.ensure(this).config.apis = {
|
|
95
|
+
...RuntimeConfig.ensure(this).config.apis!,
|
|
96
|
+
[apiName]: this.api.url!,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Grants IAM permissions to invoke any method on this API.
|
|
102
|
+
*
|
|
103
|
+
* @param grantee - The IAM principal to grant permissions to
|
|
104
|
+
*/
|
|
105
|
+
public grantInvokeAccess(grantee: IGrantable) {
|
|
106
|
+
Grant.addToPrincipal({
|
|
107
|
+
grantee,
|
|
108
|
+
actions: ['execute-api:Invoke'],
|
|
109
|
+
resourceArns: [this.api.arnForExecuteApi('*', '/*', '*')],
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { Construct } from 'constructs';
|
|
2
|
+
import {
|
|
3
|
+
RestApi as _RestApi,
|
|
4
|
+
RestApiProps as _RestApiProps,
|
|
5
|
+
AccessLogFormat,
|
|
6
|
+
IResource,
|
|
7
|
+
LogGroupLogDestination,
|
|
8
|
+
MethodLoggingLevel,
|
|
9
|
+
} from 'aws-cdk-lib/aws-apigateway';
|
|
10
|
+
import { LogGroup } from 'aws-cdk-lib/aws-logs';
|
|
11
|
+
import { RuntimeConfig } from '../runtime-config.js';
|
|
12
|
+
import { Grant, IGrantable } from 'aws-cdk-lib/aws-iam';
|
|
13
|
+
import { OperationDetails, RestApiIntegration } from './utils.js';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Properties for creating a RestApi construct.
|
|
17
|
+
*
|
|
18
|
+
* @template TIntegrations - Record mapping operation names to their integrations
|
|
19
|
+
* @template TOperation - String literal type representing operation names
|
|
20
|
+
*/
|
|
21
|
+
export interface RestApiProps<
|
|
22
|
+
TIntegrations extends Record<TOperation, RestApiIntegration>,
|
|
23
|
+
TOperation extends string,
|
|
24
|
+
> extends _RestApiProps {
|
|
25
|
+
/**
|
|
26
|
+
* Unique name for the API, used in runtime configuration
|
|
27
|
+
*/
|
|
28
|
+
readonly apiName: string;
|
|
29
|
+
/**
|
|
30
|
+
* Map of operation names to their API path and HTTP method details
|
|
31
|
+
*/
|
|
32
|
+
readonly operations: Record<TOperation, OperationDetails>;
|
|
33
|
+
/**
|
|
34
|
+
* Map of operation names to their API Gateway integrations
|
|
35
|
+
*/
|
|
36
|
+
readonly integrations: TIntegrations;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* A CDK construct that creates and configures an AWS API Gateway REST API.
|
|
41
|
+
*
|
|
42
|
+
* This class extends the base CDK RestApi with additional functionality:
|
|
43
|
+
* - Type-safe operation and integration management
|
|
44
|
+
* - Automatic resource creation based on path patterns
|
|
45
|
+
* - Integration with runtime configuration for client discovery
|
|
46
|
+
*
|
|
47
|
+
* @template TOperation - String literal type representing operation names
|
|
48
|
+
* @template TIntegrations - Record mapping operation names to their integrations
|
|
49
|
+
*/
|
|
50
|
+
export class RestApi<
|
|
51
|
+
TOperation extends string,
|
|
52
|
+
TIntegrations extends Record<TOperation, RestApiIntegration>,
|
|
53
|
+
> extends Construct {
|
|
54
|
+
/** The underlying CDK RestApi instance */
|
|
55
|
+
public readonly api: _RestApi;
|
|
56
|
+
|
|
57
|
+
/** Map of operation names to their API Gateway integrations */
|
|
58
|
+
public readonly integrations: TIntegrations;
|
|
59
|
+
|
|
60
|
+
constructor(
|
|
61
|
+
scope: Construct,
|
|
62
|
+
id: string,
|
|
63
|
+
{
|
|
64
|
+
apiName,
|
|
65
|
+
operations,
|
|
66
|
+
integrations,
|
|
67
|
+
...props
|
|
68
|
+
}: RestApiProps<TIntegrations, TOperation>,
|
|
69
|
+
) {
|
|
70
|
+
super(scope, id);
|
|
71
|
+
this.integrations = integrations;
|
|
72
|
+
|
|
73
|
+
// Create the API Gateway REST API with logging enabled
|
|
74
|
+
this.api = new _RestApi(this, 'Api', {
|
|
75
|
+
deployOptions: {
|
|
76
|
+
accessLogDestination: new LogGroupLogDestination(
|
|
77
|
+
new LogGroup(this, 'AccessLogs'),
|
|
78
|
+
),
|
|
79
|
+
accessLogFormat: AccessLogFormat.clf(),
|
|
80
|
+
loggingLevel: MethodLoggingLevel.INFO,
|
|
81
|
+
},
|
|
82
|
+
...props,
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// Create API resources and methods for each operation
|
|
86
|
+
(Object.entries(operations) as [TOperation, OperationDetails][]).map(
|
|
87
|
+
([op, details]) => {
|
|
88
|
+
const resource = this.getOrCreateResource(
|
|
89
|
+
this.api.root,
|
|
90
|
+
(details.path.startsWith('/')
|
|
91
|
+
? details.path.slice(1)
|
|
92
|
+
: details.path
|
|
93
|
+
).split('/'),
|
|
94
|
+
);
|
|
95
|
+
resource.addMethod(details.method, integrations[op].integration, integrations[op].options);
|
|
96
|
+
},
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
// Register the API URL in runtime configuration for client discovery
|
|
100
|
+
RuntimeConfig.ensure(this).config.apis = {
|
|
101
|
+
...RuntimeConfig.ensure(this).config.apis!,
|
|
102
|
+
[apiName]: this.api.url!,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Recursively creates or retrieves API Gateway resources based on a path pattern.
|
|
108
|
+
*
|
|
109
|
+
* @param resource - The parent API Gateway resource
|
|
110
|
+
* @param pathParts - Array of path segments to create or retrieve
|
|
111
|
+
* @returns The API Gateway resource at the end of the path
|
|
112
|
+
*/
|
|
113
|
+
private getOrCreateResource(
|
|
114
|
+
resource: IResource,
|
|
115
|
+
[nextPathPart, ...pathParts]: string[],
|
|
116
|
+
): IResource {
|
|
117
|
+
if (!nextPathPart) {
|
|
118
|
+
return resource;
|
|
119
|
+
}
|
|
120
|
+
const childResource =
|
|
121
|
+
resource.getResource(nextPathPart) ?? resource.addResource(nextPathPart);
|
|
122
|
+
return this.getOrCreateResource(childResource, pathParts);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Grants IAM permissions to invoke any method on this API.
|
|
127
|
+
*
|
|
128
|
+
* @param grantee - The IAM principal to grant permissions to
|
|
129
|
+
*/
|
|
130
|
+
public grantInvokeAccess(grantee: IGrantable) {
|
|
131
|
+
Grant.addToPrincipal({
|
|
132
|
+
grantee,
|
|
133
|
+
actions: ['execute-api:Invoke'],
|
|
134
|
+
resourceArns: [this.api.arnForExecuteApi('*', '/*', '*')],
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { TRPCRouterRecord, AnyTRPCRouter } from '@trpc/server';
|
|
2
|
+
import { OperationDetails } from './utils.js';
|
|
3
|
+
import { HttpMethod } from 'aws-cdk-lib/aws-apigatewayv2';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Helper type that recursively extracts procedure names from a tRPC router.
|
|
7
|
+
* This type traverses the router structure and builds fully qualified procedure names
|
|
8
|
+
* with dot notation for nested routers.
|
|
9
|
+
*
|
|
10
|
+
* @template T - The tRPC router record type
|
|
11
|
+
* @template Prefix - The current path prefix for nested procedures
|
|
12
|
+
*/
|
|
13
|
+
type _Procedures<T extends TRPCRouterRecord, Prefix extends string = ''> = {
|
|
14
|
+
[K in keyof T]: K extends string
|
|
15
|
+
? T[K] extends TRPCRouterRecord
|
|
16
|
+
? _Procedures<T[K], `${Prefix}${K}.`>
|
|
17
|
+
: `${Prefix}${K}`
|
|
18
|
+
: never;
|
|
19
|
+
}[keyof T];
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Extracts all procedure names from a tRPC router as a union of string literals.
|
|
23
|
+
* This type is used to provide type-safe access to procedure names throughout the API.
|
|
24
|
+
*
|
|
25
|
+
* @template TRouter - The tRPC router type
|
|
26
|
+
*/
|
|
27
|
+
export type Procedures<TRouter extends AnyTRPCRouter> = _Procedures<
|
|
28
|
+
TRouter['_def']['record']
|
|
29
|
+
>;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Converts a tRPC router to a map of API operations.
|
|
33
|
+
* This method recursively traverses the router structure and creates operation details
|
|
34
|
+
* for each procedure, mapping queries to GET methods and mutations to POST methods.
|
|
35
|
+
*
|
|
36
|
+
* @param router - The tRPC router to convert
|
|
37
|
+
* @param prefix - The current path prefix for nested procedures
|
|
38
|
+
* @returns A map of procedure names to their API operation details
|
|
39
|
+
*/
|
|
40
|
+
export const routerToOperations = <TRouter extends AnyTRPCRouter>(
|
|
41
|
+
router: TRouter,
|
|
42
|
+
prefix = '',
|
|
43
|
+
): Record<Procedures<TRouter>, OperationDetails> => {
|
|
44
|
+
return Object.fromEntries(
|
|
45
|
+
Object.entries(router._def.procedures).flatMap(
|
|
46
|
+
([op, procedureOrRouter]: [string, any]) => {
|
|
47
|
+
const fullPath = prefix ? `${prefix}.${op}` : op;
|
|
48
|
+
return procedureOrRouter._def?.router
|
|
49
|
+
? Object.entries(
|
|
50
|
+
routerToOperations<TRouter>(procedureOrRouter, fullPath),
|
|
51
|
+
)
|
|
52
|
+
: [
|
|
53
|
+
[
|
|
54
|
+
fullPath,
|
|
55
|
+
{
|
|
56
|
+
path: fullPath,
|
|
57
|
+
method:
|
|
58
|
+
procedureOrRouter._def.type === 'query'
|
|
59
|
+
? HttpMethod.GET
|
|
60
|
+
: HttpMethod.POST,
|
|
61
|
+
},
|
|
62
|
+
],
|
|
63
|
+
];
|
|
64
|
+
},
|
|
65
|
+
),
|
|
66
|
+
) as Record<Procedures<TRouter>, OperationDetails>;
|
|
67
|
+
};
|