@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.
Files changed (283) hide show
  1. package/.editorconfig +9 -0
  2. package/.eslintignore +4 -0
  3. package/.eslintrc.json +27 -0
  4. package/.github/CODEOWNERS +2 -0
  5. package/.github/workflows/build.yml +36 -0
  6. package/.github/workflows/eslint.yml +38 -0
  7. package/.github/workflows/mirror.yml +15 -0
  8. package/.gitignore +29 -0
  9. package/.husky/pre-commit +4 -0
  10. package/.prettierrc.json +10 -0
  11. package/dist/aws/infra/api/integration.js +52 -0
  12. package/dist/aws/infra/api/response.js +61 -0
  13. package/dist/aws/infra/api/responses.js +82 -0
  14. package/dist/aws/infra/api/static-integration.js +54 -0
  15. package/dist/aws/infra/canaries/canary-alarm.js +26 -0
  16. package/dist/aws/infra/canaries/canary-keys.js +7 -0
  17. package/dist/aws/infra/canaries/canary-parameters.js +3 -0
  18. package/dist/aws/infra/canaries/canary-role.js +46 -0
  19. package/dist/aws/infra/canaries/canary.js +32 -0
  20. package/dist/aws/infra/canaries/database-canary.js +70 -0
  21. package/dist/aws/infra/canaries/database-checker.js +103 -0
  22. package/dist/aws/infra/canaries/url-canary.js +47 -0
  23. package/dist/aws/infra/canaries/url-checker.js +252 -0
  24. package/dist/aws/infra/documentation.js +95 -0
  25. package/dist/aws/infra/scheduler.js +31 -0
  26. package/dist/aws/infra/security-rule.js +39 -0
  27. package/dist/aws/infra/sqs-integration.js +93 -0
  28. package/dist/aws/infra/sqs-queue.js +130 -0
  29. package/dist/aws/infra/stack/lambda-configs.js +105 -0
  30. package/dist/aws/infra/stack/monitoredfunction.js +143 -0
  31. package/dist/aws/infra/stack/rest_apis.js +185 -0
  32. package/dist/aws/infra/stack/stack-checking-aspect.js +174 -0
  33. package/dist/aws/infra/stack/stack.js +67 -0
  34. package/dist/aws/infra/stack/subscription.js +42 -0
  35. package/dist/aws/infra/usage-plans.js +42 -0
  36. package/dist/aws/runtime/apikey.js +13 -0
  37. package/dist/aws/runtime/digitraffic-integration-response.js +26 -0
  38. package/dist/aws/runtime/environment.js +12 -0
  39. package/dist/aws/runtime/messaging.js +31 -0
  40. package/dist/aws/runtime/s3.js +30 -0
  41. package/dist/aws/runtime/secrets/dbsecret.js +96 -0
  42. package/dist/aws/runtime/secrets/proxy-holder.js +27 -0
  43. package/dist/aws/runtime/secrets/rds-holder.js +27 -0
  44. package/dist/aws/runtime/secrets/secret-holder.js +76 -0
  45. package/dist/aws/runtime/secrets/secret.js +43 -0
  46. package/dist/aws/types/errors.js +16 -0
  47. package/dist/aws/types/lambda-response.js +33 -0
  48. package/dist/aws/types/mediatypes.js +16 -0
  49. package/dist/aws/types/model-with-reference.js +3 -0
  50. package/dist/aws/types/proxytypes.js +3 -0
  51. package/dist/aws/types/tags.js +7 -0
  52. package/dist/database/cached.js +32 -0
  53. package/dist/database/database.js +70 -0
  54. package/dist/database/last-updated.js +54 -0
  55. package/dist/database/models.js +3 -0
  56. package/dist/marine/id_utils.js +33 -0
  57. package/dist/marine/rtz.js +3 -0
  58. package/dist/test/asserter.js +45 -0
  59. package/dist/test/db-testutils.js +31 -0
  60. package/dist/test/httpserver.js +74 -0
  61. package/dist/test/secret.js +25 -0
  62. package/dist/test/secrets-manager.js +59 -0
  63. package/dist/test/testutils.js +44 -0
  64. package/dist/types/either.js +3 -0
  65. package/dist/types/input-error.js +7 -0
  66. package/dist/types/language.js +10 -0
  67. package/dist/types/traffictype.js +13 -0
  68. package/dist/types/validator.js +14 -0
  69. package/dist/utils/api-model.js +129 -0
  70. package/dist/utils/base64.js +21 -0
  71. package/dist/utils/date-utils.js +34 -0
  72. package/dist/utils/geojson-types.js +18 -0
  73. package/dist/utils/geometry.js +164 -0
  74. package/dist/utils/retry.js +50 -0
  75. package/dist/utils/slack.js +25 -0
  76. package/dist/utils/utils.js +75 -0
  77. package/jest.config.js +15 -0
  78. package/package.json +15 -13
  79. package/src/@types/geojson-validation/index.d.ts +4 -0
  80. package/src/aws/infra/api/integration.ts +73 -0
  81. package/src/aws/infra/api/response.ts +67 -0
  82. package/src/aws/infra/api/responses.ts +124 -0
  83. package/src/aws/infra/api/static-integration.ts +62 -0
  84. package/src/aws/infra/canaries/canary-alarm.ts +31 -0
  85. package/src/aws/infra/canaries/canary-keys.ts +3 -0
  86. package/{aws/infra/canaries/canary-parameters.d.ts → src/aws/infra/canaries/canary-parameters.ts} +7 -6
  87. package/src/aws/infra/canaries/canary-role.ts +47 -0
  88. package/src/aws/infra/canaries/canary.ts +46 -0
  89. package/src/aws/infra/canaries/database-canary.ts +98 -0
  90. package/src/aws/infra/canaries/database-checker.ts +155 -0
  91. package/src/aws/infra/canaries/url-canary.ts +74 -0
  92. package/src/aws/infra/canaries/url-checker.ts +366 -0
  93. package/src/aws/infra/documentation.ts +124 -0
  94. package/src/aws/infra/scheduler.ts +59 -0
  95. package/src/aws/infra/security-rule.ts +38 -0
  96. package/src/aws/infra/sqs-integration.ts +102 -0
  97. package/src/aws/infra/sqs-queue.ts +148 -0
  98. package/src/aws/infra/stack/lambda-configs.ts +207 -0
  99. package/src/aws/infra/stack/monitoredfunction.ts +342 -0
  100. package/src/aws/infra/stack/rest_apis.ts +223 -0
  101. package/src/aws/infra/stack/stack-checking-aspect.ts +279 -0
  102. package/src/aws/infra/stack/stack.ts +145 -0
  103. package/src/aws/infra/stack/subscription.ts +58 -0
  104. package/src/aws/infra/usage-plans.ts +41 -0
  105. package/src/aws/runtime/apikey.ts +9 -0
  106. package/src/aws/runtime/digitraffic-integration-response.ts +28 -0
  107. package/src/aws/runtime/environment.ts +9 -0
  108. package/src/aws/runtime/messaging.ts +26 -0
  109. package/src/aws/runtime/s3.ts +44 -0
  110. package/src/aws/runtime/secrets/dbsecret.ts +116 -0
  111. package/src/aws/runtime/secrets/proxy-holder.ts +37 -0
  112. package/src/aws/runtime/secrets/rds-holder.ts +33 -0
  113. package/src/aws/runtime/secrets/secret-holder.ts +116 -0
  114. package/src/aws/runtime/secrets/secret.ts +50 -0
  115. package/src/aws/types/errors.ts +14 -0
  116. package/src/aws/types/lambda-response.ts +43 -0
  117. package/{aws/types/mediatypes.d.ts → src/aws/types/mediatypes.ts} +4 -3
  118. package/{aws/types/model-with-reference.d.ts → src/aws/types/model-with-reference.ts} +2 -1
  119. package/src/aws/types/proxytypes.ts +27 -0
  120. package/src/aws/types/tags.ts +3 -0
  121. package/src/database/cached.ts +35 -0
  122. package/src/database/database.ts +96 -0
  123. package/src/database/last-updated.ts +59 -0
  124. package/{database/models.d.ts → src/database/models.ts} +1 -0
  125. package/src/marine/id_utils.ts +30 -0
  126. package/src/marine/rtz.ts +57 -0
  127. package/src/test/asserter.ts +48 -0
  128. package/src/test/db-testutils.ts +44 -0
  129. package/src/test/httpserver.ts +96 -0
  130. package/src/test/secret.ts +23 -0
  131. package/src/test/secrets-manager.ts +34 -0
  132. package/src/test/testutils.ts +39 -0
  133. package/src/types/either.ts +3 -0
  134. package/src/types/input-error.ts +2 -0
  135. package/src/types/language.ts +3 -0
  136. package/src/types/traffictype.ts +8 -0
  137. package/src/types/validator.ts +10 -0
  138. package/src/utils/api-model.ts +133 -0
  139. package/src/utils/base64.ts +16 -0
  140. package/src/utils/date-utils.ts +30 -0
  141. package/src/utils/geojson-types.ts +22 -0
  142. package/src/utils/geometry.ts +164 -0
  143. package/src/utils/retry.ts +49 -0
  144. package/src/utils/slack.ts +22 -0
  145. package/src/utils/utils.ts +105 -0
  146. package/test/marine/id_utils.test.ts +57 -0
  147. package/test/promise/promise.test.ts +143 -0
  148. package/test/secrets/dbsecret.test.ts +59 -0
  149. package/test/secrets/secret-holder.test.ts +143 -0
  150. package/test/secrets/secret.test.ts +49 -0
  151. package/test/test/httpserver.test.ts +128 -0
  152. package/test/utils/date-utils.test.ts +28 -0
  153. package/test/utils/geometry.test.ts +29 -0
  154. package/test/utils/utils.test.ts +64 -0
  155. package/tsconfig.eslint.json +4 -0
  156. package/tsconfig.json +22 -0
  157. package/yarn.lock +4060 -0
  158. package/aws/infra/api/integration.d.ts +0 -21
  159. package/aws/infra/api/integration.js +0 -52
  160. package/aws/infra/api/response.d.ts +0 -22
  161. package/aws/infra/api/response.js +0 -61
  162. package/aws/infra/api/responses.d.ts +0 -39
  163. package/aws/infra/api/responses.js +0 -79
  164. package/aws/infra/api/static-integration.d.ts +0 -15
  165. package/aws/infra/api/static-integration.js +0 -54
  166. package/aws/infra/canaries/canary-alarm.d.ts +0 -6
  167. package/aws/infra/canaries/canary-alarm.js +0 -26
  168. package/aws/infra/canaries/canary-parameters.js +0 -3
  169. package/aws/infra/canaries/canary-role.d.ts +0 -6
  170. package/aws/infra/canaries/canary-role.js +0 -46
  171. package/aws/infra/canaries/canary.d.ts +0 -8
  172. package/aws/infra/canaries/canary.js +0 -32
  173. package/aws/infra/canaries/database-canary.d.ts +0 -18
  174. package/aws/infra/canaries/database-canary.js +0 -55
  175. package/aws/infra/canaries/database-checker.d.ts +0 -21
  176. package/aws/infra/canaries/database-checker.js +0 -109
  177. package/aws/infra/canaries/url-canary.d.ts +0 -19
  178. package/aws/infra/canaries/url-canary.js +0 -46
  179. package/aws/infra/canaries/url-checker.d.ts +0 -46
  180. package/aws/infra/canaries/url-checker.js +0 -238
  181. package/aws/infra/documentation.d.ts +0 -56
  182. package/aws/infra/documentation.js +0 -95
  183. package/aws/infra/scheduler.d.ts +0 -12
  184. package/aws/infra/scheduler.js +0 -31
  185. package/aws/infra/security-rule.d.ts +0 -12
  186. package/aws/infra/security-rule.js +0 -39
  187. package/aws/infra/sqs-integration.d.ts +0 -7
  188. package/aws/infra/sqs-integration.js +0 -93
  189. package/aws/infra/sqs-queue.d.ts +0 -16
  190. package/aws/infra/sqs-queue.js +0 -130
  191. package/aws/infra/stack/lambda-configs.d.ts +0 -72
  192. package/aws/infra/stack/lambda-configs.js +0 -93
  193. package/aws/infra/stack/monitoredfunction.d.ts +0 -84
  194. package/aws/infra/stack/monitoredfunction.js +0 -135
  195. package/aws/infra/stack/rest_apis.d.ts +0 -41
  196. package/aws/infra/stack/rest_apis.js +0 -185
  197. package/aws/infra/stack/stack-checking-aspect.d.ts +0 -21
  198. package/aws/infra/stack/stack-checking-aspect.js +0 -174
  199. package/aws/infra/stack/stack.d.ts +0 -44
  200. package/aws/infra/stack/stack.js +0 -60
  201. package/aws/infra/stack/subscription.d.ts +0 -17
  202. package/aws/infra/stack/subscription.js +0 -41
  203. package/aws/infra/usage-plans.d.ts +0 -15
  204. package/aws/infra/usage-plans.js +0 -42
  205. package/aws/runtime/apikey.d.ts +0 -2
  206. package/aws/runtime/apikey.js +0 -13
  207. package/aws/runtime/digitraffic-integration-response.d.ts +0 -8
  208. package/aws/runtime/digitraffic-integration-response.js +0 -26
  209. package/aws/runtime/environment.d.ts +0 -1
  210. package/aws/runtime/environment.js +0 -12
  211. package/aws/runtime/messaging.d.ts +0 -10
  212. package/aws/runtime/messaging.js +0 -31
  213. package/aws/runtime/s3.d.ts +0 -2
  214. package/aws/runtime/s3.js +0 -30
  215. package/aws/runtime/secrets/dbsecret.d.ts +0 -54
  216. package/aws/runtime/secrets/dbsecret.js +0 -96
  217. package/aws/runtime/secrets/proxy-holder.d.ts +0 -9
  218. package/aws/runtime/secrets/proxy-holder.js +0 -26
  219. package/aws/runtime/secrets/rds-holder.d.ts +0 -9
  220. package/aws/runtime/secrets/rds-holder.js +0 -26
  221. package/aws/runtime/secrets/secret-holder.d.ts +0 -26
  222. package/aws/runtime/secrets/secret-holder.js +0 -73
  223. package/aws/runtime/secrets/secret.d.ts +0 -8
  224. package/aws/runtime/secrets/secret.js +0 -43
  225. package/aws/types/errors.d.ts +0 -4
  226. package/aws/types/errors.js +0 -9
  227. package/aws/types/lambda-response.d.ts +0 -12
  228. package/aws/types/lambda-response.js +0 -28
  229. package/aws/types/mediatypes.js +0 -15
  230. package/aws/types/model-with-reference.js +0 -3
  231. package/aws/types/proxytypes.d.ts +0 -26
  232. package/aws/types/proxytypes.js +0 -3
  233. package/aws/types/tags.d.ts +0 -2
  234. package/aws/types/tags.js +0 -7
  235. package/database/cached.d.ts +0 -7
  236. package/database/cached.js +0 -32
  237. package/database/database.d.ts +0 -19
  238. package/database/database.js +0 -62
  239. package/database/last-updated.d.ts +0 -16
  240. package/database/last-updated.js +0 -54
  241. package/database/models.js +0 -3
  242. package/index.d.ts +0 -1
  243. package/index.js +0 -18
  244. package/marine/id_utils.d.ts +0 -3
  245. package/marine/id_utils.js +0 -33
  246. package/marine/rtz.d.ts +0 -48
  247. package/marine/rtz.js +0 -3
  248. package/test/asserter.d.ts +0 -11
  249. package/test/asserter.js +0 -45
  250. package/test/db-testutils.d.ts +0 -2
  251. package/test/db-testutils.js +0 -31
  252. package/test/httpserver.d.ts +0 -18
  253. package/test/httpserver.js +0 -67
  254. package/test/secret.d.ts +0 -3
  255. package/test/secret.js +0 -25
  256. package/test/secrets-manager.d.ts +0 -9
  257. package/test/secrets-manager.js +0 -59
  258. package/test/testutils.d.ts +0 -12
  259. package/test/testutils.js +0 -44
  260. package/types/input-error.d.ts +0 -2
  261. package/types/input-error.js +0 -7
  262. package/types/language.d.ts +0 -5
  263. package/types/language.js +0 -10
  264. package/types/traffictype.d.ts +0 -8
  265. package/types/traffictype.js +0 -13
  266. package/types/validator.d.ts +0 -4
  267. package/types/validator.js +0 -14
  268. package/utils/api-model.d.ts +0 -87
  269. package/utils/api-model.js +0 -129
  270. package/utils/base64.d.ts +0 -12
  271. package/utils/base64.js +0 -21
  272. package/utils/date-utils.d.ts +0 -17
  273. package/utils/date-utils.js +0 -34
  274. package/utils/geojson-types.d.ts +0 -14
  275. package/utils/geojson-types.js +0 -18
  276. package/utils/geometry.d.ts +0 -36
  277. package/utils/geometry.js +0 -140
  278. package/utils/retry.d.ts +0 -13
  279. package/utils/retry.js +0 -50
  280. package/utils/slack.d.ts +0 -5
  281. package/utils/slack.js +0 -25
  282. package/utils/utils.d.ts +0 -30
  283. 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
+ };