@digitraffic/common 2026.3.17-1 → 2026.3.26-1-beta

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 (238) hide show
  1. package/README.md +18 -1
  2. package/dist/__test__/asserter.d.ts +13 -0
  3. package/dist/__test__/asserter.js +39 -0
  4. package/dist/__test__/db-testutils.d.ts +3 -0
  5. package/dist/__test__/db-testutils.js +38 -0
  6. package/dist/__test__/dependencies.test.d.ts +1 -0
  7. package/dist/__test__/dependencies.test.js +21 -0
  8. package/dist/__test__/imports.test.d.ts +1 -0
  9. package/dist/__test__/imports.test.js +318 -0
  10. package/dist/__test__/infra/acl-builder.test.d.ts +1 -0
  11. package/dist/__test__/infra/acl-builder.test.js +72 -0
  12. package/dist/__test__/infra/api/handler-factory.test.d.ts +1 -0
  13. package/dist/__test__/infra/api/handler-factory.test.js +42 -0
  14. package/dist/__test__/infra/api/integration.test.d.ts +1 -0
  15. package/dist/__test__/infra/api/integration.test.js +162 -0
  16. package/dist/__test__/infra/api/response.test.d.ts +1 -0
  17. package/dist/__test__/infra/api/response.test.js +77 -0
  18. package/dist/__test__/infra/api/static-integration.test.d.ts +1 -0
  19. package/dist/__test__/infra/api/static-integration.test.js +35 -0
  20. package/dist/__test__/infra/documentation.test.d.ts +1 -0
  21. package/dist/__test__/infra/documentation.test.js +38 -0
  22. package/dist/__test__/infra/scheduler.test.d.ts +1 -0
  23. package/dist/__test__/infra/scheduler.test.js +23 -0
  24. package/dist/__test__/infra/security-rule.test.d.ts +1 -0
  25. package/dist/__test__/infra/security-rule.test.js +21 -0
  26. package/dist/__test__/infra/stack/rest-apis.test.d.ts +1 -0
  27. package/dist/__test__/infra/stack/rest-apis.test.js +47 -0
  28. package/dist/__test__/marine/id_utils.test.d.ts +1 -0
  29. package/dist/__test__/marine/id_utils.test.js +45 -0
  30. package/dist/__test__/mock-ky.d.ts +2 -0
  31. package/dist/__test__/mock-ky.js +15 -0
  32. package/dist/__test__/promise/promise.test.d.ts +1 -0
  33. package/dist/__test__/promise/promise.test.js +126 -0
  34. package/dist/__test__/runtime/dt-logger.test.d.ts +1 -0
  35. package/dist/__test__/runtime/dt-logger.test.js +193 -0
  36. package/dist/__test__/secrets/secret-holder.test.d.ts +1 -0
  37. package/dist/__test__/secrets/secret-holder.test.js +96 -0
  38. package/dist/__test__/secrets/secret.test.d.ts +1 -0
  39. package/dist/__test__/secrets/secret.test.js +57 -0
  40. package/dist/__test__/stack/dt-function.test.d.ts +1 -0
  41. package/dist/__test__/stack/dt-function.test.js +340 -0
  42. package/dist/__test__/stack/rest-apis.test.d.ts +1 -0
  43. package/dist/__test__/stack/rest-apis.test.js +45 -0
  44. package/dist/__test__/test/mock-ky.test.d.ts +1 -0
  45. package/dist/__test__/test/mock-ky.test.js +46 -0
  46. package/dist/__test__/testutils.d.ts +12 -0
  47. package/dist/__test__/testutils.js +32 -0
  48. package/dist/__test__/types/lambda-proxy-types.test.d.ts +8 -0
  49. package/dist/__test__/types/lambda-proxy-types.test.js +155 -0
  50. package/dist/__test__/types/lambda-response-builder.test.d.ts +1 -0
  51. package/dist/__test__/types/lambda-response-builder.test.js +81 -0
  52. package/dist/__test__/types/lambda-response.test.d.ts +9 -0
  53. package/dist/__test__/types/lambda-response.test.js +73 -0
  54. package/dist/__test__/utils/base64.test.d.ts +1 -0
  55. package/dist/__test__/utils/base64.test.js +38 -0
  56. package/dist/__test__/utils/date-utils.test.d.ts +1 -0
  57. package/dist/__test__/utils/date-utils.test.js +32 -0
  58. package/dist/__test__/utils/geometry.test.d.ts +1 -0
  59. package/dist/__test__/utils/geometry.test.js +25 -0
  60. package/dist/__test__/utils/lambda-proxy-event.test.d.ts +1 -0
  61. package/dist/__test__/utils/lambda-proxy-event.test.js +45 -0
  62. package/dist/__test__/utils/logging.test.d.ts +1 -0
  63. package/dist/__test__/utils/logging.test.js +75 -0
  64. package/dist/__test__/utils/stop-watch.test.d.ts +1 -0
  65. package/dist/__test__/utils/stop-watch.test.js +118 -0
  66. package/dist/__test__/utils/utils.test.d.ts +1 -0
  67. package/dist/__test__/utils/utils.test.js +48 -0
  68. package/dist/aws/infra/acl-builder.d.ts +53 -0
  69. package/dist/aws/infra/acl-builder.js +407 -0
  70. package/dist/aws/infra/api/handler-factory.d.ts +22 -0
  71. package/dist/aws/infra/api/handler-factory.js +68 -0
  72. package/dist/aws/infra/api/integration.d.ts +49 -0
  73. package/dist/aws/infra/api/integration.js +162 -0
  74. package/dist/aws/infra/api/response.d.ts +62 -0
  75. package/dist/aws/infra/api/response.js +132 -0
  76. package/dist/aws/infra/api/responses.d.ts +60 -0
  77. package/dist/aws/infra/api/responses.js +90 -0
  78. package/dist/aws/infra/api/static-integration.d.ts +16 -0
  79. package/dist/aws/infra/api/static-integration.js +76 -0
  80. package/dist/aws/infra/bucket-policy.d.ts +38 -0
  81. package/dist/aws/infra/bucket-policy.js +30 -0
  82. package/dist/aws/infra/canaries/canary-alarm.d.ts +6 -0
  83. package/dist/aws/infra/canaries/canary-alarm.js +20 -0
  84. package/dist/aws/infra/canaries/canary-keys.d.ts +3 -0
  85. package/dist/aws/infra/canaries/canary-keys.js +4 -0
  86. package/dist/aws/infra/canaries/canary-parameters.d.ts +19 -0
  87. package/dist/aws/infra/canaries/canary-parameters.js +2 -0
  88. package/dist/aws/infra/canaries/canary-role.d.ts +14 -0
  89. package/dist/aws/infra/canaries/canary-role.js +51 -0
  90. package/dist/aws/infra/canaries/canary.d.ts +8 -0
  91. package/dist/aws/infra/canaries/canary.js +26 -0
  92. package/dist/aws/infra/canaries/database-canary.d.ts +17 -0
  93. package/dist/aws/infra/canaries/database-canary.js +65 -0
  94. package/dist/aws/infra/canaries/database-checker.d.ts +33 -0
  95. package/dist/aws/infra/canaries/database-checker.js +119 -0
  96. package/dist/aws/infra/canaries/url-canary.d.ts +16 -0
  97. package/dist/aws/infra/canaries/url-canary.js +55 -0
  98. package/dist/aws/infra/canaries/url-checker.d.ts +45 -0
  99. package/dist/aws/infra/canaries/url-checker.js +256 -0
  100. package/dist/aws/infra/documentation.d.ts +56 -0
  101. package/dist/aws/infra/documentation.js +90 -0
  102. package/dist/aws/infra/import-util.d.ts +17 -0
  103. package/dist/aws/infra/import-util.js +41 -0
  104. package/dist/aws/infra/scheduler.d.ts +12 -0
  105. package/dist/aws/infra/scheduler.js +27 -0
  106. package/dist/aws/infra/security-rule.d.ts +12 -0
  107. package/dist/aws/infra/security-rule.js +35 -0
  108. package/dist/aws/infra/sqs-integration.d.ts +4 -0
  109. package/dist/aws/infra/sqs-integration.js +85 -0
  110. package/dist/aws/infra/sqs-queue.d.ts +19 -0
  111. package/dist/aws/infra/sqs-queue.js +145 -0
  112. package/dist/aws/infra/stack/dt-function-alarms.d.ts +29 -0
  113. package/dist/aws/infra/stack/dt-function-alarms.js +54 -0
  114. package/dist/aws/infra/stack/dt-function.d.ts +124 -0
  115. package/dist/aws/infra/stack/dt-function.js +315 -0
  116. package/dist/aws/infra/stack/lambda-configs.d.ts +44 -0
  117. package/dist/aws/infra/stack/lambda-configs.js +71 -0
  118. package/dist/aws/infra/stack/lambda-log-group.d.ts +15 -0
  119. package/dist/aws/infra/stack/lambda-log-group.js +24 -0
  120. package/dist/aws/infra/stack/monitoredfunction.d.ts +85 -0
  121. package/dist/aws/infra/stack/monitoredfunction.js +147 -0
  122. package/dist/aws/infra/stack/parameters.d.ts +40 -0
  123. package/dist/aws/infra/stack/parameters.js +50 -0
  124. package/dist/aws/infra/stack/rest-api.d.ts +74 -0
  125. package/dist/aws/infra/stack/rest-api.js +235 -0
  126. package/dist/aws/infra/stack/stack-checking-aspect.d.ts +20 -0
  127. package/dist/aws/infra/stack/stack-checking-aspect.js +183 -0
  128. package/dist/aws/infra/stack/stack.d.ts +56 -0
  129. package/dist/aws/infra/stack/stack.js +71 -0
  130. package/dist/aws/infra/stack/subscription.d.ts +17 -0
  131. package/dist/aws/infra/stack/subscription.js +37 -0
  132. package/dist/aws/infra/stacks/db-dns-stack.d.ts +13 -0
  133. package/dist/aws/infra/stacks/db-dns-stack.js +60 -0
  134. package/dist/aws/infra/stacks/db-proxy-stack.d.ts +24 -0
  135. package/dist/aws/infra/stacks/db-proxy-stack.js +74 -0
  136. package/dist/aws/infra/stacks/db-stack.d.ts +65 -0
  137. package/dist/aws/infra/stacks/db-stack.js +189 -0
  138. package/dist/aws/infra/stacks/intra-stack-configuration.d.ts +5 -0
  139. package/dist/aws/infra/stacks/intra-stack-configuration.js +2 -0
  140. package/dist/aws/infra/stacks/network-stack.d.ts +14 -0
  141. package/dist/aws/infra/stacks/network-stack.js +45 -0
  142. package/dist/aws/infra/usage-plans.d.ts +16 -0
  143. package/dist/aws/infra/usage-plans.js +38 -0
  144. package/dist/aws/runtime/apikey.d.ts +2 -0
  145. package/dist/aws/runtime/apikey.js +13 -0
  146. package/dist/aws/runtime/digitraffic-integration-response.d.ts +8 -0
  147. package/dist/aws/runtime/digitraffic-integration-response.js +25 -0
  148. package/dist/aws/runtime/dt-logger-default.d.ts +9 -0
  149. package/dist/aws/runtime/dt-logger-default.js +6 -0
  150. package/dist/aws/runtime/dt-logger.d.ts +117 -0
  151. package/dist/aws/runtime/dt-logger.js +159 -0
  152. package/dist/aws/runtime/environment.d.ts +5 -0
  153. package/dist/aws/runtime/environment.js +7 -0
  154. package/dist/aws/runtime/s3.d.ts +3 -0
  155. package/dist/aws/runtime/s3.js +21 -0
  156. package/dist/aws/runtime/secrets/dbsecret.d.ts +16 -0
  157. package/dist/aws/runtime/secrets/dbsecret.js +26 -0
  158. package/dist/aws/runtime/secrets/proxy-holder.d.ts +9 -0
  159. package/dist/aws/runtime/secrets/proxy-holder.js +25 -0
  160. package/dist/aws/runtime/secrets/rds-holder.d.ts +9 -0
  161. package/dist/aws/runtime/secrets/rds-holder.js +25 -0
  162. package/dist/aws/runtime/secrets/secret-holder.d.ts +30 -0
  163. package/dist/aws/runtime/secrets/secret-holder.js +81 -0
  164. package/dist/aws/runtime/secrets/secret.d.ts +8 -0
  165. package/dist/aws/runtime/secrets/secret.js +61 -0
  166. package/dist/aws/types/errors.d.ts +8 -0
  167. package/dist/aws/types/errors.js +13 -0
  168. package/dist/aws/types/lambda-proxy-types.d.ts +59 -0
  169. package/dist/aws/types/lambda-proxy-types.js +210 -0
  170. package/dist/aws/types/lambda-response.d.ts +89 -0
  171. package/dist/aws/types/lambda-response.js +204 -0
  172. package/dist/aws/types/mediatypes.d.ts +11 -0
  173. package/dist/aws/types/mediatypes.js +14 -0
  174. package/dist/aws/types/model-with-reference.d.ts +7 -0
  175. package/dist/aws/types/model-with-reference.js +2 -0
  176. package/dist/aws/types/tags.d.ts +2 -0
  177. package/dist/aws/types/tags.js +4 -0
  178. package/dist/database/database.d.ts +27 -0
  179. package/dist/database/database.js +95 -0
  180. package/dist/database/last-updated.d.ts +15 -0
  181. package/dist/database/last-updated.js +46 -0
  182. package/dist/database/models.d.ts +6 -0
  183. package/dist/database/models.js +2 -0
  184. package/dist/index.d.ts +2 -0
  185. package/dist/index.js +2 -0
  186. package/dist/marine/id_utils.d.ts +3 -0
  187. package/dist/marine/id_utils.js +36 -0
  188. package/dist/marine/rtz.d.ts +48 -0
  189. package/dist/marine/rtz.js +2 -0
  190. package/dist/types/async-timeout-error.d.ts +3 -0
  191. package/dist/types/async-timeout-error.js +6 -0
  192. package/dist/types/either.d.ts +9 -0
  193. package/dist/types/either.js +2 -0
  194. package/dist/types/geojson.d.ts +47 -0
  195. package/dist/types/geojson.js +51 -0
  196. package/dist/types/http-error.d.ts +4 -0
  197. package/dist/types/http-error.js +8 -0
  198. package/dist/types/input-error.d.ts +2 -0
  199. package/dist/types/input-error.js +3 -0
  200. package/dist/types/language.d.ts +5 -0
  201. package/dist/types/language.js +7 -0
  202. package/dist/types/nullable.d.ts +24 -0
  203. package/dist/types/nullable.js +2 -0
  204. package/dist/types/openapi-schema.d.ts +932 -0
  205. package/dist/types/openapi-schema.js +151 -0
  206. package/dist/types/traffictype.d.ts +11 -0
  207. package/dist/types/traffictype.js +13 -0
  208. package/dist/types/urn.d.ts +1 -0
  209. package/dist/types/urn.js +2 -0
  210. package/dist/types/util-types.d.ts +11 -0
  211. package/dist/types/util-types.js +2 -0
  212. package/dist/types/validator.d.ts +4 -0
  213. package/dist/types/validator.js +9 -0
  214. package/dist/utils/api-model.d.ts +51 -0
  215. package/dist/utils/api-model.js +118 -0
  216. package/dist/utils/base64.d.ts +34 -0
  217. package/dist/utils/base64.js +53 -0
  218. package/dist/utils/date-utils.d.ts +27 -0
  219. package/dist/utils/date-utils.js +45 -0
  220. package/dist/utils/geojson-types.d.ts +14 -0
  221. package/dist/utils/geojson-types.js +15 -0
  222. package/dist/utils/geometry.d.ts +44 -0
  223. package/dist/utils/geometry.js +154 -0
  224. package/dist/utils/lambda-proxy-event.d.ts +9 -0
  225. package/dist/utils/lambda-proxy-event.js +31 -0
  226. package/dist/utils/logging.d.ts +40 -0
  227. package/dist/utils/logging.js +88 -0
  228. package/dist/utils/retry.d.ts +33 -0
  229. package/dist/utils/retry.js +135 -0
  230. package/dist/utils/slack.d.ts +5 -0
  231. package/dist/utils/slack.js +24 -0
  232. package/dist/utils/stop-watch.d.ts +46 -0
  233. package/dist/utils/stop-watch.js +114 -0
  234. package/dist/utils/utils.d.ts +95 -0
  235. package/dist/utils/utils.js +178 -0
  236. package/dist/utils/zod-utils.d.ts +27 -0
  237. package/dist/utils/zod-utils.js +57 -0
  238. package/package.json +30 -28
@@ -0,0 +1,145 @@
1
+ import { PutObjectCommand, S3Client } from "@aws-sdk/client-s3";
2
+ import { Duration } from "aws-cdk-lib";
3
+ import { ComparisonOperator, TreatMissingData, } from "aws-cdk-lib/aws-cloudwatch";
4
+ import { SnsAction } from "aws-cdk-lib/aws-cloudwatch-actions";
5
+ import { PolicyStatement } from "aws-cdk-lib/aws-iam";
6
+ import { InlineCode, Runtime } from "aws-cdk-lib/aws-lambda";
7
+ import { SqsEventSource } from "aws-cdk-lib/aws-lambda-event-sources";
8
+ import { BlockPublicAccess, Bucket } from "aws-cdk-lib/aws-s3";
9
+ import { Queue, QueueEncryption } from "aws-cdk-lib/aws-sqs";
10
+ import { logger } from "../runtime/dt-logger-default.js";
11
+ import { createLambdaLogGroup } from "./stack/lambda-log-group.js";
12
+ import { MonitoredFunction } from "./stack/monitoredfunction.js";
13
+ const DLQ_LAMBDA_CODE = `
14
+ import type { ObjectCannedACL } from "@aws-sdk/client-s3";
15
+ import { PutObjectCommand, S3Client } from "@aws-sdk/client-s3";
16
+ import { type NodeJsRuntimeStreamingBlobPayloadInputTypes } from "@smithy/types";
17
+ import { logger } from "./dt-logger-default.mjs";
18
+
19
+
20
+ const bucketName = "__bucketName__";
21
+
22
+ __upload__
23
+
24
+ exports.handler = async (event) => __handler__
25
+ `;
26
+ /**
27
+ * Construct for creating SQS-queues.
28
+ *
29
+ * If you don't config your own deadLetterQueue, this will create a dlq for you, also a lambda function, a s3 bucket
30
+ * and an alarm for the queue. Anything that goes to the dlq will be written into the bucket and the alarm is activated.
31
+ */
32
+ export class DigitrafficSqsQueue extends Queue {
33
+ static create(stack, name, props) {
34
+ const queueName = `${stack.configuration.shortName}-${name}-Queue`;
35
+ const queueProps = {
36
+ ...props,
37
+ encryption: QueueEncryption.KMS_MANAGED,
38
+ queueName,
39
+ deadLetterQueue: props.deadLetterQueue ?? {
40
+ maxReceiveCount: 2,
41
+ queue: DigitrafficDLQueue.create(stack, name),
42
+ },
43
+ };
44
+ return new DigitrafficSqsQueue(stack, queueName, queueProps);
45
+ }
46
+ /**
47
+ * Create a fifo with given name. No DLQ created!
48
+ */
49
+ static createFifo(stack, name, props) {
50
+ const queueName = `${stack.configuration.shortName}-${name}-Queue.fifo`;
51
+ const queueProps = {
52
+ ...props,
53
+ encryption: QueueEncryption.KMS_MANAGED,
54
+ queueName,
55
+ };
56
+ return new DigitrafficSqsQueue(stack, queueName, queueProps);
57
+ }
58
+ }
59
+ export const DigitrafficDLQueue = {
60
+ create(stack, name) {
61
+ const dlqName = `${stack.configuration.shortName}-${name}-DLQ`;
62
+ const dlqFunctionName = `${dlqName}-Function`;
63
+ const dlq = new DigitrafficSqsQueue(stack, dlqName, {
64
+ queueName: dlqName,
65
+ visibilityTimeout: Duration.seconds(60),
66
+ encryption: QueueEncryption.KMS_MANAGED,
67
+ });
68
+ const dlqBucket = new Bucket(stack, `${dlqName}-Bucket`, {
69
+ blockPublicAccess: BlockPublicAccess.BLOCK_ALL,
70
+ });
71
+ const dlqLogGroup = createLambdaLogGroup({
72
+ stack,
73
+ functionName: dlqFunctionName,
74
+ });
75
+ const lambda = MonitoredFunction.create(stack, dlqFunctionName, {
76
+ runtime: Runtime.NODEJS_24_X,
77
+ logGroup: dlqLogGroup,
78
+ functionName: dlqFunctionName,
79
+ code: getDlqCode(dlqBucket.bucketName),
80
+ timeout: Duration.seconds(10),
81
+ handler: "index.handler",
82
+ memorySize: 128,
83
+ reservedConcurrentExecutions: 1,
84
+ });
85
+ const statement = new PolicyStatement();
86
+ statement.addActions("s3:PutObject");
87
+ statement.addActions("s3:PutObjectAcl");
88
+ statement.addResources(`${dlqBucket.bucketArn}/*`);
89
+ lambda.addToRolePolicy(statement);
90
+ lambda.addEventSource(new SqsEventSource(dlq));
91
+ addDLQAlarm(stack, dlqName, dlq);
92
+ return dlq;
93
+ },
94
+ };
95
+ function addDLQAlarm(stack, dlqName, dlq) {
96
+ const alarmName = `${dlqName}-Alarm`;
97
+ dlq
98
+ .metricNumberOfMessagesReceived({
99
+ period: Duration.minutes(5),
100
+ })
101
+ .createAlarm(stack, alarmName, {
102
+ alarmName,
103
+ threshold: 0,
104
+ evaluationPeriods: 1,
105
+ treatMissingData: TreatMissingData.NOT_BREACHING,
106
+ comparisonOperator: ComparisonOperator.GREATER_THAN_THRESHOLD,
107
+ })
108
+ .addAlarmAction(new SnsAction(stack.warningTopic));
109
+ }
110
+ function getDlqCode(Bucket) {
111
+ const functionBody = DLQ_LAMBDA_CODE.replace("__bucketName__", Bucket)
112
+ .replace("__upload__", uploadToS3.toString())
113
+ .replace("__handler__", createHandler().toString().substring(23)); // remove function handler() from signature
114
+ return new InlineCode(functionBody);
115
+ }
116
+ async function uploadToS3(s3, bucketName, body, objectName, cannedAcl, contentType) {
117
+ const command = new PutObjectCommand({
118
+ Bucket: bucketName,
119
+ Key: objectName,
120
+ Body: body,
121
+ ACL: cannedAcl,
122
+ ContentType: contentType,
123
+ });
124
+ try {
125
+ await s3.send(command);
126
+ }
127
+ catch (_error) {
128
+ logger.error({
129
+ method: "s3.uploadToS3",
130
+ message: `upload failed to bucket ${bucketName}`,
131
+ });
132
+ }
133
+ }
134
+ // bucketName is unused, will be overridden in the actual lambda code below
135
+ const bucketName = "";
136
+ function createHandler() {
137
+ return async function handler(event) {
138
+ const millis = Date.now();
139
+ const s3 = new S3Client({});
140
+ await Promise.all(event.Records.map((e, idx) => {
141
+ return uploadToS3(s3, bucketName, e.body, `dlq-${millis}-${idx}.json`);
142
+ }));
143
+ };
144
+ }
145
+ //# sourceMappingURL=sqs-queue.js.map
@@ -0,0 +1,29 @@
1
+ import { ComparisonOperator } from "aws-cdk-lib/aws-cloudwatch";
2
+ export interface AlarmProps {
3
+ readonly threshold?: number;
4
+ readonly evaluationPeriods: number;
5
+ readonly datapointsToAlarm: number;
6
+ readonly comparisonOperator: ComparisonOperator;
7
+ }
8
+ export declare class DtFunctionAlarms {
9
+ durationAlarm?: AlarmProps;
10
+ durationWarning?: AlarmProps;
11
+ errorAlarms?: AlarmProps;
12
+ throttleAlarm?: AlarmProps;
13
+ /**
14
+ * Set throttle alarm. Undefined disables the alarm.
15
+ */
16
+ withThrottleAlarm(throttleAlarm?: AlarmProps): this;
17
+ /**
18
+ * Set error alarm. Undefined disables the alarm.
19
+ */
20
+ withErrorAlarm(errorAlarm?: AlarmProps): this;
21
+ /**
22
+ * Set duration alarm. Undefined disables the alarm.
23
+ */
24
+ withDurationAlarm(durationAlarmn?: AlarmProps): this;
25
+ /**
26
+ * Set duration warning. Undefined disables the warning.
27
+ */
28
+ withDurationWarning(durationWarning?: AlarmProps): this;
29
+ }
@@ -0,0 +1,54 @@
1
+ import { ComparisonOperator } from "aws-cdk-lib/aws-cloudwatch";
2
+ export class DtFunctionAlarms {
3
+ durationAlarm = {
4
+ evaluationPeriods: 1,
5
+ datapointsToAlarm: 1,
6
+ comparisonOperator: ComparisonOperator.GREATER_THAN_OR_EQUAL_TO_THRESHOLD,
7
+ };
8
+ durationWarning = {
9
+ evaluationPeriods: 1,
10
+ datapointsToAlarm: 1,
11
+ comparisonOperator: ComparisonOperator.GREATER_THAN_OR_EQUAL_TO_THRESHOLD,
12
+ };
13
+ errorAlarms = {
14
+ threshold: 1,
15
+ evaluationPeriods: 1,
16
+ datapointsToAlarm: 1,
17
+ comparisonOperator: ComparisonOperator.GREATER_THAN_OR_EQUAL_TO_THRESHOLD,
18
+ };
19
+ throttleAlarm = {
20
+ threshold: 1,
21
+ evaluationPeriods: 1,
22
+ datapointsToAlarm: 1,
23
+ comparisonOperator: ComparisonOperator.GREATER_THAN_OR_EQUAL_TO_THRESHOLD,
24
+ };
25
+ /**
26
+ * Set throttle alarm. Undefined disables the alarm.
27
+ */
28
+ withThrottleAlarm(throttleAlarm) {
29
+ this.throttleAlarm = throttleAlarm;
30
+ return this;
31
+ }
32
+ /**
33
+ * Set error alarm. Undefined disables the alarm.
34
+ */
35
+ withErrorAlarm(errorAlarm) {
36
+ this.errorAlarms = errorAlarm;
37
+ return this;
38
+ }
39
+ /**
40
+ * Set duration alarm. Undefined disables the alarm.
41
+ */
42
+ withDurationAlarm(durationAlarmn) {
43
+ this.durationAlarm = durationAlarmn;
44
+ return this;
45
+ }
46
+ /**
47
+ * Set duration warning. Undefined disables the warning.
48
+ */
49
+ withDurationWarning(durationWarning) {
50
+ this.durationWarning = durationWarning;
51
+ return this;
52
+ }
53
+ }
54
+ //# sourceMappingURL=dt-function-alarms.js.map
@@ -0,0 +1,124 @@
1
+ import type { Stack } from "aws-cdk-lib";
2
+ import { Duration } from "aws-cdk-lib";
3
+ import type { ISecurityGroup } from "aws-cdk-lib/aws-ec2";
4
+ import type { IRole } from "aws-cdk-lib/aws-iam";
5
+ import { PolicyStatement } from "aws-cdk-lib/aws-iam";
6
+ import type { ILayerVersion } from "aws-cdk-lib/aws-lambda";
7
+ import { Architecture, Function as AwsFunction, Code, Runtime } from "aws-cdk-lib/aws-lambda";
8
+ import { DtFunctionAlarms } from "./dt-function-alarms.js";
9
+ import type { LambdaEnvironment } from "./lambda-configs.js";
10
+ import type { DigitrafficStackLike } from "./stack.js";
11
+ export declare class FunctionBuilder {
12
+ private readonly _stack;
13
+ private readonly _name;
14
+ private description?;
15
+ private runtime;
16
+ private architecture;
17
+ private role?;
18
+ private timeout;
19
+ private reservedConcurrentExecutions;
20
+ private memorySize;
21
+ private functionName;
22
+ private environment;
23
+ private vpc?;
24
+ private alarms;
25
+ private code;
26
+ private handler;
27
+ private readonly layers;
28
+ private readonly securityGroups;
29
+ private readonly policyStatements;
30
+ private readonly allowedActions;
31
+ private readonly _features;
32
+ constructor(stack: Stack & DigitrafficStackLike, lambdaName: string);
33
+ /**
34
+ * Creates a new builder with defaults, using the lambdaName as a source for the lambda implementation (dist/lambdaName/lambdaName.js).
35
+ * Database access is given by default.
36
+ */
37
+ static create(stack: Stack & DigitrafficStackLike, lambdaName: string): FunctionBuilder;
38
+ /**
39
+ * Creates a new builder with defaults, but without database or secret access.
40
+ */
41
+ static plain(stack: Stack & DigitrafficStackLike, lambdaName: string): FunctionBuilder;
42
+ /**
43
+ * Stack only has one lambda. Default is that it has multiple lambdas.
44
+ */
45
+ singleLambda(): this;
46
+ /**
47
+ * Use AssetCode from given path(dist/lambda/${path}). Default path is lambdaName. Also calls withHandler with the same value.
48
+ */
49
+ withAssetCode(path?: string): this;
50
+ withCode(code: Code): this;
51
+ /**
52
+ * Use given handler(${name}.handler) to run the lambda. Default value is lambdaName.
53
+ * @param name
54
+ * @param handlerFunctionName
55
+ * @returns
56
+ */
57
+ withHandler(name: string, handlerFunctionName?: string): this;
58
+ withDescription(description: string): this;
59
+ /**
60
+ * Do not grant database access. Default is with database access.
61
+ */
62
+ withoutDatabaseAccess(): this;
63
+ /**
64
+ * Do not grant secret access. Default is with secret access.
65
+ */
66
+ withoutSecretAccess(): this;
67
+ /**
68
+ * Add given security groups to the lambda.
69
+ */
70
+ withSecurityGroup(...groups: ISecurityGroup[]): this;
71
+ /**
72
+ * Set memorySize(in MB) for the lambda. Default is 128 MB.
73
+ */
74
+ withMemorySize(memorySize: number): this;
75
+ /**
76
+ * Set runtime for the lambda. Default is Runtime.NODEJS_24_X.
77
+ */
78
+ withRuntime(runtime: Runtime): this;
79
+ /**
80
+ * Add Lambda layers.
81
+ */
82
+ withLayers(...layers: ILayerVersion[]): this;
83
+ /**
84
+ * Set architecture for the lambda. Default is Architecture.ARM_64.
85
+ */
86
+ withArchitecture(architecture: Architecture): this;
87
+ /**
88
+ * Set timeout for the lambda. Default is Duration.seconds(60).
89
+ */
90
+ withTimeout(timeout: Duration): this;
91
+ /**
92
+ * Set reservedConcurrectExecutions for the lambda. Default is 2.
93
+ */
94
+ withReservedConcurrentExecutions(executions: number): this;
95
+ /**
96
+ * Set role for the lambda. Default is no role.
97
+ */
98
+ withRole(role: IRole): this;
99
+ /**
100
+ * Set environment for the lambda. Default environment comes from the DigitrafficStack and
101
+ * this does not clear it, only adds to the default values.
102
+ */
103
+ withEnvironment(environment: LambdaEnvironment): this;
104
+ /**
105
+ * Configure alarms for the lambda. By default, all alarms are added.
106
+ */
107
+ withAlarms(alarms: DtFunctionAlarms): this;
108
+ /**
109
+ * Add a policy statement to the lambda's role.
110
+ */
111
+ withRolePolicies(...policies: PolicyStatement[]): this;
112
+ /**
113
+ * Add allowed actions to the lambda's role. Creates a policy statement that allows
114
+ * the specified actions on all resources (*).
115
+ */
116
+ withAllowedActions(...actions: string[]): this;
117
+ private getSecurityGroups;
118
+ build(): AwsFunction;
119
+ private attachPolicies;
120
+ private validateNoWildcardActions;
121
+ private getEnvironment;
122
+ private createAlarms;
123
+ private createAlarm;
124
+ }
@@ -0,0 +1,315 @@
1
+ import { basename } from "node:path";
2
+ import { Duration } from "aws-cdk-lib";
3
+ import { SnsAction } from "aws-cdk-lib/aws-cloudwatch-actions";
4
+ import { Effect, PolicyStatement } from "aws-cdk-lib/aws-iam";
5
+ import { ApplicationLogLevel, Architecture, AssetCode, Function as AwsFunction, Code, LoggingFormat, Runtime, SystemLogLevel, } from "aws-cdk-lib/aws-lambda";
6
+ import { camelCase, startCase } from "es-toolkit";
7
+ import { DatabaseEnvironmentKeys } from "../../../database/database.js";
8
+ import { EnvKeys } from "../../runtime/environment.js";
9
+ import { DtFunctionAlarms } from "./dt-function-alarms.js";
10
+ import { createLambdaLogGroup } from "./lambda-log-group.js";
11
+ export class FunctionBuilder {
12
+ _stack;
13
+ _name;
14
+ description;
15
+ runtime = Runtime.NODEJS_24_X;
16
+ architecture = Architecture.ARM_64;
17
+ role;
18
+ timeout = Duration.seconds(60);
19
+ reservedConcurrentExecutions = 2;
20
+ memorySize = 128;
21
+ functionName;
22
+ environment = {};
23
+ vpc;
24
+ alarms = new DtFunctionAlarms();
25
+ // these will be overridden in constructor, but inspection won't see it, so set some default values.
26
+ code = Code.fromInline("placeholder");
27
+ handler = "";
28
+ layers = [];
29
+ securityGroups = [];
30
+ policyStatements = [];
31
+ allowedActions = [];
32
+ _features = {
33
+ singleLambda: false,
34
+ databaseAccess: true,
35
+ secretAccess: true,
36
+ };
37
+ constructor(stack, lambdaName) {
38
+ this._name = lambdaName;
39
+ this._stack = stack;
40
+ this.functionName = `${stack.configuration.shortName}-${startCase(camelCase(lambdaName)).replace(/\s/g, "")}`;
41
+ this.environment = {};
42
+ this.vpc = stack.vpc;
43
+ // this calls withHandler as well but with full path
44
+ this.withAssetCode(lambdaName);
45
+ // Remove path from lambda to get module name.
46
+ // e.g. for lambdaName "api/charging-network/v1/operators",
47
+ // moduleName becomes "operators" and handler becomes "operators.handler"
48
+ const moduleName = basename(lambdaName);
49
+ this.withHandler(moduleName);
50
+ }
51
+ /**
52
+ * Creates a new builder with defaults, using the lambdaName as a source for the lambda implementation (dist/lambdaName/lambdaName.js).
53
+ * Database access is given by default.
54
+ */
55
+ static create(stack, lambdaName) {
56
+ return new FunctionBuilder(stack, lambdaName);
57
+ }
58
+ /**
59
+ * Creates a new builder with defaults, but without database or secret access.
60
+ */
61
+ static plain(stack, lambdaName) {
62
+ return new FunctionBuilder(stack, lambdaName)
63
+ .withoutDatabaseAccess()
64
+ .withoutSecretAccess();
65
+ }
66
+ /**
67
+ * Stack only has one lambda. Default is that it has multiple lambdas.
68
+ */
69
+ singleLambda() {
70
+ this._features.singleLambda = true;
71
+ return this;
72
+ }
73
+ /**
74
+ * Use AssetCode from given path(dist/lambda/${path}). Default path is lambdaName. Also calls withHandler with the same value.
75
+ */
76
+ withAssetCode(path = this._name) {
77
+ const lambdaPath = this._features.singleLambda
78
+ ? `dist/lambda/`
79
+ : `dist/lambda/${path}`;
80
+ this.code = new AssetCode(lambdaPath);
81
+ this.withHandler(path);
82
+ return this;
83
+ }
84
+ withCode(code) {
85
+ this.code = code;
86
+ return this;
87
+ }
88
+ /**
89
+ * Use given handler(${name}.handler) to run the lambda. Default value is lambdaName.
90
+ * @param name
91
+ * @param handlerFunctionName
92
+ * @returns
93
+ */
94
+ withHandler(name, handlerFunctionName = "handler") {
95
+ this.handler = `${name}.${handlerFunctionName}`;
96
+ return this;
97
+ }
98
+ withDescription(description) {
99
+ this.description = description;
100
+ return this;
101
+ }
102
+ /**
103
+ * Do not grant database access. Default is with database access.
104
+ */
105
+ withoutDatabaseAccess() {
106
+ this._features.databaseAccess = false;
107
+ return this;
108
+ }
109
+ /**
110
+ * Do not grant secret access. Default is with secret access.
111
+ */
112
+ withoutSecretAccess() {
113
+ this._features.secretAccess = false;
114
+ return this;
115
+ }
116
+ /**
117
+ * Add given security groups to the lambda.
118
+ */
119
+ withSecurityGroup(...groups) {
120
+ this.securityGroups.push(...groups);
121
+ return this;
122
+ }
123
+ /**
124
+ * Set memorySize(in MB) for the lambda. Default is 128 MB.
125
+ */
126
+ withMemorySize(memorySize) {
127
+ this.memorySize = memorySize;
128
+ return this;
129
+ }
130
+ /**
131
+ * Set runtime for the lambda. Default is Runtime.NODEJS_24_X.
132
+ */
133
+ withRuntime(runtime) {
134
+ this.runtime = runtime;
135
+ return this;
136
+ }
137
+ /**
138
+ * Add Lambda layers.
139
+ */
140
+ withLayers(...layers) {
141
+ this.layers.push(...layers);
142
+ return this;
143
+ }
144
+ /**
145
+ * Set architecture for the lambda. Default is Architecture.ARM_64.
146
+ */
147
+ withArchitecture(architecture) {
148
+ this.architecture = architecture;
149
+ return this;
150
+ }
151
+ /**
152
+ * Set timeout for the lambda. Default is Duration.seconds(60).
153
+ */
154
+ withTimeout(timeout) {
155
+ this.timeout = timeout;
156
+ return this;
157
+ }
158
+ /**
159
+ * Set reservedConcurrectExecutions for the lambda. Default is 2.
160
+ */
161
+ withReservedConcurrentExecutions(executions) {
162
+ this.reservedConcurrentExecutions = executions;
163
+ return this;
164
+ }
165
+ /**
166
+ * Set role for the lambda. Default is no role.
167
+ */
168
+ withRole(role) {
169
+ this.role = role;
170
+ return this;
171
+ }
172
+ /**
173
+ * Set environment for the lambda. Default environment comes from the DigitrafficStack and
174
+ * this does not clear it, only adds to the default values.
175
+ */
176
+ withEnvironment(environment) {
177
+ this.environment = { ...this.environment, ...environment };
178
+ return this;
179
+ }
180
+ /**
181
+ * Configure alarms for the lambda. By default, all alarms are added.
182
+ */
183
+ withAlarms(alarms) {
184
+ this.alarms = alarms;
185
+ return this;
186
+ }
187
+ /**
188
+ * Add a policy statement to the lambda's role.
189
+ */
190
+ withRolePolicies(...policies) {
191
+ this.policyStatements.push(...policies);
192
+ return this;
193
+ }
194
+ /**
195
+ * Add allowed actions to the lambda's role. Creates a policy statement that allows
196
+ * the specified actions on all resources (*).
197
+ */
198
+ withAllowedActions(...actions) {
199
+ this.allowedActions.push(...actions);
200
+ return this;
201
+ }
202
+ getSecurityGroups() {
203
+ if (this._features.databaseAccess) {
204
+ if (!this._stack.lambdaDbSg) {
205
+ throw new Error("Lambda db security group no defined");
206
+ }
207
+ return [...this.securityGroups, this._stack.lambdaDbSg];
208
+ }
209
+ return this.securityGroups;
210
+ }
211
+ build() {
212
+ const logGroup = createLambdaLogGroup({
213
+ stack: this._stack,
214
+ functionName: this.functionName,
215
+ });
216
+ const vpcSubnets = this._stack.vpc
217
+ ? {
218
+ subnets: this._stack.vpc.privateSubnets,
219
+ }
220
+ : undefined;
221
+ const securityGroups = this.getSecurityGroups();
222
+ const createdFunction = new AwsFunction(this._stack, this.functionName, {
223
+ vpc: this.vpc,
224
+ vpcSubnets,
225
+ loggingFormat: LoggingFormat.JSON,
226
+ applicationLogLevelV2: ApplicationLogLevel.DEBUG,
227
+ systemLogLevelV2: SystemLogLevel.INFO,
228
+ runtime: this.runtime,
229
+ architecture: this.architecture,
230
+ code: this.code,
231
+ handler: this.handler,
232
+ role: this.role,
233
+ timeout: this.timeout,
234
+ logGroup,
235
+ reservedConcurrentExecutions: this.reservedConcurrentExecutions,
236
+ memorySize: this.memorySize,
237
+ functionName: this.functionName,
238
+ securityGroups,
239
+ environment: this.getEnvironment(),
240
+ description: this.description,
241
+ layers: this.layers.length > 0 ? this.layers : undefined,
242
+ });
243
+ if (this._features.secretAccess) {
244
+ this._stack.grantSecret(createdFunction);
245
+ }
246
+ this.attachPolicies(createdFunction);
247
+ this.createAlarms(createdFunction);
248
+ return createdFunction;
249
+ }
250
+ attachPolicies(lambda) {
251
+ this.validateNoWildcardActions();
252
+ for (const policyStatement of this.policyStatements) {
253
+ lambda.addToRolePolicy(policyStatement);
254
+ }
255
+ if (this.allowedActions.length > 0) {
256
+ lambda.addToRolePolicy(new PolicyStatement({
257
+ effect: Effect.ALLOW,
258
+ actions: this.allowedActions,
259
+ resources: ["*"],
260
+ }));
261
+ }
262
+ }
263
+ validateNoWildcardActions() {
264
+ if (this.allowedActions.includes("*")) {
265
+ throw new Error(`Lambda ${this.functionName} cannot use wildcard action "*" in withAllowedActions. Please specify explicit actions.`);
266
+ }
267
+ }
268
+ getEnvironment() {
269
+ let environment = {};
270
+ if (this._features.secretAccess) {
271
+ environment = {
272
+ ...environment,
273
+ [EnvKeys.SECRET_ID]: this._stack.configuration.secretId,
274
+ };
275
+ }
276
+ if (this._features.databaseAccess) {
277
+ environment = {
278
+ ...environment,
279
+ [DatabaseEnvironmentKeys.DB_APPLICATION]: this._stack.configuration.shortName,
280
+ };
281
+ }
282
+ return { ...environment, ...this.environment };
283
+ }
284
+ createAlarms(lambda) {
285
+ const alarmSnsAction = new SnsAction(this._stack.alarmTopic);
286
+ const warningSnsAction = new SnsAction(this._stack.warningTopic);
287
+ const trafficType = this._stack.configuration.trafficType;
288
+ const production = this._stack.configuration.production;
289
+ if (this.alarms.durationAlarm) {
290
+ this.createAlarm(lambda, lambda.metricDuration().with({ statistic: "max" }), "Duration", "Duration alarm", `Duration has exceeded ${this.timeout.toSeconds()} seconds`, trafficType, production ? alarmSnsAction : warningSnsAction, this.timeout.toMilliseconds(), this.alarms.durationAlarm);
291
+ }
292
+ if (this.alarms.durationWarning) {
293
+ this.createAlarm(lambda, lambda.metricDuration().with({ statistic: "max" }), "Duration-Warning", "Duration warning", `Duration is 85 % of max ${this.timeout.toSeconds()} seconds`, trafficType, warningSnsAction, this.timeout.toMilliseconds() * 0.85, this.alarms.durationWarning);
294
+ }
295
+ if (this.alarms.errorAlarms) {
296
+ this.createAlarm(lambda, lambda.metricErrors(), "Errors", "Errors alarm", "Invocations did not succeed", trafficType, production ? alarmSnsAction : warningSnsAction, 1, this.alarms.errorAlarms);
297
+ }
298
+ if (this.alarms.throttleAlarm) {
299
+ this.createAlarm(lambda, lambda.metricThrottles(), "Throttles", "Throttles alarm", "Has throttled", trafficType, production ? alarmSnsAction : warningSnsAction, 1, this.alarms.throttleAlarm);
300
+ }
301
+ }
302
+ createAlarm(lambda, metric, alarmId, alarmName, alarmDescription, trafficType, alarmSnsAction, threshold, alarmProps) {
303
+ metric
304
+ .createAlarm(this._stack, `${lambda.node.id}-${alarmId}`, {
305
+ alarmName: `${trafficType ?? ""} ${this._stack.stackName} ${this.functionName} ${alarmName}`.trim(),
306
+ alarmDescription,
307
+ threshold: alarmProps.threshold ?? threshold,
308
+ evaluationPeriods: alarmProps.evaluationPeriods,
309
+ datapointsToAlarm: alarmProps.datapointsToAlarm,
310
+ comparisonOperator: alarmProps.comparisonOperator,
311
+ })
312
+ .addAlarmAction(alarmSnsAction);
313
+ }
314
+ }
315
+ //# sourceMappingURL=dt-function.js.map