@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,193 @@
1
+ import { Writable } from "node:stream";
2
+ import { describe, expect, test } from "vitest";
3
+ import { DtLogger } from "../../aws/runtime/dt-logger.js";
4
+ const LOG_LINE = {
5
+ method: "dt-logger.test",
6
+ message: "FOO",
7
+ };
8
+ const EXPECTED_LOG_LINE = {
9
+ method: "dt-logger.test",
10
+ message: "dt-logger.test FOO",
11
+ };
12
+ describe("dt-logger", () => {
13
+ function assertLog(config, message, expected) {
14
+ assertWrite(config, (logger) => {
15
+ logger.info(message);
16
+ }, expected);
17
+ }
18
+ function assertDebug(config, message, expected) {
19
+ assertWrite(config, (logger) => {
20
+ logger.debug(message);
21
+ }, expected);
22
+ }
23
+ function assertError(config, message, expected) {
24
+ assertWrite(config, (logger) => {
25
+ logger.error(message);
26
+ }, expected);
27
+ }
28
+ function assertWrite(config, writeFunction, expected) {
29
+ const logged = [];
30
+ const writeStream = new Writable({
31
+ write: (chunk) => {
32
+ logged.push(chunk.toString());
33
+ },
34
+ });
35
+ const logger = new DtLogger({
36
+ ...config,
37
+ ...{ writeStream: writeStream },
38
+ });
39
+ writeFunction(logger);
40
+ expect(logged.length).toBe(1);
41
+ const loggedLine = JSON.parse(logged[0]);
42
+ console.info(loggedLine);
43
+ if (typeof expected === "object" && "stack" in expected && expected.stack) {
44
+ const stack = loggedLine.stack;
45
+ expect(stack).toBeDefined();
46
+ // stack should be multiline string
47
+ const stackLines = stack.split("\n");
48
+ expect(stackLines.length).toBeGreaterThanOrEqual(2);
49
+ expect(stackLines[0]).toEqual(expected.stack);
50
+ expect(stackLines[1]?.trim()?.startsWith("at ")).toBe(true);
51
+ delete loggedLine.stack;
52
+ delete expected.stack;
53
+ }
54
+ expect(loggedLine).toMatchObject(expected);
55
+ }
56
+ test("custom date, number, text and '=' values", () => {
57
+ const date = new Date();
58
+ assertLog({}, {
59
+ ...LOG_LINE,
60
+ customDate: date,
61
+ customNumber: 123,
62
+ customText: "abc",
63
+ customEqualsText: "foo=bar",
64
+ }, {
65
+ method: EXPECTED_LOG_LINE.method,
66
+ message: `${EXPECTED_LOG_LINE.message} date=${date.toISOString()} number=123 text=abc equalsText="foo=bar"`,
67
+ date: date.toISOString(),
68
+ number: 123,
69
+ text: "abc",
70
+ });
71
+ });
72
+ test("custom count should be a number", () => {
73
+ assertLog({}, {
74
+ ...LOG_LINE,
75
+ customFooCount: 123,
76
+ }, {
77
+ method: EXPECTED_LOG_LINE.method,
78
+ message: `${EXPECTED_LOG_LINE.message} fooCount=123`,
79
+ fooCount: 123,
80
+ });
81
+ });
82
+ test("default values", () => {
83
+ assertLog({}, LOG_LINE, {
84
+ method: EXPECTED_LOG_LINE.method,
85
+ message: EXPECTED_LOG_LINE.message,
86
+ level: "INFO",
87
+ });
88
+ });
89
+ test("method not duplicated", () => {
90
+ assertLog({}, EXPECTED_LOG_LINE, {
91
+ method: EXPECTED_LOG_LINE.method,
92
+ message: EXPECTED_LOG_LINE.message,
93
+ level: "INFO",
94
+ });
95
+ });
96
+ test("set lambdaName", () => {
97
+ const LAMBDA_NAME = "test_lambda_name";
98
+ assertLog({ lambdaName: LAMBDA_NAME }, LOG_LINE, {
99
+ lambdaName: LAMBDA_NAME,
100
+ method: EXPECTED_LOG_LINE.method,
101
+ message: EXPECTED_LOG_LINE.message,
102
+ level: "INFO",
103
+ });
104
+ });
105
+ test("set runtime", () => {
106
+ const RUNTIME = "test_runtime";
107
+ assertLog({ runTime: RUNTIME }, LOG_LINE, {
108
+ message: EXPECTED_LOG_LINE.message,
109
+ method: EXPECTED_LOG_LINE.method,
110
+ level: "INFO",
111
+ runtime: RUNTIME,
112
+ });
113
+ });
114
+ test("debug - string", () => {
115
+ const DEBUG_STRING = "debug string";
116
+ assertDebug({}, DEBUG_STRING, {
117
+ message: DEBUG_STRING,
118
+ level: "DEBUG",
119
+ });
120
+ });
121
+ test("debug - json", () => {
122
+ const DEBUG_JSON = {
123
+ debug: "debug",
124
+ thing: 42,
125
+ };
126
+ assertDebug({}, DEBUG_JSON, {
127
+ message: {
128
+ debug: "debug",
129
+ thing: 42,
130
+ },
131
+ level: "DEBUG",
132
+ });
133
+ });
134
+ test("error - Error string", () => {
135
+ const error = "FAIL!";
136
+ assertError({}, {
137
+ ...LOG_LINE,
138
+ error,
139
+ }, {
140
+ ...EXPECTED_LOG_LINE,
141
+ error: "FAIL!",
142
+ level: "ERROR",
143
+ });
144
+ });
145
+ test("error - Error object", () => {
146
+ const error = {
147
+ errorMessage: "FAIL!",
148
+ errorCode: 123,
149
+ };
150
+ assertError({}, {
151
+ ...LOG_LINE,
152
+ error,
153
+ }, {
154
+ ...EXPECTED_LOG_LINE,
155
+ error: '{"errorMessage":"FAIL!","errorCode":123}',
156
+ level: "ERROR",
157
+ });
158
+ });
159
+ test("error - Error", () => {
160
+ const error = new Error("FAIL!");
161
+ assertError({}, {
162
+ ...LOG_LINE,
163
+ error,
164
+ }, {
165
+ ...EXPECTED_LOG_LINE,
166
+ error: "Error: FAIL!",
167
+ level: "ERROR",
168
+ });
169
+ });
170
+ test("error - Error with stack", () => {
171
+ let error;
172
+ try {
173
+ // @ts-expect-error
174
+ console.log(`Result: ${undefined.length}`);
175
+ }
176
+ catch (e) {
177
+ // @ts-expect-error
178
+ console.debug(`Failed message: ${e.message}`);
179
+ console.debug(`Failed stack: ${e.stack}`);
180
+ error = e;
181
+ }
182
+ assertError({}, {
183
+ ...LOG_LINE,
184
+ error,
185
+ }, {
186
+ ...EXPECTED_LOG_LINE,
187
+ error: "TypeError: Cannot read properties of undefined (reading 'length')",
188
+ level: "ERROR",
189
+ stack: "TypeError: Cannot read properties of undefined (reading 'length')",
190
+ });
191
+ });
192
+ });
193
+ //# sourceMappingURL=dt-logger.test.js.map
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,96 @@
1
+ import { SecretsManager } from "@aws-sdk/client-secrets-manager";
2
+ import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
3
+ import { setEnvVariable, setEnvVariableAwsRegion } from "../../utils/utils.js";
4
+ const SECRET_WITH_PREFIX = {
5
+ "prefix.value": "value",
6
+ "prefix.name": "name",
7
+ "wrong.value": "value",
8
+ username: "DB_USER",
9
+ };
10
+ const emptySecret = { $metadata: {} };
11
+ const getSecretValueMock = vi.fn();
12
+ vi.spyOn(SecretsManager.prototype, "getSecretValue").mockImplementation(getSecretValueMock);
13
+ const { SecretHolder } = await import("../../aws/runtime/secrets/secret-holder.js");
14
+ const { DatabaseEnvironmentKeys } = await import("../../database/database.js");
15
+ function mockSecret(secret) {
16
+ if (!secret) {
17
+ getSecretValueMock.mockImplementation(() => Promise.resolve(emptySecret));
18
+ }
19
+ else {
20
+ getSecretValueMock.mockImplementation(() => Promise.resolve({
21
+ ...emptySecret,
22
+ ...{ SecretString: JSON.stringify(secret) },
23
+ }));
24
+ }
25
+ }
26
+ describe("SecretHolder - tests", () => {
27
+ beforeEach(() => {
28
+ setEnvVariable("SECRET_ID", "test-id");
29
+ setEnvVariableAwsRegion("eu-west-1");
30
+ });
31
+ afterEach(() => {
32
+ delete process.env[DatabaseEnvironmentKeys.DB_USER];
33
+ });
34
+ test("get - no secret", () => {
35
+ mockSecret(null);
36
+ const holder = SecretHolder.create();
37
+ const secret = holder.get();
38
+ return expect(secret).rejects.toThrow("No secret found!");
39
+ }, 10000);
40
+ test("get - empty secret", () => {
41
+ mockSecret({});
42
+ const holder = SecretHolder.create();
43
+ const secret = holder.get();
44
+ return expect(secret).resolves.toEqual({});
45
+ });
46
+ test("get - no prefix", () => {
47
+ mockSecret(SECRET_WITH_PREFIX);
48
+ const holder = SecretHolder.create();
49
+ const secret = holder.get();
50
+ return expect(secret).resolves.toEqual(SECRET_WITH_PREFIX);
51
+ });
52
+ test("get - check keys - not found", () => {
53
+ mockSecret(SECRET_WITH_PREFIX);
54
+ const holder = SecretHolder.create("", ["not_found"]);
55
+ const secret = holder.get();
56
+ return expect(secret).rejects.toThrow();
57
+ });
58
+ test("get - check keys - found", () => {
59
+ mockSecret(SECRET_WITH_PREFIX);
60
+ const holder = SecretHolder.create("", ["prefix.value", "username"]);
61
+ return expect(holder.get()).resolves.toBeDefined();
62
+ });
63
+ test("getSecret - with prefix", () => {
64
+ mockSecret(SECRET_WITH_PREFIX);
65
+ const holder = SecretHolder.create("prefix");
66
+ const secret = holder.get();
67
+ return expect(secret).resolves.toEqual({
68
+ value: "value",
69
+ name: "name",
70
+ });
71
+ });
72
+ test("get - ttl - do not fetch", async () => {
73
+ mockSecret(SECRET_WITH_PREFIX);
74
+ const holder = SecretHolder.create();
75
+ const callCount = getSecretValueMock.mock.calls.length;
76
+ await holder.get();
77
+ expect(getSecretValueMock).toHaveBeenCalledTimes(callCount + 1);
78
+ // gets cached secret
79
+ await holder.get();
80
+ expect(getSecretValueMock).toHaveBeenCalledTimes(callCount + 1);
81
+ });
82
+ test("get - ttl - fetch", async () => {
83
+ mockSecret(SECRET_WITH_PREFIX);
84
+ const holder = new SecretHolder("", "", [], {
85
+ ttl: 1,
86
+ });
87
+ const callCount = getSecretValueMock.mock.calls.length;
88
+ await holder.get();
89
+ expect(getSecretValueMock).toHaveBeenCalledTimes(callCount + 1);
90
+ // cache expires, fetches secret again
91
+ await new Promise((resolve) => setTimeout(resolve, 2000));
92
+ await holder.get();
93
+ expect(getSecretValueMock).toHaveBeenCalledTimes(callCount + 2);
94
+ });
95
+ });
96
+ //# sourceMappingURL=secret-holder.test.js.map
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,57 @@
1
+ import { afterEach } from "node:test";
2
+ import { SecretsManager } from "@aws-sdk/client-secrets-manager";
3
+ import { describe, expect, test, vi } from "vitest";
4
+ import { setEnvVariableAwsRegion } from "../../utils/utils.js";
5
+ const SECRET_ID = "test_secret";
6
+ const SECRET_WITH_PREFIX = {
7
+ "prefix.value": "value",
8
+ "prefix.name": "name",
9
+ "wrong.value": "value",
10
+ };
11
+ const emptySecret = { $metadata: {} };
12
+ const getSecretValueMock = vi.fn();
13
+ vi.spyOn(SecretsManager.prototype, "getSecretValue").mockImplementation(getSecretValueMock);
14
+ function mockSecret(secret) {
15
+ if (!secret) {
16
+ getSecretValueMock.mockImplementation(() => Promise.resolve(emptySecret));
17
+ }
18
+ else {
19
+ getSecretValueMock.mockImplementation(() => Promise.resolve({
20
+ ...emptySecret,
21
+ ...{ SecretString: JSON.stringify(secret) },
22
+ }));
23
+ }
24
+ }
25
+ setEnvVariableAwsRegion("eu-west-1");
26
+ const secret = await import("../../aws/runtime/secrets/secret.js");
27
+ const { getSecret } = secret;
28
+ describe("secret - test", () => {
29
+ afterEach(() => {
30
+ vi.restoreAllMocks();
31
+ });
32
+ test("getSecret - no secret", async () => {
33
+ mockSecret(null);
34
+ await expect(async () => {
35
+ await getSecret(SECRET_ID, "");
36
+ }).rejects.toThrow("No secret found!");
37
+ });
38
+ test("getSecret - empty secret", async () => {
39
+ mockSecret({});
40
+ const secret = await getSecret(SECRET_ID, "");
41
+ expect(secret).toEqual({});
42
+ });
43
+ test("getSecret - no prefix", async () => {
44
+ mockSecret(SECRET_WITH_PREFIX);
45
+ const secret = await getSecret(SECRET_ID, "");
46
+ expect(secret).toEqual(SECRET_WITH_PREFIX);
47
+ });
48
+ test("getSecret - with prefix", async () => {
49
+ mockSecret(SECRET_WITH_PREFIX);
50
+ const secret = await getSecret(SECRET_ID, "prefix");
51
+ expect(secret).toEqual({
52
+ value: "value",
53
+ name: "name",
54
+ });
55
+ });
56
+ });
57
+ //# sourceMappingURL=secret.test.js.map
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,340 @@
1
+ import { App, Duration } from "aws-cdk-lib";
2
+ import { Match, Template } from "aws-cdk-lib/assertions";
3
+ import { PolicyStatement, Role, ServicePrincipal } from "aws-cdk-lib/aws-iam";
4
+ import { Architecture, Code, Runtime } from "aws-cdk-lib/aws-lambda";
5
+ import { describe, expect, test } from "vitest";
6
+ import { FunctionBuilder } from "../../aws/infra/stack/dt-function.js";
7
+ import { DigitrafficStack } from "../../aws/infra/stack/stack.js";
8
+ import { EnvKeys } from "../../aws/runtime/environment.js";
9
+ import { TrafficType } from "../../types/traffictype.js";
10
+ const TEST_ENV_VAR = "TEST_ENV_VAR";
11
+ const TEST_ENV_VALUE = "testValue";
12
+ // AWS::Lambda::Function Template Reference
13
+ // https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-lambda-function.html
14
+ describe("FunctionBuilder test", () => {
15
+ function createTemplate(tester, plain = false, lambdaName = "test") {
16
+ const app = new App();
17
+ const stack = new DigitrafficStack(app, "test-stack", {
18
+ alarmTopicArn: "",
19
+ production: false,
20
+ shortName: "test",
21
+ stackProps: {},
22
+ secretId: "testSecret",
23
+ trafficType: TrafficType.ROAD,
24
+ warningTopicArn: "",
25
+ });
26
+ const environment = {
27
+ [TEST_ENV_VAR]: TEST_ENV_VALUE,
28
+ };
29
+ const builder = plain
30
+ ? FunctionBuilder.plain(stack, lambdaName)
31
+ .withEnvironment(environment)
32
+ .withCode(Code.fromInline("{}"))
33
+ : FunctionBuilder.create(stack, lambdaName)
34
+ .withoutDatabaseAccess()
35
+ .withEnvironment(environment)
36
+ .withCode(Code.fromInline("{}"));
37
+ tester(builder);
38
+ builder.build();
39
+ return Template.fromStack(stack);
40
+ }
41
+ function expectEnvironmentValue(template, key, value) {
42
+ template.hasResourceProperties("AWS::Lambda::Function", {
43
+ Environment: {
44
+ Variables: Match.objectLike({
45
+ [key]: value,
46
+ }),
47
+ },
48
+ });
49
+ }
50
+ function expectEnvironmentValueMissing(template, key) {
51
+ template.hasResourceProperties("AWS::Lambda::Function", {
52
+ Environment: {
53
+ Variables: Match.objectLike({
54
+ [key]: Match.absent(),
55
+ }),
56
+ },
57
+ });
58
+ }
59
+ test("default builder", () => {
60
+ const template = createTemplate((_builder) => { });
61
+ template.hasResourceProperties("AWS::Lambda::Function", {
62
+ Environment: {
63
+ Variables: {
64
+ [EnvKeys.SECRET_ID]: "testSecret",
65
+ [TEST_ENV_VAR]: TEST_ENV_VALUE,
66
+ },
67
+ },
68
+ Runtime: Runtime.NODEJS_24_X.name,
69
+ MemorySize: 128,
70
+ Timeout: 60,
71
+ Handler: "test.handler",
72
+ });
73
+ });
74
+ test("plain builder", () => {
75
+ const template = createTemplate((_builder) => { }, true);
76
+ expectEnvironmentValue(template, TEST_ENV_VAR, TEST_ENV_VALUE);
77
+ expectEnvironmentValueMissing(template, EnvKeys.SECRET_ID);
78
+ });
79
+ test("withoutSecretAccess does not add secret-related environment variable", () => {
80
+ const template = createTemplate((builder) => {
81
+ builder.withoutSecretAccess();
82
+ });
83
+ expectEnvironmentValueMissing(template, EnvKeys.SECRET_ID);
84
+ });
85
+ test("Lambda runtime is set", () => {
86
+ const template = createTemplate((builder) => {
87
+ builder.withRuntime(Runtime.NODEJS_24_X);
88
+ });
89
+ template.hasResourceProperties("AWS::Lambda::Function", {
90
+ Runtime: Runtime.NODEJS_24_X.name,
91
+ });
92
+ });
93
+ test("Lambda description is set", () => {
94
+ const template = createTemplate((builder) => {
95
+ builder.withDescription("Does something useful");
96
+ });
97
+ template.hasResourceProperties("AWS::Lambda::Function", {
98
+ Description: "Does something useful",
99
+ });
100
+ });
101
+ test("Lambda memory size is set", () => {
102
+ const template = createTemplate((builder) => {
103
+ builder.withMemorySize(256);
104
+ });
105
+ template.hasResourceProperties("AWS::Lambda::Function", {
106
+ MemorySize: 256,
107
+ });
108
+ });
109
+ test("Lambda architecture is set", () => {
110
+ const template = createTemplate((builder) => {
111
+ builder.withArchitecture(Architecture.X86_64);
112
+ });
113
+ template.hasResourceProperties("AWS::Lambda::Function", {
114
+ Architectures: [Architecture.X86_64.name],
115
+ });
116
+ });
117
+ test("Lambda timeout is set", () => {
118
+ const template = createTemplate((builder) => {
119
+ builder.withTimeout(Duration.seconds(120));
120
+ });
121
+ template.hasResourceProperties("AWS::Lambda::Function", {
122
+ Timeout: 120,
123
+ });
124
+ });
125
+ test("Lambda reserved concurrency is set", () => {
126
+ const template = createTemplate((builder) => {
127
+ builder.withReservedConcurrentExecutions(5);
128
+ });
129
+ template.hasResourceProperties("AWS::Lambda::Function", {
130
+ ReservedConcurrentExecutions: 5,
131
+ });
132
+ });
133
+ test("Lambda handler is set", () => {
134
+ const template = createTemplate((builder) => {
135
+ builder.withHandler("custom", "main");
136
+ });
137
+ template.hasResourceProperties("AWS::Lambda::Function", {
138
+ Handler: "custom.main",
139
+ });
140
+ });
141
+ test("Lambda handler module is resolved from path last element", () => {
142
+ const template = createTemplate((_builder) => { }, false, "api/charging-network/v1/operators");
143
+ // const lambdas = template.findResources("AWS::Lambda::Function");
144
+ // console.debug(JSON.stringify(lambdas, null, 2));
145
+ template.hasResourceProperties("AWS::Lambda::Function", {
146
+ Handler: "operators.handler",
147
+ });
148
+ });
149
+ test("Lambda handler module is same as lambda name", () => {
150
+ const template = createTemplate((_builder) => { }, false, "operators");
151
+ template.hasResourceProperties("AWS::Lambda::Function", {
152
+ Handler: "operators.handler",
153
+ });
154
+ });
155
+ test("withRolePolicies adds custom policy to lambda role", () => {
156
+ const template = createTemplate((builder) => {
157
+ builder.withRolePolicies(new PolicyStatement({
158
+ actions: ["s3:GetObject"],
159
+ resources: ["arn:aws:s3:::my-bucket/*"],
160
+ }));
161
+ });
162
+ template.hasResourceProperties("AWS::IAM::Policy", {
163
+ PolicyDocument: {
164
+ Statement: Match.arrayWith([
165
+ Match.objectLike({
166
+ Action: "s3:GetObject",
167
+ Resource: "arn:aws:s3:::my-bucket/*",
168
+ }),
169
+ ]),
170
+ },
171
+ });
172
+ });
173
+ test("withAllowedActions adds policy with specified actions", () => {
174
+ const template = createTemplate((builder) => {
175
+ builder.withAllowedActions("dynamodb:PutItem", "dynamodb:GetItem");
176
+ });
177
+ template.hasResourceProperties("AWS::IAM::Policy", {
178
+ PolicyDocument: {
179
+ Statement: Match.arrayWith([
180
+ Match.objectLike({
181
+ Action: ["dynamodb:PutItem", "dynamodb:GetItem"],
182
+ Resource: "*",
183
+ }),
184
+ ]),
185
+ },
186
+ });
187
+ });
188
+ test("withRolePolicies and withAllowedActions can be used together", () => {
189
+ const template = createTemplate((builder) => {
190
+ builder
191
+ .withRolePolicies(new PolicyStatement({
192
+ actions: ["s3:GetObject"],
193
+ resources: ["arn:aws:s3:::my-bucket/*"],
194
+ }))
195
+ .withAllowedActions("dynamodb:PutItem", "dynamodb:GetItem");
196
+ });
197
+ template.hasResourceProperties("AWS::IAM::Policy", {
198
+ PolicyDocument: {
199
+ Statement: Match.arrayWith([
200
+ Match.objectLike({
201
+ Action: "s3:GetObject",
202
+ Resource: "arn:aws:s3:::my-bucket/*",
203
+ }),
204
+ Match.objectLike({
205
+ Action: ["dynamodb:PutItem", "dynamodb:GetItem"],
206
+ Resource: "*",
207
+ }),
208
+ ]),
209
+ },
210
+ });
211
+ });
212
+ test("Multiple withRolePolicies calls add multiple policies", () => {
213
+ const template = createTemplate((builder) => {
214
+ builder
215
+ .withRolePolicies(new PolicyStatement({
216
+ actions: ["s3:GetObject"],
217
+ resources: ["arn:aws:s3:::my-bucket/*"],
218
+ }))
219
+ .withRolePolicies(new PolicyStatement({
220
+ actions: ["sqs:SendMessage"],
221
+ resources: ["arn:aws:sqs:us-east-1:123456789012:my-queue"],
222
+ }));
223
+ });
224
+ template.hasResourceProperties("AWS::IAM::Policy", {
225
+ PolicyDocument: {
226
+ Statement: Match.arrayWith([
227
+ Match.objectLike({
228
+ Action: "s3:GetObject",
229
+ Resource: "arn:aws:s3:::my-bucket/*",
230
+ }),
231
+ Match.objectLike({
232
+ Action: "sqs:SendMessage",
233
+ Resource: "arn:aws:sqs:us-east-1:123456789012:my-queue",
234
+ }),
235
+ ]),
236
+ },
237
+ });
238
+ });
239
+ test("Multiple withAllowedActions calls accumulate actions", () => {
240
+ const template = createTemplate((builder) => {
241
+ builder
242
+ .withAllowedActions("dynamodb:PutItem", "dynamodb:GetItem")
243
+ .withAllowedActions("s3:ListBucket");
244
+ });
245
+ template.hasResourceProperties("AWS::IAM::Policy", {
246
+ PolicyDocument: {
247
+ Statement: Match.arrayWith([
248
+ Match.objectLike({
249
+ Action: ["dynamodb:PutItem", "dynamodb:GetItem", "s3:ListBucket"],
250
+ Resource: "*",
251
+ }),
252
+ ]),
253
+ },
254
+ });
255
+ });
256
+ test("withRole and withRolePolicies work together", () => {
257
+ const app = new App();
258
+ const stack = new DigitrafficStack(app, "test-stack", {
259
+ alarmTopicArn: "",
260
+ production: false,
261
+ shortName: "test",
262
+ stackProps: {},
263
+ secretId: "testSecret",
264
+ trafficType: TrafficType.ROAD,
265
+ warningTopicArn: "",
266
+ });
267
+ // Create a custom role
268
+ const customRole = new Role(stack, "CustomRole", {
269
+ assumedBy: new ServicePrincipal("lambda.amazonaws.com"),
270
+ });
271
+ FunctionBuilder.plain(stack, "test")
272
+ .withCode(Code.fromInline("{}"))
273
+ .withRole(customRole)
274
+ .withRolePolicies(new PolicyStatement({
275
+ actions: ["s3:GetObject"],
276
+ resources: ["arn:aws:s3:::my-bucket/*"],
277
+ }))
278
+ .withAllowedActions("dynamodb:Query")
279
+ .build();
280
+ const template = Template.fromStack(stack);
281
+ // Verify the custom role is used
282
+ template.hasResourceProperties("AWS::Lambda::Function", {
283
+ Role: Match.objectLike({
284
+ "Fn::GetAtt": Match.arrayWith([Match.stringLikeRegexp("CustomRole")]),
285
+ }),
286
+ });
287
+ // Verify the policies are attached
288
+ template.hasResourceProperties("AWS::IAM::Policy", {
289
+ PolicyDocument: {
290
+ Statement: Match.arrayWith([
291
+ Match.objectLike({
292
+ Action: "s3:GetObject",
293
+ Resource: "arn:aws:s3:::my-bucket/*",
294
+ }),
295
+ Match.objectLike({
296
+ Action: "dynamodb:Query",
297
+ Resource: "*",
298
+ }),
299
+ ]),
300
+ },
301
+ });
302
+ });
303
+ test("withAllowedActions throws error when wildcard action is used", () => {
304
+ const app = new App();
305
+ const stack = new DigitrafficStack(app, "test-stack", {
306
+ alarmTopicArn: "",
307
+ production: false,
308
+ shortName: "test",
309
+ stackProps: {},
310
+ secretId: "testSecret",
311
+ trafficType: TrafficType.ROAD,
312
+ warningTopicArn: "",
313
+ });
314
+ expect(() => {
315
+ FunctionBuilder.plain(stack, "test")
316
+ .withCode(Code.fromInline("{}"))
317
+ .withAllowedActions("*")
318
+ .build();
319
+ }).toThrow('Lambda test-Test cannot use wildcard action "*" in withAllowedActions');
320
+ });
321
+ test("withAllowedActions throws error when wildcard action is mixed with other actions", () => {
322
+ const app = new App();
323
+ const stack = new DigitrafficStack(app, "test-stack", {
324
+ alarmTopicArn: "",
325
+ production: false,
326
+ shortName: "test",
327
+ stackProps: {},
328
+ secretId: "testSecret",
329
+ trafficType: TrafficType.ROAD,
330
+ warningTopicArn: "",
331
+ });
332
+ expect(() => {
333
+ FunctionBuilder.plain(stack, "test")
334
+ .withCode(Code.fromInline("{}"))
335
+ .withAllowedActions("s3:GetObject", "*", "dynamodb:Query")
336
+ .build();
337
+ }).toThrow('Lambda test-Test cannot use wildcard action "*" in withAllowedActions');
338
+ });
339
+ });
340
+ //# sourceMappingURL=dt-function.test.js.map