@sylvesterllc/aws-constructs 1.0.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/.github/workflows/publish.yml +36 -0
- package/dist/config/AppConfig.d.ts +24 -0
- package/dist/config/AppConfig.js +62 -0
- package/dist/config/Constants.d.ts +26 -0
- package/dist/config/Constants.js +30 -0
- package/dist/config/Environments.d.ts +2 -0
- package/dist/config/Environments.js +3 -0
- package/dist/config/MicroserviceAppConfig.d.ts +5 -0
- package/dist/config/MicroserviceAppConfig.js +11 -0
- package/dist/config/bishopConfig.d.ts +2 -0
- package/dist/config/bishopConfig.js +81 -0
- package/dist/config/customConfigs/ApiAppConfig.d.ts +5 -0
- package/dist/config/customConfigs/ApiAppConfig.js +4 -0
- package/dist/config/customConfigs/GlobalAppConfig.d.ts +8 -0
- package/dist/config/customConfigs/GlobalAppConfig.js +3 -0
- package/dist/config/customConfigs/IAppConfig.d.ts +10 -0
- package/dist/config/customConfigs/IAppConfig.js +3 -0
- package/dist/config/customConfigs/ResourceAppConfig.d.ts +8 -0
- package/dist/config/customConfigs/ResourceAppConfig.js +3 -0
- package/dist/config/microServiceConfig.d.ts +2 -0
- package/dist/config/microServiceConfig.js +82 -0
- package/dist/config/types/TsgCdkConfigType.d.ts +3 -0
- package/dist/config/types/TsgCdkConfigType.js +3 -0
- package/dist/config/types/TsgCognitoConfig.d.ts +5 -0
- package/dist/config/types/TsgCognitoConfig.js +3 -0
- package/dist/config/types/TsgDnsConfig.d.ts +9 -0
- package/dist/config/types/TsgDnsConfig.js +3 -0
- package/dist/config/types/TsgDynamoDbProp.d.ts +6 -0
- package/dist/config/types/TsgDynamoDbProp.js +3 -0
- package/dist/config/types/TsgDynamoIndex.d.ts +13 -0
- package/dist/config/types/TsgDynamoIndex.js +3 -0
- package/dist/config/types/TsgDynamoProp.d.ts +15 -0
- package/dist/config/types/TsgDynamoProp.js +3 -0
- package/dist/config/types/TsgDynamoTable.d.ts +4 -0
- package/dist/config/types/TsgDynamoTable.js +3 -0
- package/dist/config/types/TsgDynamoTableRef.d.ts +4 -0
- package/dist/config/types/TsgDynamoTableRef.js +3 -0
- package/dist/config/types/TsgLambda.d.ts +4 -0
- package/dist/config/types/TsgLambda.js +3 -0
- package/dist/config/types/TsgLambdaLayerProp.d.ts +5 -0
- package/dist/config/types/TsgLambdaLayerProp.js +3 -0
- package/dist/config/types/TsgLambdaMap.d.ts +3 -0
- package/dist/config/types/TsgLambdaMap.js +3 -0
- package/dist/config/types/TsgLambdaName.d.ts +1 -0
- package/dist/config/types/TsgLambdaName.js +3 -0
- package/dist/config/types/TsgLambdaNode.d.ts +3 -0
- package/dist/config/types/TsgLambdaNode.js +3 -0
- package/dist/config/types/TsgLambdaProp.d.ts +16 -0
- package/dist/config/types/TsgLambdaProp.js +3 -0
- package/dist/config/types/TsgLambdaProps.d.ts +16 -0
- package/dist/config/types/TsgLambdaProps.js +3 -0
- package/dist/config/types/TsgLambdaRoutable.d.ts +6 -0
- package/dist/config/types/TsgLambdaRoutable.js +3 -0
- package/dist/config/types/index.d.ts +8 -0
- package/dist/config/types/index.js +25 -0
- package/dist/constructs/MicroService.d.ts +12 -0
- package/dist/constructs/MicroService.js +47 -0
- package/dist/constructs/index.d.ts +1 -0
- package/dist/constructs/index.js +6 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +8 -0
- package/dist/interfaces/CreateLambdaFunctionInput.d.ts +10 -0
- package/dist/interfaces/CreateLambdaFunctionInput.js +3 -0
- package/dist/interfaces/MicroserviceProps.d.ts +10 -0
- package/dist/interfaces/MicroserviceProps.js +3 -0
- package/dist/resources/base/baseResource.d.ts +10 -0
- package/dist/resources/base/baseResource.js +11 -0
- package/dist/resources/certificate/createCertificate.d.ts +9 -0
- package/dist/resources/certificate/createCertificate.js +29 -0
- package/dist/resources/cognito/createCognito.d.ts +7 -0
- package/dist/resources/cognito/createCognito.js +35 -0
- package/dist/resources/cognito/createCognitoProps.d.ts +2 -0
- package/dist/resources/cognito/createCognitoProps.js +47 -0
- package/dist/resources/dynamodb/CreateDynamo.d.ts +15 -0
- package/dist/resources/dynamodb/CreateDynamo.js +87 -0
- package/dist/resources/gateway/createApi.d.ts +19 -0
- package/dist/resources/gateway/createApi.js +104 -0
- package/dist/resources/gateway/createMicroServiceBundle.d.ts +25 -0
- package/dist/resources/gateway/createMicroServiceBundle.js +126 -0
- package/dist/resources/helpers/createAuthorizer.d.ts +15 -0
- package/dist/resources/helpers/createAuthorizer.js +80 -0
- package/dist/resources/helpers/createCommonLayer.d.ts +3 -0
- package/dist/resources/helpers/createCommonLayer.js +15 -0
- package/dist/resources/helpers/createRoutes.d.ts +8 -0
- package/dist/resources/helpers/createRoutes.js +30 -0
- package/dist/resources/lambda/createLambda.d.ts +20 -0
- package/dist/resources/lambda/createLambda.js +135 -0
- package/dist/resources/lambda-layer/createLambdaLayer.d.ts +5 -0
- package/dist/resources/lambda-layer/createLambdaLayer.js +31 -0
- package/dist/resources/layers/common/src/nodejs/api-response/index.d.ts +1 -0
- package/dist/resources/layers/common/src/nodejs/api-response/index.js +18 -0
- package/dist/resources/layers/common/src/nodejs/api-response/responses.d.ts +24 -0
- package/dist/resources/layers/common/src/nodejs/api-response/responses.js +43 -0
- package/dist/resources/layers/common/src/nodejs/index.d.ts +2 -0
- package/dist/resources/layers/common/src/nodejs/index.js +19 -0
- package/dist/resources/layers/common/src/nodejs/logger/IContext.d.ts +5 -0
- package/dist/resources/layers/common/src/nodejs/logger/IContext.js +3 -0
- package/dist/resources/layers/common/src/nodejs/logger/index.d.ts +2 -0
- package/dist/resources/layers/common/src/nodejs/logger/index.js +19 -0
- package/dist/resources/layers/common/src/nodejs/logger/logger.d.ts +2 -0
- package/dist/resources/layers/common/src/nodejs/logger/logger.js +12 -0
- package/dist/resources/resources/iam/policyDocuments/cognitoPolicy.d.ts +4 -0
- package/dist/resources/resources/iam/policyDocuments/cognitoPolicy.js +52 -0
- package/dist/resources/resources/iam/roles/createRole.d.ts +4 -0
- package/dist/resources/resources/iam/roles/createRole.js +22 -0
- package/dist/resources/route53/CreateZone.d.ts +0 -0
- package/dist/resources/route53/CreateZone.js +47 -0
- package/dist/resources/route53/create-zones.d.ts +0 -0
- package/dist/resources/route53/create-zones.js +30 -0
- package/dist/resources/securityManager/index.d.ts +4 -0
- package/dist/resources/securityManager/index.js +18 -0
- package/docs/lambda-config.md +1 -0
- package/package.json +48 -0
- package/readme.md +147 -0
- package/src/config/AppConfig.ts +93 -0
- package/src/config/Constants.ts +27 -0
- package/src/config/Environments.ts +2 -0
- package/src/config/MicroserviceAppConfig.ts +9 -0
- package/src/config/bishopConfig.ts +83 -0
- package/src/config/customConfigs/ApiAppConfig.ts +6 -0
- package/src/config/customConfigs/GlobalAppConfig.ts +11 -0
- package/src/config/customConfigs/IAppConfig.ts +13 -0
- package/src/config/customConfigs/ResourceAppConfig.ts +12 -0
- package/src/config/microServiceConfig.ts +85 -0
- package/src/config/types/TsgCdkConfigType.ts +3 -0
- package/src/config/types/TsgCognitoConfig.ts +6 -0
- package/src/config/types/TsgDnsConfig.ts +9 -0
- package/src/config/types/TsgDynamoDbProp.ts +7 -0
- package/src/config/types/TsgDynamoIndex.ts +16 -0
- package/src/config/types/TsgDynamoProp.ts +17 -0
- package/src/config/types/TsgDynamoTable.ts +5 -0
- package/src/config/types/TsgDynamoTableRef.ts +4 -0
- package/src/config/types/TsgLambda.ts +6 -0
- package/src/config/types/TsgLambdaLayerProp.ts +5 -0
- package/src/config/types/TsgLambdaMap.ts +4 -0
- package/src/config/types/TsgLambdaName.ts +4 -0
- package/src/config/types/TsgLambdaNode.ts +4 -0
- package/src/config/types/TsgLambdaProp.ts +16 -0
- package/src/config/types/TsgLambdaProps.ts +19 -0
- package/src/config/types/TsgLambdaRoutable.ts +6 -0
- package/src/config/types/index.ts +9 -0
- package/src/constructs/MicroService.ts +68 -0
- package/src/constructs/index.ts +1 -0
- package/src/index.ts +4 -0
- package/src/interfaces/CreateLambdaFunctionInput.ts +11 -0
- package/src/interfaces/MicroserviceProps.ts +11 -0
- package/src/resources/.gitkeep +0 -0
- package/src/resources/base/baseResource.ts +15 -0
- package/src/resources/certificate/createCertificate.ts +40 -0
- package/src/resources/cicd/.gitkeep +0 -0
- package/src/resources/cognito/.gitkeep +0 -0
- package/src/resources/cognito/createCognito.ts +47 -0
- package/src/resources/cognito/createCognitoProps.ts +46 -0
- package/src/resources/dynamodb/CreateDynamo.ts +111 -0
- package/src/resources/gateway/createApi.ts +138 -0
- package/src/resources/gateway/createMicroServiceBundle.ts +183 -0
- package/src/resources/helpers/createAuthorizer.ts +106 -0
- package/src/resources/helpers/createCommonLayer.ts +18 -0
- package/src/resources/helpers/createRoutes.ts +43 -0
- package/src/resources/lambda/createLambda.ts +184 -0
- package/src/resources/lambda-layer/createLambdaLayer.ts +40 -0
- package/src/resources/layers/common/package.json +22 -0
- package/src/resources/layers/common/src/nodejs/api-response/index.ts +1 -0
- package/src/resources/layers/common/src/nodejs/api-response/responses.ts +41 -0
- package/src/resources/layers/common/src/nodejs/index.ts +3 -0
- package/src/resources/layers/common/src/nodejs/logger/IContext.ts +7 -0
- package/src/resources/layers/common/src/nodejs/logger/index.ts +2 -0
- package/src/resources/layers/common/src/nodejs/logger/logger.ts +11 -0
- package/src/resources/layers/common/tsconfig.json +7 -0
- package/src/resources/resources/iam/policyDocuments/cognitoPolicy.ts +55 -0
- package/src/resources/resources/iam/roles/createRole.ts +30 -0
- package/src/resources/route53/CreateZone.ts +62 -0
- package/src/resources/route53/create-zones.ts +41 -0
- package/src/resources/securityManager/index.ts +24 -0
- package/tsconfig.json +40 -0
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { IRestApi, TokenAuthorizer } from "aws-cdk-lib/aws-apigateway";
|
|
2
|
+
import { ITable, Table } from "aws-cdk-lib/aws-dynamodb";
|
|
3
|
+
import { NodejsFunction } from "aws-cdk-lib/aws-lambda-nodejs";
|
|
4
|
+
import { Effect, PolicyStatement } from "aws-cdk-lib/aws-iam";
|
|
5
|
+
import { Construct } from "constructs";
|
|
6
|
+
import { TsgDynamoTableRef, TsgLambdaProp } from "../../config/types";
|
|
7
|
+
import { TsgLambdaProps } from "../../config/types/TsgLambdaProps";
|
|
8
|
+
import { MicroserviceProps } from "../../interfaces/MicroserviceProps";
|
|
9
|
+
import { CreateAuthorizer } from "../helpers/createAuthorizer";
|
|
10
|
+
import { Routes } from "../helpers/createRoutes";
|
|
11
|
+
import { CreateLambda } from "../lambda/createLambda";
|
|
12
|
+
import { Duration, Stack } from "aws-cdk-lib";
|
|
13
|
+
import { CreateDynamoDb } from "../dynamodb/CreateDynamo";
|
|
14
|
+
import { ISecret } from "aws-cdk-lib/aws-secretsmanager";
|
|
15
|
+
import { LayerVersion } from "aws-cdk-lib/aws-lambda";
|
|
16
|
+
import { AppConfig } from "../../config/AppConfig";
|
|
17
|
+
import { MetricOptions } from "aws-cdk-lib/aws-cloudwatch";
|
|
18
|
+
|
|
19
|
+
export class CreateMicroServiceBundle {
|
|
20
|
+
|
|
21
|
+
protected readonly requireDynamoTableRefs: boolean;
|
|
22
|
+
protected readonly requireAuthorizer: boolean;
|
|
23
|
+
|
|
24
|
+
constructor(scope: Construct,
|
|
25
|
+
private readonly gatewayApi: IRestApi,
|
|
26
|
+
private readonly props: MicroserviceProps,
|
|
27
|
+
private readonly appConfig: AppConfig,
|
|
28
|
+
private readonly tables?: Table[],
|
|
29
|
+
private readonly secretMgr?: ISecret,
|
|
30
|
+
private readonly layers?: LayerVersion[],
|
|
31
|
+
) {
|
|
32
|
+
|
|
33
|
+
this.requireDynamoTableRefs = (props.RESOURCES.DYNAMO?.TABLE_REFS?.length ?? 0 > 0) ? true : false;
|
|
34
|
+
this.requireAuthorizer = (props.RESOURCES.AUTHORIZER) ? true : false;
|
|
35
|
+
this.onInit(scope);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
private onInit(scope: Construct) {
|
|
39
|
+
|
|
40
|
+
//console.log("ENTER CreateMicroServiceBundle.onInit");
|
|
41
|
+
let authorizer: TokenAuthorizer|undefined = undefined;
|
|
42
|
+
|
|
43
|
+
// Create Authorizer
|
|
44
|
+
if (this.requireAuthorizer) {
|
|
45
|
+
authorizer = new CreateAuthorizer(scope, this.appConfig, this.props.RESOURCES.AUTHORIZER!).JwtAuthorizer;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Create Lambdas
|
|
49
|
+
const lambdaProp: TsgLambdaProps = {
|
|
50
|
+
scope,
|
|
51
|
+
prop: this.props,
|
|
52
|
+
layers: this.layers,
|
|
53
|
+
appConfig: this.appConfig
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const lambdas = new CreateLambda(lambdaProp, this.appConfig);
|
|
57
|
+
|
|
58
|
+
if (this.tables) {
|
|
59
|
+
this.AssignAccessToTables(this.tables, lambdas.Lambdas);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Allow access to existing tables
|
|
63
|
+
if (this.requireDynamoTableRefs) {
|
|
64
|
+
this.AssignAccessToTableRefs(scope, this.props.RESOURCES.DYNAMO?.TABLE_REFS, lambdas.Lambdas);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (this.secretMgr) {
|
|
68
|
+
this.AssignAccessToSecretManager(this.secretMgr, lambdas.Lambdas);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
lambdas.Lambdas.map((lambda) => {
|
|
72
|
+
lambda.metricErrors({
|
|
73
|
+
label: `${lambda.functionName}-errors`,
|
|
74
|
+
period: Duration.minutes(3)
|
|
75
|
+
|
|
76
|
+
})
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
this.AddRoutes(this.props, this.gatewayApi, lambdas.Lambdas, authorizer);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
private AssignAccessToTables(tables: Table[], lambdas: NodejsFunction[]) {
|
|
83
|
+
|
|
84
|
+
if (tables) {
|
|
85
|
+
tables.forEach((table: ITable) => {
|
|
86
|
+
|
|
87
|
+
lambdas.forEach((lambda: NodejsFunction) => {
|
|
88
|
+
|
|
89
|
+
// This is a CDK bug: It doesn't provide
|
|
90
|
+
// access to the indexes.
|
|
91
|
+
//table.grantReadWriteData(lambda);
|
|
92
|
+
|
|
93
|
+
// Workaround:
|
|
94
|
+
lambda.addToRolePolicy(
|
|
95
|
+
new PolicyStatement({
|
|
96
|
+
effect: Effect.ALLOW,
|
|
97
|
+
actions: CreateDynamoDb.ReadWriteActions,
|
|
98
|
+
resources: [
|
|
99
|
+
table.tableArn,
|
|
100
|
+
`${table.tableArn}/*`, // This is not recognized by cdk, but table is. why?
|
|
101
|
+
],
|
|
102
|
+
})
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
private AssignAccessToTableRefs(scope: Construct, tableRefs: TsgDynamoTableRef[] | undefined, lambdas: NodejsFunction[]) {
|
|
114
|
+
|
|
115
|
+
if (tableRefs) {
|
|
116
|
+
tableRefs.forEach((tableRef: TsgDynamoTableRef) => {
|
|
117
|
+
|
|
118
|
+
if (tableRef.region) {
|
|
119
|
+
this.AssignReadWriteAccessToTableInRegion(scope, tableRef, lambdas);
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
this.AssignReadWriteAccessToTable(scope, tableRef, lambdas);
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
private AssignReadWriteAccessToTableInRegion(scope: Construct, tableRef: TsgDynamoTableRef, lambdas: NodejsFunction[]) {
|
|
129
|
+
lambdas.forEach((lambda: NodejsFunction) => {
|
|
130
|
+
|
|
131
|
+
let tableArn = "arn:aws:dynamodb:" + tableRef.region + ":" + ((scope as Stack).account) + ":table/" + tableRef.tableName;
|
|
132
|
+
let statement = new PolicyStatement({
|
|
133
|
+
effect: Effect.ALLOW,
|
|
134
|
+
resources: [
|
|
135
|
+
tableArn,
|
|
136
|
+
tableArn + "/index/*",
|
|
137
|
+
],
|
|
138
|
+
actions: CreateDynamoDb.ReadWriteActions,
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
lambda.role?.addToPrincipalPolicy(statement);
|
|
142
|
+
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
private AssignReadWriteAccessToTable(scope: Construct, tableRef: TsgDynamoTableRef, lambdas: NodejsFunction[]) {
|
|
147
|
+
let table: ITable = Table.fromTableName(scope, tableRef.tableName, tableRef.tableName);
|
|
148
|
+
|
|
149
|
+
lambdas.forEach((lambda: NodejsFunction) => {
|
|
150
|
+
table.grantReadWriteData(lambda)
|
|
151
|
+
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
private AddRoutes(props: MicroserviceProps,
|
|
156
|
+
gateway: IRestApi,
|
|
157
|
+
lambdas: NodejsFunction[],
|
|
158
|
+
authorizer?: TokenAuthorizer) {
|
|
159
|
+
|
|
160
|
+
this.appConfig.lambdaConfigs?.forEach((prop: TsgLambdaProp) => {
|
|
161
|
+
|
|
162
|
+
const lambdaId = CreateLambda.getIdForLambda(prop);
|
|
163
|
+
const lambdaNode = lambdas.find(x => x.node.id === lambdaId);
|
|
164
|
+
|
|
165
|
+
if (!lambdaNode) {
|
|
166
|
+
throw new Error("Can't find the Lambda Integration");
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
Routes.createResource(prop, gateway, lambdaNode, authorizer);
|
|
170
|
+
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
private AssignAccessToSecretManager(secret: ISecret, lambdas: NodejsFunction[]) {
|
|
175
|
+
|
|
176
|
+
lambdas.forEach((lambda) => {
|
|
177
|
+
const result = secret.grantRead(lambda);
|
|
178
|
+
console.log('Assigning Access to Secret Manager: ',result);
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { CfnOutput, Duration } from "aws-cdk-lib";
|
|
2
|
+
import { IdentitySource, TokenAuthorizer } from "aws-cdk-lib/aws-apigateway";
|
|
3
|
+
import { ManagedPolicy, ServicePrincipal } from "aws-cdk-lib/aws-iam";
|
|
4
|
+
import { NodejsFunction, NodejsFunctionProps, SourceMapMode } from "aws-cdk-lib/aws-lambda-nodejs";
|
|
5
|
+
import { Construct } from "constructs";
|
|
6
|
+
import * as path from 'path';
|
|
7
|
+
import { AppConfig } from "../../config/AppConfig";
|
|
8
|
+
|
|
9
|
+
import { TsgLambdaProp } from "../../config/types";
|
|
10
|
+
import { MicroserviceProps } from "../../interfaces/MicroserviceProps";
|
|
11
|
+
import { BaseResource } from "../base/baseResource";
|
|
12
|
+
import { CreateLambda } from "../lambda/createLambda";
|
|
13
|
+
|
|
14
|
+
export class CreateAuthorizer extends BaseResource<TokenAuthorizer> {
|
|
15
|
+
|
|
16
|
+
get JwtAuthorizer() {
|
|
17
|
+
return this.createdResources[0];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
constructor(scope: Construct, props: AppConfig, protected authProps: TsgLambdaProp) {
|
|
21
|
+
|
|
22
|
+
super(scope, props);
|
|
23
|
+
|
|
24
|
+
this.createdResources = this.createResource(scope)!;
|
|
25
|
+
|
|
26
|
+
if (this.createdResources) {
|
|
27
|
+
this.createOutput<TokenAuthorizer>(scope, this.createdResources);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
protected createResource(scope: Construct): TokenAuthorizer[] | null {
|
|
33
|
+
return [this.createLambdaAuthorizer(scope, this.authProps)];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
private createLambdaAuthorizer(scope: Construct, lambdaConfig: TsgLambdaProp) {
|
|
37
|
+
//console.log("ENTER createLambdaAuthorizer");
|
|
38
|
+
|
|
39
|
+
const authorizerProps = this.createLambdaFunctionProps(lambdaConfig!);
|
|
40
|
+
|
|
41
|
+
const lambdaId = CreateLambda.getIdForLambda(lambdaConfig);
|
|
42
|
+
const lambda = new NodejsFunction(scope, lambdaId, authorizerProps);
|
|
43
|
+
|
|
44
|
+
lambda.grantInvoke(new ServicePrincipal('apigateway.amazonaws.com'));
|
|
45
|
+
|
|
46
|
+
if (lambdaConfig?.managedPolicies && lambdaConfig.managedPolicies.length > 0) {
|
|
47
|
+
this.assignManagedPolicies(lambda, lambdaConfig.managedPolicies);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const lambdaAuthorizer = new TokenAuthorizer(scope, `${lambdaConfig.name}-authorizer`, {
|
|
51
|
+
handler: lambda,
|
|
52
|
+
authorizerName: lambdaConfig.name,
|
|
53
|
+
resultsCacheTtl: Duration.seconds(0),
|
|
54
|
+
identitySource: IdentitySource.header('Authorization'),
|
|
55
|
+
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
return lambdaAuthorizer;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
protected createOutput<T>(scope: Construct, createdAssets: T[]): void {
|
|
62
|
+
|
|
63
|
+
const entity = createdAssets[0];
|
|
64
|
+
|
|
65
|
+
// new CfnOutput(scope, `authorizer`, {
|
|
66
|
+
// value: (entity as TokenAuthorizer).authorizerArn
|
|
67
|
+
// });
|
|
68
|
+
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
private createLambdaFunctionProps(prop: TsgLambdaProp) {
|
|
72
|
+
|
|
73
|
+
const lambdaProp: NodejsFunctionProps = {
|
|
74
|
+
entry: path.join(prop.codePath),
|
|
75
|
+
functionName: prop.name,
|
|
76
|
+
handler: prop.handler,
|
|
77
|
+
runtime: prop.runtime || this.config.GLOBALS.stackRuntime,
|
|
78
|
+
timeout: prop.duration || Duration.minutes(2),
|
|
79
|
+
memorySize: prop.memory || 512,
|
|
80
|
+
environment: {
|
|
81
|
+
"VERBOSE_LOGGING": "true",
|
|
82
|
+
...prop.environment
|
|
83
|
+
},
|
|
84
|
+
bundling: {
|
|
85
|
+
minify: false,
|
|
86
|
+
target: 'es2020',
|
|
87
|
+
sourceMap: true,
|
|
88
|
+
sourceMapMode: SourceMapMode.INLINE,
|
|
89
|
+
environment: prop.environment || prop.environment,
|
|
90
|
+
},
|
|
91
|
+
}
|
|
92
|
+
return lambdaProp;
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
private assignManagedPolicies(lambda: NodejsFunction, managedPolicyNames: string[]) {
|
|
96
|
+
|
|
97
|
+
managedPolicyNames.forEach((managedPolicyName: string) => {
|
|
98
|
+
|
|
99
|
+
let policy = ManagedPolicy.fromAwsManagedPolicyName(managedPolicyName);
|
|
100
|
+
|
|
101
|
+
lambda.role?.addManagedPolicy(policy);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Construct } from "constructs";
|
|
2
|
+
import { createLambdaLayer } from "../lambda-layer/createLambdaLayer";
|
|
3
|
+
import { TsgLambdaLayerProp } from "../../config/types/TsgLambdaLayerProp";
|
|
4
|
+
import { AppConfig } from "../../config/AppConfig";
|
|
5
|
+
|
|
6
|
+
export const createCommonLayer = (scope: Construct, appProps: AppConfig) => {
|
|
7
|
+
|
|
8
|
+
const prop: TsgLambdaLayerProp = {
|
|
9
|
+
description: 'Common Lambda layer',
|
|
10
|
+
codePath: './node_modules/@davissylvester/bishop-cdk-constructs/dist',
|
|
11
|
+
name: 'bishop-common-layer'
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const layer = createLambdaLayer(scope, appProps, prop)
|
|
15
|
+
|
|
16
|
+
return layer;
|
|
17
|
+
};
|
|
18
|
+
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { IRestApi, LambdaIntegration, Resource, TokenAuthorizer } from "aws-cdk-lib/aws-apigateway";
|
|
2
|
+
import { NodejsFunction } from "aws-cdk-lib/aws-lambda-nodejs";
|
|
3
|
+
import { TsgLambdaProp } from "../../config/types";
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
export class Routes {
|
|
7
|
+
|
|
8
|
+
public static Resources: Resource[] = [];
|
|
9
|
+
private static routeMap = new Map();
|
|
10
|
+
|
|
11
|
+
public static createResource(
|
|
12
|
+
prop: TsgLambdaProp,
|
|
13
|
+
api: IRestApi,
|
|
14
|
+
lambdaNode: NodejsFunction,
|
|
15
|
+
authorizer?: TokenAuthorizer) {
|
|
16
|
+
|
|
17
|
+
const routeMap: Map<string, Resource> = new Map();
|
|
18
|
+
|
|
19
|
+
// Only attach lambda to an Api Gateway if a route exist
|
|
20
|
+
if (prop.apiGateway?.route) {
|
|
21
|
+
|
|
22
|
+
// Note: this now uses the bundle version as the first segment in the path.
|
|
23
|
+
let activeRoutePath = `/${(prop.apiGateway.version) ? prop.apiGateway.version : 1 }`;
|
|
24
|
+
let activeResource: Resource = Routes.routeMap.get(activeRoutePath) || api.root.addResource(activeRoutePath);
|
|
25
|
+
Routes.routeMap.set(activeRoutePath, activeResource);
|
|
26
|
+
|
|
27
|
+
// Now we go through our route segments creating the rest of the path.
|
|
28
|
+
const pathSegments = prop.apiGateway?.route.split("/").filter(x => (x));
|
|
29
|
+
for (let i = 0; i < pathSegments.length; i++) {
|
|
30
|
+
activeRoutePath = `${activeRoutePath}/${pathSegments[i]}`;
|
|
31
|
+
let secondaryResource = Routes.routeMap.get(activeRoutePath) || activeResource!.addResource(pathSegments[i]);
|
|
32
|
+
Routes.routeMap.set(activeRoutePath, secondaryResource);
|
|
33
|
+
activeResource = secondaryResource;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Finally, we attach our function to the last resource
|
|
37
|
+
activeResource.addMethod(prop.apiGateway.method || 'GET',
|
|
38
|
+
new LambdaIntegration(lambdaNode, { proxy: true, }),
|
|
39
|
+
prop.apiGateway.secure ? { authorizer } : undefined);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { CfnOutput, Duration } from "aws-cdk-lib";
|
|
2
|
+
import { TokenAuthorizer } from "aws-cdk-lib/aws-apigateway";
|
|
3
|
+
import { Alarm, ComparisonOperator, IAlarmAction } from "aws-cdk-lib/aws-cloudwatch";
|
|
4
|
+
import { IRole, ManagedPolicy } from "aws-cdk-lib/aws-iam";
|
|
5
|
+
import { LayerVersion } from "aws-cdk-lib/aws-lambda";
|
|
6
|
+
import { NodejsFunction, NodejsFunctionProps, SourceMapMode } from "aws-cdk-lib/aws-lambda-nodejs";
|
|
7
|
+
import { Construct } from "constructs";
|
|
8
|
+
import * as path from 'path';
|
|
9
|
+
import { AppConfig } from "../../config/AppConfig";
|
|
10
|
+
import { TsgLambdaProp } from "../../config/types";
|
|
11
|
+
|
|
12
|
+
import { TsgLambdaProps } from "../../config/types/TsgLambdaProps";
|
|
13
|
+
import { CreateLambdaFunctionInput } from "../../interfaces/CreateLambdaFunctionInput";
|
|
14
|
+
import { BaseResource } from "../base/baseResource";
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
export class CreateLambda extends BaseResource<NodejsFunction> {
|
|
18
|
+
|
|
19
|
+
public Lambdas: NodejsFunction[] = [];
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
constructor(private props: TsgLambdaProps, config: AppConfig) {
|
|
23
|
+
super(props.scope, config);
|
|
24
|
+
|
|
25
|
+
const resources = this.createResource(props.scope);
|
|
26
|
+
|
|
27
|
+
this.Lambdas = [...resources];
|
|
28
|
+
|
|
29
|
+
this.createAlarmsForLambdas(this.Lambdas);
|
|
30
|
+
|
|
31
|
+
this.createOutput(props.scope, resources);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
protected createResource(scope: Construct): NodejsFunction[] {
|
|
35
|
+
|
|
36
|
+
const result = this.createLambdas(this.props);
|
|
37
|
+
|
|
38
|
+
return result;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
protected createOutput<T>(scope: Construct, createdAssets: T[]): void {
|
|
42
|
+
createdAssets.forEach((x, idx) => {
|
|
43
|
+
|
|
44
|
+
new CfnOutput(scope, `lambda-${idx}`, {
|
|
45
|
+
// @ts-ignore
|
|
46
|
+
value: x.functionName
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
private createLambdas(props: TsgLambdaProps): NodejsFunction[] {
|
|
52
|
+
|
|
53
|
+
const createdLambdas: NodejsFunction[] = this.createLambdaFunctions(this.scope, props.role, props.layers);
|
|
54
|
+
|
|
55
|
+
return createdLambdas;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
private createLambdaFunctions(scope: Construct, role?: IRole, layers?: LayerVersion[]) {
|
|
59
|
+
|
|
60
|
+
//console.log("ENTER createLambdaFunctions");
|
|
61
|
+
const createdLambdas = this.props.prop.RESOURCES.LAMBDA.map((config:TsgLambdaProp) => {
|
|
62
|
+
|
|
63
|
+
//console.log("ENTER createLambdaFunctions.map for " + config.name);
|
|
64
|
+
let lambdaProps = this.createLambdaProps(config, role, layers);
|
|
65
|
+
|
|
66
|
+
const lambdaId = CreateLambda.getIdForLambda(config);
|
|
67
|
+
let fctn = new NodejsFunction(scope, lambdaId, lambdaProps);
|
|
68
|
+
|
|
69
|
+
// If we have managed policies, we add them.
|
|
70
|
+
if (config.managedPolicies && config.managedPolicies?.length > 0) {
|
|
71
|
+
|
|
72
|
+
this.assignManagedPolicies(fctn, config.managedPolicies);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return fctn;
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
return createdLambdas || [];
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
private createLambdaProps(prop: TsgLambdaProp, role?: IRole, layers?: LayerVersion[], props?: TsgLambdaProps) {
|
|
82
|
+
|
|
83
|
+
return this.createLambdaFunctionProps({
|
|
84
|
+
prop,
|
|
85
|
+
role,
|
|
86
|
+
layers,
|
|
87
|
+
props
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
private createLambdaFunctionProps(props: CreateLambdaFunctionInput) {
|
|
92
|
+
const { prop, role, layers } = props;
|
|
93
|
+
|
|
94
|
+
const lambdaProp: NodejsFunctionProps = {
|
|
95
|
+
entry: path.join(prop.codePath),
|
|
96
|
+
functionName: `${this.props.appConfig.AppPrefix}-${prop.name}`,
|
|
97
|
+
handler: prop.handler,
|
|
98
|
+
runtime: prop.runtime || this.config.GLOBALS.stackRuntime,
|
|
99
|
+
timeout: prop.duration || Duration.minutes(2),
|
|
100
|
+
memorySize: prop.memory || 512,
|
|
101
|
+
environment: {
|
|
102
|
+
"VERBOSE_LOGGING": "true",
|
|
103
|
+
...prop.environment
|
|
104
|
+
},
|
|
105
|
+
bundling: {
|
|
106
|
+
minify: false,
|
|
107
|
+
target: 'es2020',
|
|
108
|
+
sourceMap: true,
|
|
109
|
+
sourceMapMode: SourceMapMode.INLINE,
|
|
110
|
+
environment: prop.environment || prop.environment,
|
|
111
|
+
},
|
|
112
|
+
role,
|
|
113
|
+
layers
|
|
114
|
+
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
return lambdaProp;
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
private assignManagedPolicies(lambda: NodejsFunction, managedPolicyNames: string[]) {
|
|
122
|
+
|
|
123
|
+
managedPolicyNames.forEach((managedPolicyName: string) => {
|
|
124
|
+
|
|
125
|
+
let policy = ManagedPolicy.fromAwsManagedPolicyName(managedPolicyName);
|
|
126
|
+
|
|
127
|
+
lambda.role?.addManagedPolicy(policy);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
private createAlarmsForLambdas(lambdas: NodejsFunction[]) {
|
|
133
|
+
|
|
134
|
+
lambdas.forEach((lambda, idx) => {
|
|
135
|
+
|
|
136
|
+
const errorMetric = lambda.metricErrors({
|
|
137
|
+
period: Duration.minutes(3),
|
|
138
|
+
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
const durationMetric = lambda.metricDuration({
|
|
142
|
+
period: Duration.minutes(3),
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
const invocationMetric = lambda.metricInvocations({
|
|
146
|
+
period: Duration.minutes(3),
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
new Alarm(this.props.scope, `${this.config.AppPrefix}-${idx}-error-alarm`, {
|
|
150
|
+
metric: errorMetric,
|
|
151
|
+
threshold: 5,
|
|
152
|
+
comparisonOperator: ComparisonOperator.GREATER_THAN_OR_EQUAL_TO_THRESHOLD,
|
|
153
|
+
evaluationPeriods: 3,
|
|
154
|
+
alarmDescription: `${this.config.AppPrefix}-${idx} errors over 3 min period`,
|
|
155
|
+
alarmName: `${this.config.AppPrefix}-${idx}-error-alarm`
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
new Alarm(this.props.scope, `${this.config.AppPrefix}-${idx}-duration-alarm`, {
|
|
159
|
+
metric: durationMetric,
|
|
160
|
+
threshold: 1,
|
|
161
|
+
comparisonOperator: ComparisonOperator.GREATER_THAN_OR_EQUAL_TO_THRESHOLD,
|
|
162
|
+
evaluationPeriods: 3,
|
|
163
|
+
alarmDescription: `${this.config.AppPrefix}-${idx} duration errors over 3 min period`,
|
|
164
|
+
alarmName: `${this.config.AppPrefix}-${idx}-duration-alarm`
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
const invocationAlarm = new Alarm(this.props.scope, `${this.config.AppPrefix}-${idx}-invocation-alarm`, {
|
|
168
|
+
metric: errorMetric,
|
|
169
|
+
threshold: 1000,
|
|
170
|
+
comparisonOperator: ComparisonOperator.GREATER_THAN_OR_EQUAL_TO_THRESHOLD,
|
|
171
|
+
evaluationPeriods: 3,
|
|
172
|
+
alarmDescription: `${this.config.AppPrefix}-${idx} errors over 3 min period`,
|
|
173
|
+
alarmName: `${this.config.AppPrefix}-${idx}-invocation-Metric-alarm`
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
// const alarmAction: IAlarmAction = {};
|
|
177
|
+
// invocationAlarm.addAlarmAction(alarmAction);
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
public static getIdForLambda(lambdaProp: TsgLambdaProp) {
|
|
182
|
+
return `${lambdaProp.name}`.toLowerCase();
|
|
183
|
+
}
|
|
184
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Code, LayerVersion, LayerVersionProps, Runtime } from "aws-cdk-lib/aws-lambda";
|
|
2
|
+
import { Construct } from "constructs";
|
|
3
|
+
import { TsgLambdaLayerProp } from "../../config/types/TsgLambdaLayerProp";
|
|
4
|
+
import { MicroserviceProps } from "../../interfaces/MicroserviceProps";
|
|
5
|
+
import * as path from "path";
|
|
6
|
+
|
|
7
|
+
export const createLambdaLayer = (scope: Construct, globalProps: MicroserviceProps, prop: TsgLambdaLayerProp) => {
|
|
8
|
+
|
|
9
|
+
const propResult = createLambdaLayerProps(prop, globalProps);
|
|
10
|
+
|
|
11
|
+
const layer = new LayerVersion(scope, `${globalProps.GLOBALS.name}-common-layer`, propResult);
|
|
12
|
+
|
|
13
|
+
return layer;
|
|
14
|
+
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const createLambdaLayerProps = (prop: TsgLambdaLayerProp, props: MicroserviceProps) => {
|
|
18
|
+
|
|
19
|
+
const baseProps = baseLayerProps(props, prop.description);
|
|
20
|
+
|
|
21
|
+
const baseLayer: LayerVersionProps = {
|
|
22
|
+
...baseProps,
|
|
23
|
+
code: Code.fromAsset(path.join(prop.codePath),),
|
|
24
|
+
description: prop.description,
|
|
25
|
+
layerVersionName: `${prop.name}`
|
|
26
|
+
};
|
|
27
|
+
return baseLayer;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const baseLayerProps = (prop: MicroserviceProps, desc: string) => {
|
|
31
|
+
|
|
32
|
+
const baseLayerProps: LayerVersionProps = {
|
|
33
|
+
code: Code.fromAsset(path.join(''),),
|
|
34
|
+
compatibleRuntimes: [prop.GLOBALS.stackRuntime],
|
|
35
|
+
license: 'Apache-2.0',
|
|
36
|
+
description: desc,
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
return baseLayerProps;
|
|
40
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@davissylvester/bishop-common",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Common Layers for Lambdas",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"test": "jest && exit 1"
|
|
8
|
+
},
|
|
9
|
+
"keywords": [],
|
|
10
|
+
"author": "Davis Sylvester <Davis@sylvesterllc.com>",
|
|
11
|
+
"license": "ISC",
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"@aws-sdk/client-dynamodb": "^3.137.0",
|
|
14
|
+
"@aws-sdk/lib-dynamodb": "^3.145.0",
|
|
15
|
+
"@aws-sdk/util-dynamodb": "^3.137.0",
|
|
16
|
+
"luxon": "^3.0.4",
|
|
17
|
+
"uuidv4": "^6.2.13"
|
|
18
|
+
},
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"@types/aws-lambda": "^8.10.111"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./responses";
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export const badResponse = (errorMessage: string) => {
|
|
2
|
+
return {
|
|
3
|
+
statusCode: 400,
|
|
4
|
+
body: JSON.stringify({
|
|
5
|
+
message: errorMessage
|
|
6
|
+
}),
|
|
7
|
+
headers: {
|
|
8
|
+
"Access-Control-Allow-Origin": "*",
|
|
9
|
+
"Access-Control-Allow-Credentials": true,
|
|
10
|
+
},
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export const okResponse = (data: Object) => {
|
|
16
|
+
return {
|
|
17
|
+
statusCode: 200,
|
|
18
|
+
body: JSON.stringify({
|
|
19
|
+
data
|
|
20
|
+
}),
|
|
21
|
+
headers: {
|
|
22
|
+
"Access-Control-Allow-Origin": "*",
|
|
23
|
+
"Access-Control-Allow-Credentials": true,
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export const unauthorizedResponse = (data: object) => {
|
|
30
|
+
return {
|
|
31
|
+
statusCode: 401,
|
|
32
|
+
body: JSON.stringify({
|
|
33
|
+
data
|
|
34
|
+
}),
|
|
35
|
+
headers: {
|
|
36
|
+
"Access-Control-Allow-Origin": "*",
|
|
37
|
+
"Access-Control-Allow-Credentials": true,
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { ILoggerContext } from "./IContext";
|
|
2
|
+
|
|
3
|
+
export function logger(context: ILoggerContext) {
|
|
4
|
+
|
|
5
|
+
const showLogs = Boolean(process.env.VERBOSE_LOGGING || false);
|
|
6
|
+
|
|
7
|
+
if (showLogs) {
|
|
8
|
+
console.log(`### ${(context.functionName) ? `${context.functionName} :: ` : ''} ${(context.name) ? `${context.name} :: ` : ''} ${(context.message) ? `${context.message} :: ` : ''}`);
|
|
9
|
+
}
|
|
10
|
+
// ### run() ::
|
|
11
|
+
}
|