@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,74 @@
1
+ import { Construct } from "constructs";
2
+ import { CanaryParameters } from "./canary-parameters";
3
+ import { Role } from "aws-cdk-lib/aws-iam";
4
+ import { DigitrafficCanary } from "./canary";
5
+ import { ISecret } from "aws-cdk-lib/aws-secretsmanager";
6
+ import { DigitrafficStack } from "../stack/stack";
7
+ import { LambdaEnvironment } from "../stack/lambda-configs";
8
+ import { DigitrafficRestApi } from "../stack/rest_apis";
9
+ import { ENV_API_KEY, ENV_HOSTNAME, ENV_SECRET } from "./canary-keys";
10
+
11
+ export interface UrlCanaryParameters extends CanaryParameters {
12
+ readonly hostname: string;
13
+ readonly apiKeyId?: string;
14
+ }
15
+
16
+ export class UrlCanary extends DigitrafficCanary {
17
+ constructor(
18
+ stack: Construct,
19
+ role: Role,
20
+ params: UrlCanaryParameters,
21
+ secret?: ISecret
22
+ ) {
23
+ const canaryName = `${params.name}-url`;
24
+ const environmentVariables: LambdaEnvironment = {};
25
+ environmentVariables[ENV_HOSTNAME] = params.hostname;
26
+
27
+ if (params.secret) {
28
+ environmentVariables[ENV_SECRET] = params.secret;
29
+ }
30
+
31
+ if (params.apiKeyId) {
32
+ environmentVariables[ENV_API_KEY] = params.apiKeyId;
33
+ }
34
+
35
+ if (secret) {
36
+ secret.grantRead(role);
37
+ }
38
+
39
+ // the handler code is defined at the actual project using this
40
+ super(stack, canaryName, role, params, environmentVariables);
41
+ }
42
+
43
+ static create(
44
+ stack: DigitrafficStack,
45
+ role: Role,
46
+ publicApi: DigitrafficRestApi,
47
+ params: Partial<UrlCanaryParameters>
48
+ ): UrlCanary {
49
+ return new UrlCanary(stack, role, {
50
+ ...{
51
+ handler: `${params.name}.handler`,
52
+ hostname: publicApi.hostname(),
53
+ apiKeyId: this.getApiKey(publicApi),
54
+ },
55
+ ...params,
56
+ } as UrlCanaryParameters);
57
+ }
58
+
59
+ static getApiKey(publicApi: DigitrafficRestApi): string | undefined {
60
+ const apiKeys = publicApi.apiKeyIds;
61
+
62
+ if (apiKeys.length > 1) {
63
+ console.info("rest api has more than one api key");
64
+ }
65
+
66
+ if (apiKeys.length === 0) {
67
+ console.info("rest api has no api keys");
68
+ return undefined;
69
+ }
70
+
71
+ // always use first api key
72
+ return publicApi.apiKeyIds[0];
73
+ }
74
+ }
@@ -0,0 +1,366 @@
1
+ import { IncomingMessage, RequestOptions } from "http";
2
+ import { Asserter } from "../../../test/asserter";
3
+
4
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
5
+ const synthetics = require("Synthetics");
6
+ import zlib = require("zlib");
7
+ import { MediaType } from "../../types/mediatypes";
8
+ import { getApiKeyFromAPIGateway } from "../../runtime/apikey";
9
+ import { FeatureCollection } from "geojson";
10
+ import { isValidGeoJson } from "../../../utils/geometry";
11
+ import { getEnvVariable } from "../../../utils/utils";
12
+ import { ENV_API_KEY, ENV_HOSTNAME } from "./canary-keys";
13
+
14
+ export const API_KEY_HEADER = "x-api-key";
15
+
16
+ const baseHeaders = {
17
+ "Digitraffic-User": "internal-digitraffic-canary",
18
+ "Accept-Encoding": "gzip",
19
+ Accept: "*/*",
20
+ } as Record<string, string>;
21
+
22
+ type CheckerFunction = (Res: IncomingMessage) => void;
23
+ type JsonCheckerFunction<T> = (
24
+ json: T,
25
+ body: string,
26
+ message: IncomingMessage
27
+ ) => void;
28
+
29
+ export class UrlChecker {
30
+ private readonly requestOptions: RequestOptions;
31
+
32
+ constructor(hostname: string, apiKey?: string) {
33
+ const headers = { ...baseHeaders };
34
+
35
+ if (apiKey) {
36
+ headers[API_KEY_HEADER] = apiKey;
37
+ }
38
+
39
+ this.requestOptions = {
40
+ hostname,
41
+ method: "GET",
42
+ protocol: "https:",
43
+ headers,
44
+ };
45
+
46
+ synthetics.getConfiguration().disableRequestMetrics();
47
+
48
+ synthetics
49
+ .getConfiguration()
50
+ .withIncludeRequestBody(false)
51
+ .withIncludeRequestHeaders(false)
52
+ .withIncludeResponseBody(false)
53
+ .withIncludeResponseHeaders(false)
54
+ .withFailedCanaryMetric(true);
55
+ }
56
+
57
+ static create(hostname: string, apiKeyId: string): Promise<UrlChecker> {
58
+ return getApiKeyFromAPIGateway(apiKeyId).then((apiKey) => {
59
+ return new UrlChecker(hostname, apiKey.value);
60
+ });
61
+ }
62
+
63
+ static createV2(): Promise<UrlChecker> {
64
+ return this.create(
65
+ getEnvVariable(ENV_HOSTNAME),
66
+ getEnvVariable(ENV_API_KEY)
67
+ );
68
+ }
69
+
70
+ expectStatus<T>(
71
+ statusCode: number,
72
+ url: string,
73
+ callback: JsonCheckerFunction<T>
74
+ ): Promise<void> {
75
+ const requestOptions = {
76
+ ...this.requestOptions,
77
+ ...{
78
+ path: url,
79
+ },
80
+ };
81
+
82
+ return synthetics.executeHttpStep(
83
+ `Verify ${statusCode} for ${url.replace(/auth=.*/, "")}`,
84
+ requestOptions,
85
+ callback
86
+ );
87
+ }
88
+
89
+ expect200<T>(
90
+ url: string,
91
+ ...callbacks: JsonCheckerFunction<T>[]
92
+ ): Promise<void> {
93
+ const callback = async (
94
+ json: T,
95
+ body: string,
96
+ res: IncomingMessage
97
+ ) => {
98
+ await Promise.allSettled(callbacks.map((c) => c(json, body, res)));
99
+ };
100
+
101
+ return this.expectStatus(200, url, callback);
102
+ }
103
+
104
+ expect404(url: string): Promise<void> {
105
+ const requestOptions = {
106
+ ...this.requestOptions,
107
+ ...{
108
+ path: url,
109
+ },
110
+ };
111
+
112
+ return synthetics.executeHttpStep(
113
+ `Verify 404 for ${url}`,
114
+ requestOptions,
115
+ validateStatusCodeAndContentType(404, MediaType.TEXT_PLAIN)
116
+ );
117
+ }
118
+
119
+ expect400(url: string): Promise<void> {
120
+ const requestOptions = {
121
+ ...this.requestOptions,
122
+ ...{
123
+ path: url,
124
+ },
125
+ };
126
+
127
+ return synthetics.executeHttpStep(
128
+ `Verify 400 for ${url}`,
129
+ requestOptions,
130
+ validateStatusCodeAndContentType(400, MediaType.TEXT_PLAIN)
131
+ );
132
+ }
133
+
134
+ expect403WithoutApiKey(url: string, mediaType?: MediaType): Promise<void> {
135
+ if (
136
+ !this.requestOptions.headers ||
137
+ !this.requestOptions.headers[API_KEY_HEADER]
138
+ ) {
139
+ console.error("No api key defined");
140
+ }
141
+
142
+ const requestOptions = {
143
+ ...this.requestOptions,
144
+ ...{
145
+ path: url,
146
+ headers: baseHeaders,
147
+ },
148
+ };
149
+
150
+ return synthetics.executeHttpStep(
151
+ `Verify 403 for ${url}`,
152
+ requestOptions,
153
+ validateStatusCodeAndContentType(
154
+ 403,
155
+ mediaType || MediaType.APPLICATION_JSON
156
+ )
157
+ );
158
+ }
159
+
160
+ done(): string {
161
+ return "Canary successful";
162
+ }
163
+ }
164
+
165
+ async function getResponseBody(response: IncomingMessage): Promise<string> {
166
+ const body = await getBodyFromResponse(response);
167
+
168
+ if (response.headers["content-encoding"] === "gzip") {
169
+ try {
170
+ return zlib.gunzipSync(body).toString();
171
+ } catch (e) {
172
+ console.info("error " + JSON.stringify(e));
173
+ }
174
+ }
175
+
176
+ return body.toString();
177
+ }
178
+
179
+ function getBodyFromResponse(response: IncomingMessage): Promise<string> {
180
+ return new Promise((resolve: (value: string) => void) => {
181
+ const buffers: Buffer[] = [];
182
+
183
+ response.on("data", (data: Buffer) => {
184
+ buffers.push(data);
185
+ });
186
+
187
+ response.on("end", () => {
188
+ resolve(Buffer.concat(buffers).toString());
189
+ });
190
+ });
191
+ }
192
+
193
+ /**
194
+ * Returns function, that validates that the status code and content-type from response are the given values
195
+ * @param statusCode
196
+ * @param contentType
197
+ */
198
+ function validateStatusCodeAndContentType(
199
+ statusCode: number,
200
+ contentType: MediaType
201
+ ): (Res: IncomingMessage) => Promise<void> {
202
+ return (res: IncomingMessage) => {
203
+ return new Promise((resolve) => {
204
+ if (res.statusCode !== statusCode) {
205
+ throw new Error(`${res.statusCode} ${res.statusMessage}`);
206
+ }
207
+
208
+ if (res.headers["content-type"] !== contentType) {
209
+ throw new Error(
210
+ "Wrong content-type " + res.headers["content-type"]
211
+ );
212
+ }
213
+
214
+ resolve();
215
+ });
216
+ };
217
+ }
218
+
219
+ // DEPRECATED
220
+ export class ResponseChecker {
221
+ private readonly contentType;
222
+ private checkCors = true;
223
+
224
+ constructor(contentType: string) {
225
+ this.contentType = contentType;
226
+ }
227
+
228
+ static forJson(): ResponseChecker {
229
+ return new ResponseChecker(MediaType.APPLICATION_JSON);
230
+ }
231
+
232
+ static forCSV(): ResponseChecker {
233
+ return new ResponseChecker(MediaType.TEXT_CSV);
234
+ }
235
+
236
+ static forGeojson(): ResponseChecker {
237
+ return new ResponseChecker(MediaType.APPLICATION_GEOJSON);
238
+ }
239
+
240
+ static forJpeg(): ResponseChecker {
241
+ return new ResponseChecker(MediaType.IMAGE_JPEG);
242
+ }
243
+
244
+ check(): CheckerFunction {
245
+ return this.responseChecker(() => {
246
+ // no need to do anything
247
+ });
248
+ }
249
+
250
+ checkJson<T>(
251
+ fn: (json: T, body: string, res: IncomingMessage) => void
252
+ ): CheckerFunction {
253
+ return this.responseChecker((body: string, res: IncomingMessage) => {
254
+ fn(JSON.parse(body), body, res);
255
+ });
256
+ }
257
+
258
+ responseChecker(
259
+ fn: (body: string, res: IncomingMessage) => void
260
+ ): CheckerFunction {
261
+ return async (res: IncomingMessage): Promise<void> => {
262
+ if (!res.statusCode) {
263
+ throw new Error("statusCode missing");
264
+ }
265
+
266
+ if (res.statusCode < 200 || res.statusCode > 299) {
267
+ throw new Error(`${res.statusCode} ${res.statusMessage}`);
268
+ }
269
+
270
+ if (this.checkCors && !res.headers["access-control-allow-origin"]) {
271
+ throw new Error("CORS missing");
272
+ }
273
+
274
+ if (res.headers["content-type"] !== this.contentType) {
275
+ throw new Error(
276
+ "Wrong content-type " + res.headers["content-type"]
277
+ );
278
+ }
279
+
280
+ const body = await getResponseBody(res);
281
+
282
+ fn(body, res);
283
+ };
284
+ }
285
+ }
286
+
287
+ export class ContentChecker {
288
+ static checkJson<T>(
289
+ fn: (json: T, body: string, res: IncomingMessage) => void
290
+ ): CheckerFunction {
291
+ return async (res: IncomingMessage): Promise<void> => {
292
+ const body = await getResponseBody(res);
293
+
294
+ fn(JSON.parse(body), body, res);
295
+ };
296
+ }
297
+
298
+ static checkResponse(
299
+ fn: (body: string, res: IncomingMessage) => void
300
+ ): CheckerFunction {
301
+ return async (res: IncomingMessage): Promise<void> => {
302
+ const body = await getResponseBody(res);
303
+
304
+ fn(body, res);
305
+ };
306
+ }
307
+ }
308
+
309
+ export class ContentTypeChecker {
310
+ static checkContentType(contentType: MediaType) {
311
+ return (res: IncomingMessage) => {
312
+ if (!res.statusCode) {
313
+ throw new Error("statusCode missing");
314
+ }
315
+
316
+ if (res.statusCode < 200 || res.statusCode > 299) {
317
+ throw new Error(`${res.statusCode} ${res.statusMessage}`);
318
+ }
319
+
320
+ if (!res.headers["access-control-allow-origin"]) {
321
+ throw new Error("CORS missing");
322
+ }
323
+
324
+ if (res.headers["content-type"] !== contentType) {
325
+ throw new Error(
326
+ "Wrong content-type " + res.headers["content-type"]
327
+ );
328
+ }
329
+ };
330
+ }
331
+ }
332
+
333
+ export class GeoJsonChecker {
334
+ static validFeatureCollection(
335
+ fn?: (json: FeatureCollection) => void
336
+ ): CheckerFunction {
337
+ return ResponseChecker.forGeojson().checkJson(
338
+ (json: FeatureCollection) => {
339
+ Asserter.assertEquals(json.type, "FeatureCollection");
340
+ Asserter.assertTrue(isValidGeoJson(json));
341
+
342
+ if (fn) {
343
+ fn(json);
344
+ }
345
+ }
346
+ );
347
+ }
348
+ }
349
+
350
+ export class HeaderChecker {
351
+ static checkHeaderExists(headerName: string): CheckerFunction {
352
+ return (res: IncomingMessage) => {
353
+ if (!res.headers[headerName]) {
354
+ throw new Error("Missing header: " + headerName);
355
+ }
356
+ };
357
+ }
358
+
359
+ static checkHeaderMissing(headerName: string): CheckerFunction {
360
+ return (res: IncomingMessage) => {
361
+ if (res.headers[headerName]) {
362
+ throw new Error("Header should not exist: " + headerName);
363
+ }
364
+ };
365
+ }
366
+ }
@@ -0,0 +1,124 @@
1
+ import {Construct} from "constructs";
2
+ import {CfnDocumentationPart, Resource} from "aws-cdk-lib/aws-apigateway";
3
+
4
+ // Documentation parts are objects that describe an API Gateway API or parts of an API
5
+ // https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-documenting-api.html
6
+
7
+ /**
8
+ * Add description to a query parameter
9
+ * @param name query parameter name
10
+ * @param description query parameter description
11
+ * @param resource REST API resource
12
+ * @param stack CloudFormation stack
13
+ *
14
+ * @deprecated Use DigitrafficRestApi.documentResource
15
+ */
16
+ export function addQueryParameterDescription(name: string,
17
+ description: string,
18
+ resource: Resource,
19
+ stack: Construct) {
20
+ new CfnDocumentationPart(stack, `${name}Documentation`, {
21
+ restApiId: resource.api.restApiId,
22
+ location: {
23
+ type: 'QUERY_PARAMETER',
24
+ name,
25
+ path: resource.path,
26
+ },
27
+ properties: JSON.stringify({description}),
28
+ });
29
+ }
30
+
31
+ /**
32
+ * Add a documentation part to a method
33
+ * @param methodDescription
34
+ * @param documentationProperties
35
+ * @param resource REST API resource
36
+ * @param stack CloudFormation stack
37
+ */
38
+ export function addDocumentation(methodDescription: string,
39
+ documentationProperties: object,
40
+ resource: Resource,
41
+ stack: Construct) {
42
+ new CfnDocumentationPart(stack, `${methodDescription}Documentation`, {
43
+ restApiId: resource.api.restApiId,
44
+ location: {
45
+ type: 'METHOD',
46
+ path: resource.path,
47
+ },
48
+ properties: JSON.stringify(documentationProperties),
49
+ });
50
+ }
51
+
52
+ /**
53
+ * Adds OpenAPI tags to an API method
54
+ * @param methodDescription Description of API method
55
+ * @param tags OpenAPI tags
56
+ * @param resource REST API resource
57
+ * @param stack CloudFormation stack
58
+ */
59
+ export function addTags(methodDescription: string,
60
+ tags: string[],
61
+ resource: Resource,
62
+ stack: Construct) {
63
+ addDocumentation(methodDescription, {tags}, resource, stack);
64
+ }
65
+
66
+ /**
67
+ * Adds OpenAPI tags and a method summary to an API method
68
+ *
69
+ * @deprecated Use DigitrafficRestApi.documentResource
70
+ *
71
+ * @param methodDescription Description of API method
72
+ * @param tags OpenAPI tags
73
+ * @param summary OpenAPI summary
74
+ * @param resource REST API resource
75
+ * @param stack CloudFormation stack
76
+ */
77
+ export function addTagsAndSummary(
78
+ methodDescription: string,
79
+ tags: string[],
80
+ summary: string,
81
+ resource: Resource,
82
+ stack: Construct,
83
+ ) {
84
+ addDocumentation(methodDescription, {tags, summary}, resource, stack);
85
+ }
86
+
87
+ export interface DocumentationProperties {
88
+ description?: string
89
+ tags?: string[]
90
+ summary?: string
91
+ deprecated?: boolean
92
+ }
93
+
94
+ export class DocumentationPart {
95
+ readonly parameterName: string;
96
+ readonly type: string;
97
+ readonly documentationProperties: DocumentationProperties;
98
+
99
+ private constructor(parameterName: string, documentationProperties: DocumentationProperties, type: string) {
100
+ this.parameterName = parameterName;
101
+ this.documentationProperties = documentationProperties;
102
+ this.type = type;
103
+ }
104
+
105
+ deprecated(note: string): DocumentationPart {
106
+ // deprecated is not supported ATM: https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-known-issues.html
107
+ this.documentationProperties.deprecated = true;
108
+ this.documentationProperties.summary+= '. ' + note;
109
+
110
+ return this;
111
+ }
112
+
113
+ static queryParameter(parameterName: string, description: string) {
114
+ return new DocumentationPart(parameterName, {description}, "QUERY_PARAMETER");
115
+ }
116
+
117
+ static pathParameter(parameterName: string, description: string) {
118
+ return new DocumentationPart(parameterName, {description}, "PATH_PARAMETER");
119
+ }
120
+
121
+ static method(tags: string[], name: string, summary: string) {
122
+ return new DocumentationPart(name, {tags, summary}, "METHOD");
123
+ }
124
+ }
@@ -0,0 +1,59 @@
1
+ import { Rule, Schedule } from "aws-cdk-lib/aws-events";
2
+ import { Duration } from "aws-cdk-lib";
3
+ import { LambdaFunction } from "aws-cdk-lib/aws-events-targets";
4
+ import { Function as AWSFunction } from "aws-cdk-lib/aws-lambda";
5
+ import { Construct } from "constructs";
6
+
7
+ export class Scheduler extends Rule {
8
+ constructor(
9
+ stack: Construct,
10
+ ruleName: string,
11
+ schedule: Schedule,
12
+ lambda?: AWSFunction
13
+ ) {
14
+ super(stack, ruleName, { ruleName, schedule });
15
+
16
+ if (lambda) {
17
+ this.addTarget(new LambdaFunction(lambda));
18
+ }
19
+ }
20
+
21
+ static everyMinute(
22
+ stack: Construct,
23
+ ruleName: string,
24
+ lambda?: AWSFunction
25
+ ) {
26
+ return Scheduler.every(stack, ruleName, Duration.minutes(1), lambda);
27
+ }
28
+
29
+ static everyMinutes(
30
+ stack: Construct,
31
+ ruleName: string,
32
+ minutes: number,
33
+ lambda?: AWSFunction
34
+ ) {
35
+ return Scheduler.every(
36
+ stack,
37
+ ruleName,
38
+ Duration.minutes(minutes),
39
+ lambda
40
+ );
41
+ }
42
+
43
+ static everyHour(stack: Construct, ruleName: string, lambda?: AWSFunction) {
44
+ return Scheduler.every(stack, ruleName, Duration.hours(1), lambda);
45
+ }
46
+
47
+ static everyDay(stack: Construct, ruleName: string, lambda?: AWSFunction) {
48
+ return Scheduler.every(stack, ruleName, Duration.days(1), lambda);
49
+ }
50
+
51
+ static every(
52
+ stack: Construct,
53
+ ruleName: string,
54
+ duration: Duration,
55
+ lambda?: AWSFunction
56
+ ) {
57
+ return new Scheduler(stack, ruleName, Schedule.rate(duration), lambda);
58
+ }
59
+ }
@@ -0,0 +1,38 @@
1
+ import {Construct} from "constructs";
2
+ import {Rule} from "aws-cdk-lib/aws-events";
3
+ import {ITopic} from "aws-cdk-lib/aws-sns";
4
+ import {SnsTopic} from "aws-cdk-lib/aws-events-targets";
5
+
6
+ /**
7
+ * Automatic rule for Security Hub. Send notification to given topic if the following conditions apply:
8
+ * * There is a finding with a status of NEW
9
+ * * It has severity of HIGH or CRITICAL
10
+ * * It is in a FAILED state
11
+ */
12
+ export class DigitrafficSecurityRule extends Rule {
13
+ constructor(scope: Construct, topic: ITopic) {
14
+ const ruleName = 'SecurityHubRule';
15
+ super(scope, ruleName, {
16
+ ruleName,
17
+ eventPattern: {
18
+ source: ['aws.securityhub'],
19
+ detailType: ["Security Hub Findings - Imported"],
20
+ detail: {
21
+ findings: {
22
+ "Compliance": {
23
+ "Status": ["FAILED"],
24
+ },
25
+ "Workflow": {
26
+ "Status": ["NEW"],
27
+ },
28
+ "Severity": {
29
+ "Label": ["HIGH", "CRITICAL"],
30
+ },
31
+ },
32
+ },
33
+ },
34
+ });
35
+
36
+ this.addTarget(new SnsTopic(topic));
37
+ }
38
+ }