@digitraffic/common 2022.10.25-1 → 2022.10.31-1
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/.editorconfig +9 -0
- package/.eslintignore +4 -0
- package/.eslintrc.json +27 -0
- package/.github/CODEOWNERS +2 -0
- package/.github/workflows/build.yml +36 -0
- package/.github/workflows/eslint.yml +38 -0
- package/.github/workflows/mirror.yml +15 -0
- package/.gitignore +29 -0
- package/.husky/pre-commit +4 -0
- package/.prettierrc.json +10 -0
- package/dist/aws/infra/api/integration.js +52 -0
- package/dist/aws/infra/api/response.js +61 -0
- package/dist/aws/infra/api/responses.js +82 -0
- package/dist/aws/infra/api/static-integration.js +54 -0
- package/dist/aws/infra/canaries/canary-alarm.js +26 -0
- package/dist/aws/infra/canaries/canary-keys.js +7 -0
- package/dist/aws/infra/canaries/canary-parameters.js +3 -0
- package/dist/aws/infra/canaries/canary-role.js +46 -0
- package/dist/aws/infra/canaries/canary.js +32 -0
- package/dist/aws/infra/canaries/database-canary.js +70 -0
- package/dist/aws/infra/canaries/database-checker.js +103 -0
- package/dist/aws/infra/canaries/url-canary.js +47 -0
- package/dist/aws/infra/canaries/url-checker.js +252 -0
- package/dist/aws/infra/documentation.js +95 -0
- package/dist/aws/infra/scheduler.js +31 -0
- package/dist/aws/infra/security-rule.js +39 -0
- package/dist/aws/infra/sqs-integration.js +93 -0
- package/dist/aws/infra/sqs-queue.js +130 -0
- package/dist/aws/infra/stack/lambda-configs.js +105 -0
- package/dist/aws/infra/stack/monitoredfunction.js +143 -0
- package/dist/aws/infra/stack/rest_apis.js +185 -0
- package/dist/aws/infra/stack/stack-checking-aspect.js +174 -0
- package/dist/aws/infra/stack/stack.js +67 -0
- package/dist/aws/infra/stack/subscription.js +42 -0
- package/dist/aws/infra/usage-plans.js +42 -0
- package/dist/aws/runtime/apikey.js +13 -0
- package/dist/aws/runtime/digitraffic-integration-response.js +26 -0
- package/dist/aws/runtime/environment.js +12 -0
- package/dist/aws/runtime/messaging.js +31 -0
- package/dist/aws/runtime/s3.js +30 -0
- package/dist/aws/runtime/secrets/dbsecret.js +96 -0
- package/dist/aws/runtime/secrets/proxy-holder.js +27 -0
- package/dist/aws/runtime/secrets/rds-holder.js +27 -0
- package/dist/aws/runtime/secrets/secret-holder.js +76 -0
- package/dist/aws/runtime/secrets/secret.js +43 -0
- package/dist/aws/types/errors.js +16 -0
- package/dist/aws/types/lambda-response.js +33 -0
- package/dist/aws/types/mediatypes.js +16 -0
- package/dist/aws/types/model-with-reference.js +3 -0
- package/dist/aws/types/proxytypes.js +3 -0
- package/dist/aws/types/tags.js +7 -0
- package/dist/database/cached.js +32 -0
- package/dist/database/database.js +70 -0
- package/dist/database/last-updated.js +54 -0
- package/dist/database/models.js +3 -0
- package/dist/marine/id_utils.js +33 -0
- package/dist/marine/rtz.js +3 -0
- package/dist/test/asserter.js +45 -0
- package/dist/test/db-testutils.js +31 -0
- package/dist/test/httpserver.js +74 -0
- package/dist/test/secret.js +25 -0
- package/dist/test/secrets-manager.js +59 -0
- package/dist/test/testutils.js +44 -0
- package/dist/types/either.js +3 -0
- package/dist/types/input-error.js +7 -0
- package/dist/types/language.js +10 -0
- package/dist/types/traffictype.js +13 -0
- package/dist/types/validator.js +14 -0
- package/dist/utils/api-model.js +129 -0
- package/dist/utils/base64.js +21 -0
- package/dist/utils/date-utils.js +34 -0
- package/dist/utils/geojson-types.js +18 -0
- package/dist/utils/geometry.js +164 -0
- package/dist/utils/retry.js +50 -0
- package/dist/utils/slack.js +25 -0
- package/dist/utils/utils.js +75 -0
- package/jest.config.js +15 -0
- package/package.json +15 -13
- package/src/@types/geojson-validation/index.d.ts +4 -0
- package/src/aws/infra/api/integration.ts +73 -0
- package/src/aws/infra/api/response.ts +67 -0
- package/src/aws/infra/api/responses.ts +124 -0
- package/src/aws/infra/api/static-integration.ts +62 -0
- package/src/aws/infra/canaries/canary-alarm.ts +31 -0
- package/src/aws/infra/canaries/canary-keys.ts +3 -0
- package/{aws/infra/canaries/canary-parameters.d.ts → src/aws/infra/canaries/canary-parameters.ts} +7 -6
- package/src/aws/infra/canaries/canary-role.ts +47 -0
- package/src/aws/infra/canaries/canary.ts +46 -0
- package/src/aws/infra/canaries/database-canary.ts +98 -0
- package/src/aws/infra/canaries/database-checker.ts +155 -0
- package/src/aws/infra/canaries/url-canary.ts +74 -0
- package/src/aws/infra/canaries/url-checker.ts +366 -0
- package/src/aws/infra/documentation.ts +124 -0
- package/src/aws/infra/scheduler.ts +59 -0
- package/src/aws/infra/security-rule.ts +38 -0
- package/src/aws/infra/sqs-integration.ts +102 -0
- package/src/aws/infra/sqs-queue.ts +148 -0
- package/src/aws/infra/stack/lambda-configs.ts +207 -0
- package/src/aws/infra/stack/monitoredfunction.ts +342 -0
- package/src/aws/infra/stack/rest_apis.ts +223 -0
- package/src/aws/infra/stack/stack-checking-aspect.ts +279 -0
- package/src/aws/infra/stack/stack.ts +145 -0
- package/src/aws/infra/stack/subscription.ts +58 -0
- package/src/aws/infra/usage-plans.ts +41 -0
- package/src/aws/runtime/apikey.ts +9 -0
- package/src/aws/runtime/digitraffic-integration-response.ts +28 -0
- package/src/aws/runtime/environment.ts +9 -0
- package/src/aws/runtime/messaging.ts +26 -0
- package/src/aws/runtime/s3.ts +44 -0
- package/src/aws/runtime/secrets/dbsecret.ts +116 -0
- package/src/aws/runtime/secrets/proxy-holder.ts +37 -0
- package/src/aws/runtime/secrets/rds-holder.ts +33 -0
- package/src/aws/runtime/secrets/secret-holder.ts +116 -0
- package/src/aws/runtime/secrets/secret.ts +50 -0
- package/src/aws/types/errors.ts +14 -0
- package/src/aws/types/lambda-response.ts +43 -0
- package/{aws/types/mediatypes.d.ts → src/aws/types/mediatypes.ts} +4 -3
- package/{aws/types/model-with-reference.d.ts → src/aws/types/model-with-reference.ts} +2 -1
- package/src/aws/types/proxytypes.ts +27 -0
- package/src/aws/types/tags.ts +3 -0
- package/src/database/cached.ts +35 -0
- package/src/database/database.ts +96 -0
- package/src/database/last-updated.ts +59 -0
- package/{database/models.d.ts → src/database/models.ts} +1 -0
- package/src/marine/id_utils.ts +30 -0
- package/src/marine/rtz.ts +57 -0
- package/src/test/asserter.ts +48 -0
- package/src/test/db-testutils.ts +44 -0
- package/src/test/httpserver.ts +96 -0
- package/src/test/secret.ts +23 -0
- package/src/test/secrets-manager.ts +34 -0
- package/src/test/testutils.ts +39 -0
- package/src/types/either.ts +3 -0
- package/src/types/input-error.ts +2 -0
- package/src/types/language.ts +3 -0
- package/src/types/traffictype.ts +8 -0
- package/src/types/validator.ts +10 -0
- package/src/utils/api-model.ts +133 -0
- package/src/utils/base64.ts +16 -0
- package/src/utils/date-utils.ts +30 -0
- package/src/utils/geojson-types.ts +22 -0
- package/src/utils/geometry.ts +164 -0
- package/src/utils/retry.ts +49 -0
- package/src/utils/slack.ts +22 -0
- package/src/utils/utils.ts +105 -0
- package/test/marine/id_utils.test.ts +57 -0
- package/test/promise/promise.test.ts +143 -0
- package/test/secrets/dbsecret.test.ts +59 -0
- package/test/secrets/secret-holder.test.ts +143 -0
- package/test/secrets/secret.test.ts +49 -0
- package/test/test/httpserver.test.ts +128 -0
- package/test/utils/date-utils.test.ts +28 -0
- package/test/utils/geometry.test.ts +29 -0
- package/test/utils/utils.test.ts +64 -0
- package/tsconfig.eslint.json +4 -0
- package/tsconfig.json +22 -0
- package/yarn.lock +4060 -0
- package/aws/infra/api/integration.d.ts +0 -21
- package/aws/infra/api/integration.js +0 -52
- package/aws/infra/api/response.d.ts +0 -22
- package/aws/infra/api/response.js +0 -61
- package/aws/infra/api/responses.d.ts +0 -39
- package/aws/infra/api/responses.js +0 -79
- package/aws/infra/api/static-integration.d.ts +0 -15
- package/aws/infra/api/static-integration.js +0 -54
- package/aws/infra/canaries/canary-alarm.d.ts +0 -6
- package/aws/infra/canaries/canary-alarm.js +0 -26
- package/aws/infra/canaries/canary-parameters.js +0 -3
- package/aws/infra/canaries/canary-role.d.ts +0 -6
- package/aws/infra/canaries/canary-role.js +0 -46
- package/aws/infra/canaries/canary.d.ts +0 -8
- package/aws/infra/canaries/canary.js +0 -32
- package/aws/infra/canaries/database-canary.d.ts +0 -18
- package/aws/infra/canaries/database-canary.js +0 -55
- package/aws/infra/canaries/database-checker.d.ts +0 -21
- package/aws/infra/canaries/database-checker.js +0 -109
- package/aws/infra/canaries/url-canary.d.ts +0 -19
- package/aws/infra/canaries/url-canary.js +0 -46
- package/aws/infra/canaries/url-checker.d.ts +0 -46
- package/aws/infra/canaries/url-checker.js +0 -238
- package/aws/infra/documentation.d.ts +0 -56
- package/aws/infra/documentation.js +0 -95
- package/aws/infra/scheduler.d.ts +0 -12
- package/aws/infra/scheduler.js +0 -31
- package/aws/infra/security-rule.d.ts +0 -12
- package/aws/infra/security-rule.js +0 -39
- package/aws/infra/sqs-integration.d.ts +0 -7
- package/aws/infra/sqs-integration.js +0 -93
- package/aws/infra/sqs-queue.d.ts +0 -16
- package/aws/infra/sqs-queue.js +0 -130
- package/aws/infra/stack/lambda-configs.d.ts +0 -72
- package/aws/infra/stack/lambda-configs.js +0 -93
- package/aws/infra/stack/monitoredfunction.d.ts +0 -84
- package/aws/infra/stack/monitoredfunction.js +0 -135
- package/aws/infra/stack/rest_apis.d.ts +0 -41
- package/aws/infra/stack/rest_apis.js +0 -185
- package/aws/infra/stack/stack-checking-aspect.d.ts +0 -21
- package/aws/infra/stack/stack-checking-aspect.js +0 -174
- package/aws/infra/stack/stack.d.ts +0 -44
- package/aws/infra/stack/stack.js +0 -60
- package/aws/infra/stack/subscription.d.ts +0 -17
- package/aws/infra/stack/subscription.js +0 -41
- package/aws/infra/usage-plans.d.ts +0 -15
- package/aws/infra/usage-plans.js +0 -42
- package/aws/runtime/apikey.d.ts +0 -2
- package/aws/runtime/apikey.js +0 -13
- package/aws/runtime/digitraffic-integration-response.d.ts +0 -8
- package/aws/runtime/digitraffic-integration-response.js +0 -26
- package/aws/runtime/environment.d.ts +0 -1
- package/aws/runtime/environment.js +0 -12
- package/aws/runtime/messaging.d.ts +0 -10
- package/aws/runtime/messaging.js +0 -31
- package/aws/runtime/s3.d.ts +0 -2
- package/aws/runtime/s3.js +0 -30
- package/aws/runtime/secrets/dbsecret.d.ts +0 -54
- package/aws/runtime/secrets/dbsecret.js +0 -96
- package/aws/runtime/secrets/proxy-holder.d.ts +0 -9
- package/aws/runtime/secrets/proxy-holder.js +0 -26
- package/aws/runtime/secrets/rds-holder.d.ts +0 -9
- package/aws/runtime/secrets/rds-holder.js +0 -26
- package/aws/runtime/secrets/secret-holder.d.ts +0 -26
- package/aws/runtime/secrets/secret-holder.js +0 -73
- package/aws/runtime/secrets/secret.d.ts +0 -8
- package/aws/runtime/secrets/secret.js +0 -43
- package/aws/types/errors.d.ts +0 -4
- package/aws/types/errors.js +0 -9
- package/aws/types/lambda-response.d.ts +0 -12
- package/aws/types/lambda-response.js +0 -28
- package/aws/types/mediatypes.js +0 -15
- package/aws/types/model-with-reference.js +0 -3
- package/aws/types/proxytypes.d.ts +0 -26
- package/aws/types/proxytypes.js +0 -3
- package/aws/types/tags.d.ts +0 -2
- package/aws/types/tags.js +0 -7
- package/database/cached.d.ts +0 -7
- package/database/cached.js +0 -32
- package/database/database.d.ts +0 -19
- package/database/database.js +0 -62
- package/database/last-updated.d.ts +0 -16
- package/database/last-updated.js +0 -54
- package/database/models.js +0 -3
- package/index.d.ts +0 -1
- package/index.js +0 -18
- package/marine/id_utils.d.ts +0 -3
- package/marine/id_utils.js +0 -33
- package/marine/rtz.d.ts +0 -48
- package/marine/rtz.js +0 -3
- package/test/asserter.d.ts +0 -11
- package/test/asserter.js +0 -45
- package/test/db-testutils.d.ts +0 -2
- package/test/db-testutils.js +0 -31
- package/test/httpserver.d.ts +0 -18
- package/test/httpserver.js +0 -67
- package/test/secret.d.ts +0 -3
- package/test/secret.js +0 -25
- package/test/secrets-manager.d.ts +0 -9
- package/test/secrets-manager.js +0 -59
- package/test/testutils.d.ts +0 -12
- package/test/testutils.js +0 -44
- package/types/input-error.d.ts +0 -2
- package/types/input-error.js +0 -7
- package/types/language.d.ts +0 -5
- package/types/language.js +0 -10
- package/types/traffictype.d.ts +0 -8
- package/types/traffictype.js +0 -13
- package/types/validator.d.ts +0 -4
- package/types/validator.js +0 -14
- package/utils/api-model.d.ts +0 -87
- package/utils/api-model.js +0 -129
- package/utils/base64.d.ts +0 -12
- package/utils/base64.js +0 -21
- package/utils/date-utils.d.ts +0 -17
- package/utils/date-utils.js +0 -34
- package/utils/geojson-types.d.ts +0 -14
- package/utils/geojson-types.js +0 -18
- package/utils/geometry.d.ts +0 -36
- package/utils/geometry.js +0 -140
- package/utils/retry.d.ts +0 -13
- package/utils/retry.js +0 -50
- package/utils/slack.d.ts +0 -5
- package/utils/slack.js +0 -25
- package/utils/utils.d.ts +0 -30
- package/utils/utils.js +0 -64
@@ -0,0 +1,102 @@
|
|
1
|
+
import {Aws} from "aws-cdk-lib";
|
2
|
+
import {AwsIntegration, PassthroughBehavior, RequestValidator, Resource} from "aws-cdk-lib/aws-apigateway";
|
3
|
+
import {Queue} from "aws-cdk-lib/aws-sqs";
|
4
|
+
import {PolicyStatement, Role, ServicePrincipal} from "aws-cdk-lib/aws-iam";
|
5
|
+
import {IModel} from "aws-cdk-lib/aws-apigateway/lib/model";
|
6
|
+
import {Construct} from "constructs";
|
7
|
+
|
8
|
+
export function attachQueueToApiGatewayResource(
|
9
|
+
stack: Construct,
|
10
|
+
queue: Queue,
|
11
|
+
resource: Resource,
|
12
|
+
requestValidator: RequestValidator,
|
13
|
+
resourceName: string,
|
14
|
+
apiKeyRequired: boolean,
|
15
|
+
requestModels?: {[param: string]: IModel},
|
16
|
+
) {
|
17
|
+
// role for API Gateway
|
18
|
+
const apiGwRole = new Role(stack, `${resourceName}APIGatewayToSQSRole`, {
|
19
|
+
assumedBy: new ServicePrincipal('apigateway.amazonaws.com'),
|
20
|
+
});
|
21
|
+
// grants API Gateway the right to send SQS messages
|
22
|
+
apiGwRole.addToPolicy(new PolicyStatement({
|
23
|
+
resources: [
|
24
|
+
queue.queueArn,
|
25
|
+
],
|
26
|
+
actions: [
|
27
|
+
'sqs:SendMessage',
|
28
|
+
],
|
29
|
+
}));
|
30
|
+
// grants API Gateway the right write CloudWatch Logs
|
31
|
+
apiGwRole.addToPolicy(new PolicyStatement({
|
32
|
+
resources: [
|
33
|
+
'*',
|
34
|
+
],
|
35
|
+
actions: [
|
36
|
+
'logs:CreateLogGroup',
|
37
|
+
'logs:CreateLogStream',
|
38
|
+
'logs:DescribeLogGroups',
|
39
|
+
'logs:DescribeLogStreams',
|
40
|
+
'logs:PutLogEvents',
|
41
|
+
'logs:GetLogEvents',
|
42
|
+
'logs:FilterLogEvents',
|
43
|
+
],
|
44
|
+
}));
|
45
|
+
// create an integration between API Gateway and an SQS queue
|
46
|
+
const fifoMessageGroupId = queue.fifo ? '&MessageGroupId=AlwaysSameFifoGroup' : '';
|
47
|
+
const sqsIntegration = new AwsIntegration({
|
48
|
+
service: 'sqs',
|
49
|
+
integrationHttpMethod: 'POST',
|
50
|
+
options: {
|
51
|
+
passthroughBehavior: PassthroughBehavior.NEVER,
|
52
|
+
credentialsRole: apiGwRole,
|
53
|
+
requestParameters: {
|
54
|
+
// SQS requires the Content-Type of the HTTP request to be application/x-www-form-urlencoded
|
55
|
+
'integration.request.header.Content-Type': "'application/x-www-form-urlencoded'",
|
56
|
+
},
|
57
|
+
requestTemplates: {
|
58
|
+
// map the JSON request to a form parameter, FIFO needs also MessageGroupId
|
59
|
+
// https://docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReference/API_SendMessage.html
|
60
|
+
'application/json': `Action=SendMessage${fifoMessageGroupId}&MessageBody=$util.urlEncode($input.body)`,
|
61
|
+
},
|
62
|
+
// these are required by SQS
|
63
|
+
integrationResponses: [
|
64
|
+
{
|
65
|
+
statusCode: '200',
|
66
|
+
responseTemplates: {
|
67
|
+
'text/html': 'Success',
|
68
|
+
},
|
69
|
+
},
|
70
|
+
{
|
71
|
+
statusCode: '500',
|
72
|
+
responseTemplates: {
|
73
|
+
'text/html': 'Error',
|
74
|
+
},
|
75
|
+
selectionPattern: '500',
|
76
|
+
},
|
77
|
+
|
78
|
+
],
|
79
|
+
|
80
|
+
},
|
81
|
+
path: `${Aws.ACCOUNT_ID}/${queue.queueName}`,
|
82
|
+
});
|
83
|
+
resource.addMethod('POST', sqsIntegration, {
|
84
|
+
requestValidator,
|
85
|
+
apiKeyRequired,
|
86
|
+
requestModels: requestModels ?? {},
|
87
|
+
methodResponses: [
|
88
|
+
{
|
89
|
+
statusCode: '200',
|
90
|
+
responseParameters: {
|
91
|
+
'method.response.header.Content-Type': true,
|
92
|
+
},
|
93
|
+
},
|
94
|
+
{
|
95
|
+
statusCode: '500',
|
96
|
+
responseParameters: {
|
97
|
+
'method.response.header.Content-Type': true,
|
98
|
+
},
|
99
|
+
},
|
100
|
+
],
|
101
|
+
});
|
102
|
+
}
|
@@ -0,0 +1,148 @@
|
|
1
|
+
import {Queue, QueueEncryption, QueueProps} from "aws-cdk-lib/aws-sqs";
|
2
|
+
import {Duration} from "aws-cdk-lib";
|
3
|
+
import {BlockPublicAccess, Bucket} from "aws-cdk-lib/aws-s3";
|
4
|
+
import {PolicyStatement} from "aws-cdk-lib/aws-iam";
|
5
|
+
import {InlineCode, Runtime} from "aws-cdk-lib/aws-lambda";
|
6
|
+
import {RetentionDays} from "aws-cdk-lib/aws-logs";
|
7
|
+
import {SqsEventSource} from "aws-cdk-lib/aws-lambda-event-sources";
|
8
|
+
import {ComparisonOperator, TreatMissingData} from "aws-cdk-lib/aws-cloudwatch";
|
9
|
+
import {SnsAction} from "aws-cdk-lib/aws-cloudwatch-actions";
|
10
|
+
import {ManagedUpload} from "aws-sdk/clients/s3";
|
11
|
+
import {S3} from "aws-sdk";
|
12
|
+
import {SQSEvent, SQSHandler, SQSRecord} from "aws-lambda";
|
13
|
+
import {Construct} from "constructs";
|
14
|
+
import {DigitrafficStack} from "./stack/stack";
|
15
|
+
import {MonitoredFunction} from "./stack/monitoredfunction";
|
16
|
+
|
17
|
+
/**
|
18
|
+
* Construct for creating SQS-queues.
|
19
|
+
*
|
20
|
+
* If you don't config your own deadLetterQueue, this will create a dlq for you, also a lambda function, a s3 bucket
|
21
|
+
* and an alarm for the queue. Anything that goes to the dlq will be written into the bucket and the alarm is activated.
|
22
|
+
*/
|
23
|
+
export class DigitrafficSqsQueue extends Queue {
|
24
|
+
constructor(scope: Construct, name: string, props: QueueProps) {
|
25
|
+
super(scope, name, props);
|
26
|
+
}
|
27
|
+
|
28
|
+
static create(stack: DigitrafficStack, name: string, props: QueueProps): DigitrafficSqsQueue {
|
29
|
+
const queueName = `${stack.configuration.shortName}-${name}-Queue`;
|
30
|
+
const queueProps = {...props, ...{
|
31
|
+
encryption: QueueEncryption.KMS_MANAGED,
|
32
|
+
queueName,
|
33
|
+
deadLetterQueue: props.deadLetterQueue || {
|
34
|
+
maxReceiveCount: 2,
|
35
|
+
queue: DigitrafficDLQueue.create(stack, name),
|
36
|
+
},
|
37
|
+
}};
|
38
|
+
|
39
|
+
return new DigitrafficSqsQueue(stack, queueName, queueProps);
|
40
|
+
}
|
41
|
+
}
|
42
|
+
|
43
|
+
export class DigitrafficDLQueue {
|
44
|
+
static create(stack: DigitrafficStack, name: string): DigitrafficSqsQueue {
|
45
|
+
const dlqName = `${stack.configuration.shortName}-${name}-DLQ`;
|
46
|
+
|
47
|
+
const dlq = new DigitrafficSqsQueue(stack, dlqName, {
|
48
|
+
queueName: dlqName,
|
49
|
+
visibilityTimeout: Duration.seconds(60),
|
50
|
+
encryption: QueueEncryption.KMS_MANAGED,
|
51
|
+
});
|
52
|
+
|
53
|
+
const dlqBucket = new Bucket(stack, `${dlqName}-Bucket`, {
|
54
|
+
blockPublicAccess: BlockPublicAccess.BLOCK_ALL,
|
55
|
+
});
|
56
|
+
|
57
|
+
const dlqFunctionName = `${dlqName}-Function`;
|
58
|
+
const lambda = MonitoredFunction.create(stack, dlqFunctionName, {
|
59
|
+
runtime: Runtime.NODEJS_14_X,
|
60
|
+
logRetention: RetentionDays.ONE_YEAR,
|
61
|
+
functionName: dlqFunctionName,
|
62
|
+
code: getDlqCode(dlqBucket.bucketName),
|
63
|
+
timeout: Duration.seconds(10),
|
64
|
+
handler: 'index.handler',
|
65
|
+
memorySize: 128,
|
66
|
+
reservedConcurrentExecutions: 1,
|
67
|
+
});
|
68
|
+
|
69
|
+
const statement = new PolicyStatement();
|
70
|
+
statement.addActions('s3:PutObject');
|
71
|
+
statement.addActions('s3:PutObjectAcl');
|
72
|
+
statement.addResources(dlqBucket.bucketArn + '/*');
|
73
|
+
|
74
|
+
lambda.addToRolePolicy(statement);
|
75
|
+
lambda.addEventSource(new SqsEventSource(dlq));
|
76
|
+
|
77
|
+
addDLQAlarm(stack, dlqName, dlq);
|
78
|
+
|
79
|
+
return dlq;
|
80
|
+
}
|
81
|
+
}
|
82
|
+
|
83
|
+
function addDLQAlarm(stack: DigitrafficStack, dlqName: string, dlq: Queue) {
|
84
|
+
const alarmName = `${dlqName}-Alarm`;
|
85
|
+
dlq.metricNumberOfMessagesReceived({
|
86
|
+
period: Duration.minutes(5),
|
87
|
+
}).createAlarm(stack, alarmName, {
|
88
|
+
alarmName,
|
89
|
+
threshold: 0,
|
90
|
+
evaluationPeriods: 1,
|
91
|
+
treatMissingData: TreatMissingData.NOT_BREACHING,
|
92
|
+
comparisonOperator: ComparisonOperator.GREATER_THAN_THRESHOLD,
|
93
|
+
}).addAlarmAction(new SnsAction(stack.warningTopic));
|
94
|
+
}
|
95
|
+
|
96
|
+
function getDlqCode(bName: string): InlineCode {
|
97
|
+
const functionBody = DLQ_LAMBDA_CODE
|
98
|
+
.replace("__bucketName__", bName)
|
99
|
+
.replace("__upload__", uploadToS3.toString())
|
100
|
+
.replace("__doUpload__", doUpload.toString())
|
101
|
+
.replace("__handler__", createHandler().toString().substring(23)); // remove function handler() from signature
|
102
|
+
|
103
|
+
return new InlineCode(functionBody);
|
104
|
+
}
|
105
|
+
|
106
|
+
async function uploadToS3(s3: S3, bName: string, body: string, objectName: string): Promise<void> {
|
107
|
+
try {
|
108
|
+
console.info('writing %s to %s', objectName, bName);
|
109
|
+
await doUpload(s3, bName, body, objectName);
|
110
|
+
} catch (error) {
|
111
|
+
console.warn(error);
|
112
|
+
console.warn('method=uploadToS3 retrying upload to bucket %s', bName);
|
113
|
+
try {
|
114
|
+
await doUpload(s3, bName, body, objectName);
|
115
|
+
} catch (e2) {
|
116
|
+
console.error('method=uploadToS3 failed retrying upload to bucket %s', bName);
|
117
|
+
}
|
118
|
+
}
|
119
|
+
}
|
120
|
+
|
121
|
+
function doUpload(s3: S3, bName: string, Body: string, Key: string): Promise<ManagedUpload.SendData> {
|
122
|
+
return s3.upload({
|
123
|
+
Bucket: bName, Body, Key,
|
124
|
+
}).promise();
|
125
|
+
}
|
126
|
+
|
127
|
+
// bucketName is unused, will be overridden in the actual lambda code below
|
128
|
+
const bucketName = '';
|
129
|
+
|
130
|
+
function createHandler(): SQSHandler {
|
131
|
+
return async function handler(event: SQSEvent): Promise<void> {
|
132
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
133
|
+
const AWS = require('aws-sdk');
|
134
|
+
|
135
|
+
const millis = new Date().getTime();
|
136
|
+
await Promise.all(event.Records.map((e: SQSRecord, idx: number) =>
|
137
|
+
uploadToS3(new AWS.S3(), bucketName, e.body, `dlq-${millis}-${idx}.json`)));
|
138
|
+
};
|
139
|
+
}
|
140
|
+
|
141
|
+
const DLQ_LAMBDA_CODE = `const AWS = require('aws-sdk');
|
142
|
+
const bucketName = "__bucketName__";
|
143
|
+
|
144
|
+
__upload__
|
145
|
+
__doUpload__
|
146
|
+
|
147
|
+
exports.handler = async (event) => __handler__
|
148
|
+
`;
|
@@ -0,0 +1,207 @@
|
|
1
|
+
import {
|
2
|
+
Architecture,
|
3
|
+
AssetCode,
|
4
|
+
Code,
|
5
|
+
FunctionProps,
|
6
|
+
Runtime,
|
7
|
+
} from "aws-cdk-lib/aws-lambda";
|
8
|
+
import { Duration } from "aws-cdk-lib";
|
9
|
+
import { ISecurityGroup, IVpc, SubnetSelection } from "aws-cdk-lib/aws-ec2";
|
10
|
+
import { RetentionDays } from "aws-cdk-lib/aws-logs";
|
11
|
+
import { Role } from "aws-cdk-lib/aws-iam";
|
12
|
+
import { DigitrafficStack } from "./stack";
|
13
|
+
import { MonitoredFunctionAlarmProps } from "./monitoredfunction";
|
14
|
+
|
15
|
+
export type LambdaEnvironment = Record<string, string>;
|
16
|
+
|
17
|
+
export type DBLambdaEnvironment = LambdaEnvironment & {
|
18
|
+
SECRET_ID?: string;
|
19
|
+
DB_APPLICATION: string;
|
20
|
+
};
|
21
|
+
|
22
|
+
export interface LambdaConfiguration {
|
23
|
+
vpcId: string;
|
24
|
+
allowFromIpAddresses?: string[];
|
25
|
+
privateSubnetIds: string[];
|
26
|
+
availabilityZones: string[];
|
27
|
+
lambdaDbSgId: string;
|
28
|
+
dbProps?: DbProps;
|
29
|
+
defaultLambdaDurationSeconds?: number;
|
30
|
+
logsDestinationArn: string;
|
31
|
+
memorySize?: number;
|
32
|
+
runtime?: Runtime;
|
33
|
+
}
|
34
|
+
|
35
|
+
declare interface DbProps {
|
36
|
+
username: string;
|
37
|
+
password: string;
|
38
|
+
uri?: string;
|
39
|
+
ro_uri?: string;
|
40
|
+
}
|
41
|
+
|
42
|
+
export function databaseFunctionProps(
|
43
|
+
stack: DigitrafficStack,
|
44
|
+
environment: LambdaEnvironment,
|
45
|
+
lambdaName: string,
|
46
|
+
simpleLambdaName: string,
|
47
|
+
config?: Partial<FunctionParameters>
|
48
|
+
): FunctionProps {
|
49
|
+
const vpcSubnets = stack.vpc
|
50
|
+
? {
|
51
|
+
subnets: stack.vpc.privateSubnets,
|
52
|
+
}
|
53
|
+
: undefined;
|
54
|
+
|
55
|
+
return {
|
56
|
+
...lambdaFunctionProps(
|
57
|
+
stack,
|
58
|
+
environment,
|
59
|
+
lambdaName,
|
60
|
+
simpleLambdaName,
|
61
|
+
config
|
62
|
+
),
|
63
|
+
...{
|
64
|
+
vpc: stack.vpc || undefined,
|
65
|
+
vpcSubnets,
|
66
|
+
securityGroup: stack.lambdaDbSg || undefined,
|
67
|
+
},
|
68
|
+
};
|
69
|
+
}
|
70
|
+
|
71
|
+
export function lambdaFunctionProps(
|
72
|
+
stack: DigitrafficStack,
|
73
|
+
environment: LambdaEnvironment,
|
74
|
+
lambdaName: string,
|
75
|
+
simpleLambdaName: string,
|
76
|
+
config?: Partial<FunctionParameters>
|
77
|
+
): FunctionProps {
|
78
|
+
return {
|
79
|
+
runtime: config?.runtime ?? Runtime.NODEJS_14_X,
|
80
|
+
architecture: config?.architecture ?? Architecture.ARM_64,
|
81
|
+
memorySize: config?.memorySize ?? 128,
|
82
|
+
functionName: lambdaName,
|
83
|
+
role: config?.role,
|
84
|
+
timeout: Duration.seconds(config?.timeout ?? 60),
|
85
|
+
logRetention: RetentionDays.ONE_YEAR,
|
86
|
+
reservedConcurrentExecutions: config?.reservedConcurrentExecutions ?? 2,
|
87
|
+
code: getAssetCode(simpleLambdaName, config?.singleLambda ?? false),
|
88
|
+
handler: `${simpleLambdaName}.handler`,
|
89
|
+
environment,
|
90
|
+
};
|
91
|
+
}
|
92
|
+
|
93
|
+
function getAssetCode(
|
94
|
+
simpleLambdaName: string,
|
95
|
+
isSingleLambda: boolean
|
96
|
+
): AssetCode {
|
97
|
+
const lambdaPath = isSingleLambda
|
98
|
+
? `dist/lambda/`
|
99
|
+
: `dist/lambda/${simpleLambdaName}`;
|
100
|
+
|
101
|
+
return new AssetCode(lambdaPath);
|
102
|
+
}
|
103
|
+
|
104
|
+
/**
|
105
|
+
* Creates a base configuration for a Lambda that uses an RDS database
|
106
|
+
* @param vpc "Private" Lambdas are associated with a VPC
|
107
|
+
* @param lambdaDbSg Security Group shared by Lambda and RDS
|
108
|
+
* @param props Database connection properties for the Lambda
|
109
|
+
* @param config Lambda configuration
|
110
|
+
*/
|
111
|
+
export function dbLambdaConfiguration(
|
112
|
+
vpc: IVpc,
|
113
|
+
lambdaDbSg: ISecurityGroup,
|
114
|
+
props: LambdaConfiguration,
|
115
|
+
config: FunctionParameters
|
116
|
+
): FunctionProps {
|
117
|
+
return {
|
118
|
+
runtime: props.runtime || Runtime.NODEJS_14_X,
|
119
|
+
memorySize: props.memorySize || config.memorySize || 1024,
|
120
|
+
functionName: config.functionName,
|
121
|
+
code: config.code,
|
122
|
+
role: config.role,
|
123
|
+
handler: config.handler,
|
124
|
+
timeout: Duration.seconds(
|
125
|
+
config.timeout || props.defaultLambdaDurationSeconds || 60
|
126
|
+
),
|
127
|
+
environment: config.environment || {
|
128
|
+
DB_USER: props.dbProps?.username ?? "",
|
129
|
+
DB_PASS: props.dbProps?.password ?? "",
|
130
|
+
DB_URI:
|
131
|
+
(config.readOnly
|
132
|
+
? props.dbProps?.ro_uri
|
133
|
+
: props.dbProps?.uri) ?? "",
|
134
|
+
},
|
135
|
+
logRetention: RetentionDays.ONE_YEAR,
|
136
|
+
vpc: vpc,
|
137
|
+
vpcSubnets: {
|
138
|
+
subnets: vpc.privateSubnets,
|
139
|
+
},
|
140
|
+
securityGroups: [lambdaDbSg],
|
141
|
+
reservedConcurrentExecutions: config.reservedConcurrentExecutions || 3,
|
142
|
+
};
|
143
|
+
}
|
144
|
+
|
145
|
+
export function defaultLambdaConfiguration(
|
146
|
+
config: FunctionParameters
|
147
|
+
): FunctionProps {
|
148
|
+
const props: FunctionProps = {
|
149
|
+
runtime: Runtime.NODEJS_14_X,
|
150
|
+
memorySize: config.memorySize ?? 128,
|
151
|
+
functionName: config.functionName,
|
152
|
+
handler: config.handler,
|
153
|
+
environment: config.environment ?? {},
|
154
|
+
logRetention: RetentionDays.ONE_YEAR,
|
155
|
+
reservedConcurrentExecutions: config.reservedConcurrentExecutions,
|
156
|
+
code: config.code,
|
157
|
+
role: config.role,
|
158
|
+
timeout: Duration.seconds(config.timeout || 10),
|
159
|
+
};
|
160
|
+
if (config.vpc) {
|
161
|
+
return {
|
162
|
+
...props,
|
163
|
+
...{
|
164
|
+
vpc: config.vpc,
|
165
|
+
vpcSubnets: {
|
166
|
+
subnets: config.vpc?.privateSubnets,
|
167
|
+
},
|
168
|
+
},
|
169
|
+
};
|
170
|
+
}
|
171
|
+
return props;
|
172
|
+
}
|
173
|
+
|
174
|
+
export interface FunctionParameters {
|
175
|
+
memorySize?: number;
|
176
|
+
timeout?: number;
|
177
|
+
functionName?: string;
|
178
|
+
code: Code;
|
179
|
+
handler: string;
|
180
|
+
readOnly?: boolean;
|
181
|
+
environment?: {
|
182
|
+
[key: string]: string;
|
183
|
+
};
|
184
|
+
reservedConcurrentExecutions?: number;
|
185
|
+
role?: Role;
|
186
|
+
vpc?: IVpc;
|
187
|
+
vpcSubnets?: SubnetSelection;
|
188
|
+
runtime?: Runtime;
|
189
|
+
architecture?: Architecture;
|
190
|
+
singleLambda?: boolean;
|
191
|
+
}
|
192
|
+
|
193
|
+
export type MonitoredFunctionParameters = FunctionParameters & {
|
194
|
+
readonly memorySize?: number;
|
195
|
+
readonly timeout?: number;
|
196
|
+
readonly functionName?: string;
|
197
|
+
readonly reservedConcurrentExecutions?: number;
|
198
|
+
readonly role?: Role;
|
199
|
+
readonly runtime?: Runtime;
|
200
|
+
readonly architecture?: Architecture;
|
201
|
+
readonly singleLambda?: boolean;
|
202
|
+
|
203
|
+
readonly durationAlarmProps?: MonitoredFunctionAlarmProps;
|
204
|
+
readonly durationWarningProps?: MonitoredFunctionAlarmProps;
|
205
|
+
readonly errorAlarmProps?: MonitoredFunctionAlarmProps;
|
206
|
+
readonly throttleAlarmProps?: MonitoredFunctionAlarmProps;
|
207
|
+
};
|