skuba 4.3.0 → 4.4.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 (53) hide show
  1. package/README.md +1 -1
  2. package/jest/moduleNameMapper.test.ts +7 -8
  3. package/lib/api/jest/index.d.ts +23 -24
  4. package/lib/api/jest/index.js.map +1 -1
  5. package/lib/cli/configure/ensureTemplateCompletion.js +2 -2
  6. package/lib/cli/configure/ensureTemplateCompletion.js.map +1 -1
  7. package/lib/cli/configure/modules/tsconfig.js +3 -2
  8. package/lib/cli/configure/modules/tsconfig.js.map +1 -1
  9. package/lib/cli/configure/processing/package.d.ts +1 -1
  10. package/lib/cli/configure/processing/package.js +8 -10
  11. package/lib/cli/configure/processing/package.js.map +1 -1
  12. package/lib/cli/test/reporters/github/annotations.js +4 -1
  13. package/lib/cli/test/reporters/github/annotations.js.map +1 -1
  14. package/lib/utils/args.d.ts +1 -1
  15. package/lib/utils/version.js +46 -3
  16. package/lib/utils/version.js.map +1 -1
  17. package/package.json +50 -21
  18. package/template/base/_.eslintignore +5 -2
  19. package/template/base/_.gitignore +3 -2
  20. package/template/base/_.prettierignore +5 -2
  21. package/template/express-rest-api/.buildkite/pipeline.yml +4 -4
  22. package/template/express-rest-api/.gantry/common.yml +1 -1
  23. package/template/express-rest-api/README.md +3 -7
  24. package/template/express-rest-api/package.json +1 -1
  25. package/template/greeter/.buildkite/pipeline.yml +1 -1
  26. package/template/greeter/README.md +4 -8
  27. package/template/koa-rest-api/.buildkite/pipeline.yml +4 -4
  28. package/template/koa-rest-api/.gantry/common.yml +1 -1
  29. package/template/koa-rest-api/README.md +3 -7
  30. package/template/koa-rest-api/package.json +6 -8
  31. package/template/koa-rest-api/src/api/jobs/postJob.test.ts +7 -3
  32. package/template/koa-rest-api/src/framework/server.test.ts +18 -18
  33. package/template/koa-rest-api/src/framework/validation.test.ts +15 -6
  34. package/template/koa-rest-api/src/storage/jobs.ts +2 -2
  35. package/template/lambda-sqs-worker/.buildkite/pipeline.yml +2 -3
  36. package/template/lambda-sqs-worker/README.md +3 -7
  37. package/template/lambda-sqs-worker/package.json +6 -2
  38. package/template/lambda-sqs-worker/serverless.yml +17 -5
  39. package/template/lambda-sqs-worker/src/app.test.ts +22 -20
  40. package/template/lambda-sqs-worker/src/app.ts +6 -6
  41. package/template/lambda-sqs-worker/src/config.ts +3 -0
  42. package/template/lambda-sqs-worker/src/framework/handler.test.ts +10 -10
  43. package/template/lambda-sqs-worker/src/framework/handler.ts +31 -8
  44. package/template/lambda-sqs-worker/src/framework/metrics.ts +10 -6
  45. package/template/lambda-sqs-worker/src/framework/validation.test.ts +17 -10
  46. package/template/lambda-sqs-worker/src/services/jobScorer.test.ts +2 -2
  47. package/template/lambda-sqs-worker/src/services/pipelineEventSender.test.ts +1 -1
  48. package/template/lambda-sqs-worker-cdk/.buildkite/pipeline.yml +2 -2
  49. package/template/lambda-sqs-worker-cdk/infra/__snapshots__/appStack.test.ts.snap +220 -220
  50. package/template/oss-npm-package/README.md +5 -4
  51. package/template/oss-npm-package/_package.json +1 -1
  52. package/template/private-npm-package/README.md +4 -7
  53. package/template/private-npm-package/_package.json +1 -1
@@ -19,15 +19,11 @@ Next steps:
19
19
  see the [Gantry] documentation for more information.
20
20
  6. [ ] Push local commits to the upstream GitHub branch.
21
21
  7. [ ] Configure [GitHub repository settings].
22
- 8. [ ] Keep dependencies up to date with [Renovate];
23
- request installation in [SEEK-Jobs/renovate].
24
- 9. [ ] Delete this checklist 😌.
22
+ 8. [ ] Delete this checklist 😌.
25
23
 
26
- [arm64]: https://seek-oss.github.io/skuba/docs/deep-dives/arm64.html
24
+ [arm64 guide]: https://seek-oss.github.io/skuba/docs/deep-dives/arm64.html
27
25
  [builds at seek]: https://builds-at-seek.ssod.skinfra.xyz
28
26
  [github repository settings]: https://github.com/<%-orgName%>/<%-repoName%>/settings
29
- [renovate]: https://github.com/apps/renovate
30
- [seek-jobs/renovate]: https://github.com/SEEK-Jobs/renovate
31
27
 
32
28
  ## Design
33
29
 
@@ -109,5 +105,5 @@ TODO: add support links for the prod environment.
109
105
 
110
106
  [codedeploy]: https://docs.aws.amazon.com/codedeploy
111
107
  [express]: https://expressjs.com
112
- [gantry]: https://gantry.ssod.skinfra.xyz
108
+ [gantry]: https://backstage.myseek.xyz/docs/default/component/gantry/
113
109
  [technology strategy]: https://tech-strategy.ssod.skinfra.xyz
@@ -8,7 +8,7 @@
8
8
  "@types/express": "^4.17.13",
9
9
  "@types/node": "^16.0.0",
10
10
  "@types/supertest": "^2.0.11",
11
- "pino-pretty": "^8.0.0",
11
+ "pino-pretty": "^9.0.0",
12
12
  "skuba": "*",
13
13
  "supertest": "^6.1.6"
14
14
  },
@@ -32,6 +32,6 @@ steps:
32
32
  - *aws-sm
33
33
  - *private-npm
34
34
  - *docker-ecr-cache
35
- - docker-compose#v3.9.0:
35
+ - docker-compose#v3.10.0:
36
36
  run: app
37
37
  timeout_in_minutes: 10
@@ -17,15 +17,11 @@ Next steps:
17
17
  see [Builds at SEEK] for more information.
18
18
  5. [ ] Push local commits to the upstream GitHub branch.
19
19
  6. [ ] Configure [GitHub repository settings].
20
- 7. [ ] Keep dependencies up to date with [Renovate];
21
- request installation in [SEEK-Jobs/renovate].
22
- 8. [ ] Delete this checklist 😌.
20
+ 7. [ ] Delete this checklist 😌.
23
21
 
24
- [arm64]: https://seek-oss.github.io/skuba/docs/deep-dives/arm64.html
22
+ [arm64 guide]: https://seek-oss.github.io/skuba/docs/deep-dives/arm64.html
25
23
  [builds at seek]: https://builds-at-seek.ssod.skinfra.xyz
26
24
  [github repository settings]: https://github.com/<%-orgName%>/<%-repoName%>/settings
27
- [renovate]: https://github.com/apps/renovate
28
- [seek-jobs/renovate]: https://github.com/SEEK-Jobs/renovate
29
25
 
30
26
  ## Design
31
27
 
@@ -35,8 +31,8 @@ or serve as a starting point for a backend project if the other built-in templat
35
31
 
36
32
  It's a barebones Node.js application that comprises:
37
33
 
38
- - A [src/app.ts] that can be run locally to greet the user
39
- - A [src/app.test.ts] that demonstrates rudimentary Jest usage
34
+ - A [src/app.ts](src/app.ts) that can be run locally to greet the user
35
+ - A [src/app.test.ts](src/app.test.ts) that demonstrates rudimentary Jest usage
40
36
 
41
37
  ## Development
42
38
 
@@ -51,7 +51,7 @@ steps:
51
51
  - *aws-sm
52
52
  - *private-npm
53
53
  - *docker-ecr-cache
54
- - docker-compose#v3.9.0:
54
+ - docker-compose#v3.10.0:
55
55
  run: app
56
56
  timeout_in_minutes: 10
57
57
 
@@ -61,7 +61,7 @@ steps:
61
61
  - *aws-sm
62
62
  - *private-npm
63
63
  - *docker-ecr-cache
64
- - seek-jobs/gantry#v1.8.1:
64
+ - seek-jobs/gantry#v2.0.0:
65
65
  command: build
66
66
  file: gantry.build.yml
67
67
  region: <%- region %>
@@ -78,7 +78,7 @@ steps:
78
78
  concurrency_group: <%- teamName %>/deploy/gantry/<%- devGantryEnvironmentName %>
79
79
  key: deploy-dev
80
80
  plugins:
81
- - seek-jobs/gantry#v1.8.1:
81
+ - seek-jobs/gantry#v2.0.0:
82
82
  command: apply
83
83
  environment: <%- devGantryEnvironmentName %>
84
84
  file: gantry.apply.yml
@@ -93,7 +93,7 @@ steps:
93
93
  concurrency_group: <%- teamName %>/deploy/gantry/<%- prodGantryEnvironmentName %>
94
94
  depends_on: deploy-dev
95
95
  plugins:
96
- - seek-jobs/gantry#v1.8.1:
96
+ - seek-jobs/gantry#v2.0.0:
97
97
  command: apply
98
98
  environment: <%- prodGantryEnvironmentName %>
99
99
  file: gantry.apply.yml
@@ -4,7 +4,7 @@ image: '{{values "prodAccountId"}}.dkr.ecr.<%- region %>.amazonaws.com/{{values
4
4
  service: '<%- serviceName %>'
5
5
 
6
6
  # TODO: enable Datadog agent
7
- # https://gantry.ssod.skinfra.xyz/docs/v1/resources/service.html#datadogSecretId
7
+ # https://backstage.myseek.xyz/docs/default/component/gantry/v1/reference/resources/service/#datadogSecretId
8
8
  # datadogSecretId: arn:aws:secretsmanager:<%- region %>:<aws-account-id>:secret:<secret-name>
9
9
 
10
10
  tags:
@@ -19,15 +19,11 @@ Next steps:
19
19
  see the [Gantry] documentation for more information.
20
20
  6. [ ] Push local commits to the upstream GitHub branch.
21
21
  7. [ ] Configure [GitHub repository settings].
22
- 8. [ ] Keep dependencies up to date with [Renovate];
23
- request installation in [SEEK-Jobs/renovate].
24
- 9. [ ] Delete this checklist 😌.
22
+ 8. [ ] Delete this checklist 😌.
25
23
 
26
- [arm64]: https://seek-oss.github.io/skuba/docs/deep-dives/arm64.html
24
+ [arm64 guide]: https://seek-oss.github.io/skuba/docs/deep-dives/arm64.html
27
25
  [builds at seek]: https://builds-at-seek.ssod.skinfra.xyz
28
26
  [github repository settings]: https://github.com/<%-orgName%>/<%-repoName%>/settings
29
- [renovate]: https://github.com/apps/renovate
30
- [seek-jobs/renovate]: https://github.com/SEEK-Jobs/renovate
31
27
 
32
28
  ## Design
33
29
 
@@ -113,6 +109,6 @@ TODO: add support links for the prod environment.
113
109
  -->
114
110
 
115
111
  [codedeploy]: https://docs.aws.amazon.com/codedeploy
116
- [gantry]: https://gantry.ssod.skinfra.xyz
112
+ [gantry]: https://backstage.myseek.xyz/docs/default/component/gantry/
117
113
  [koa]: https://koajs.com
118
114
  [technology strategy]: https://tech-strategy.ssod.skinfra.xyz
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "dependencies": {
3
- "@koa/router": "^10.1.1",
3
+ "@koa/router": "^12.0.0",
4
4
  "@opentelemetry/api": "^1.1.0",
5
5
  "@opentelemetry/exporter-collector-grpc": "^0.25.0",
6
- "@opentelemetry/instrumentation-aws-sdk": "^0.8.0",
7
- "@opentelemetry/instrumentation-http": "^0.29.2",
8
- "@opentelemetry/sdk-node": "^0.29.2",
6
+ "@opentelemetry/instrumentation-aws-sdk": "^0.9.0",
7
+ "@opentelemetry/instrumentation-http": "^0.32.0",
8
+ "@opentelemetry/sdk-node": "^0.32.0",
9
9
  "@seek/logger": "^5.0.1",
10
10
  "aws-sdk": "^2.1039.0",
11
11
  "hot-shots": "^9.0.0",
@@ -16,8 +16,7 @@
16
16
  "runtypes-filter": "^0.6.0",
17
17
  "seek-datadog-custom-metrics": "^4.0.0",
18
18
  "seek-koala": "^6.0.0",
19
- "skuba-dive": "^2.0.0",
20
- "uuid": "^8.3.2"
19
+ "skuba-dive": "^2.0.0"
21
20
  },
22
21
  "devDependencies": {
23
22
  "@types/chance": "^1.1.3",
@@ -26,9 +25,8 @@
26
25
  "@types/koa__router": "^8.0.8",
27
26
  "@types/node": "^16.0.0",
28
27
  "@types/supertest": "^2.0.11",
29
- "@types/uuid": "^8.3.1",
30
28
  "chance": "^1.1.8",
31
- "pino-pretty": "^8.0.0",
29
+ "pino-pretty": "^9.0.0",
32
30
  "skuba": "*",
33
31
  "supertest": "^6.1.6"
34
32
  },
@@ -26,9 +26,13 @@ describe('postJobHandler', () => {
26
26
  .send(jobInput)
27
27
  .expect(422)
28
28
  .expect(({ text }) =>
29
- expect(text).toMatchInlineSnapshot(
30
- `"Expected { hirer: { id: string; }; }, but was incompatible"`,
31
- ),
29
+ expect(text).toMatchInlineSnapshot(`
30
+ "Validation failed:
31
+ {
32
+ "hirer": "Expected { id: string; }, but was missing"
33
+ }.
34
+ Object should match { hirer: { id: string; }; }"
35
+ `),
32
36
  );
33
37
  });
34
38
  });
@@ -29,9 +29,9 @@ describe('createApp', () => {
29
29
  .expect('server', /.+/)
30
30
  .expect('x-api-version', /.+/);
31
31
 
32
- expect(logger.error).not.toBeCalled();
32
+ expect(logger.error).not.toHaveBeenCalled();
33
33
 
34
- expect(logger.info).not.toBeCalled();
34
+ expect(logger.info).not.toHaveBeenCalled();
35
35
 
36
36
  metricsClient.expectTagSubset(['env:test', 'version:test']);
37
37
  metricsClient.expectTagSubset([
@@ -51,9 +51,9 @@ describe('createApp', () => {
51
51
  .expect('server', /.+/)
52
52
  .expect('x-api-version', /.+/);
53
53
 
54
- expect(logger.error).not.toBeCalled();
54
+ expect(logger.error).not.toHaveBeenCalled();
55
55
 
56
- expect(logger.info).not.toBeCalled();
56
+ expect(logger.info).not.toHaveBeenCalled();
57
57
 
58
58
  metricsClient.expectTagSubset([
59
59
  'http_method:put',
@@ -72,9 +72,9 @@ describe('createApp', () => {
72
72
  .expect('server', /.+/)
73
73
  .expect('x-api-version', /.+/);
74
74
 
75
- expect(logger.error).not.toBeCalled();
75
+ expect(logger.error).not.toHaveBeenCalled();
76
76
 
77
- expect(logger.info).nthCalledWith(
77
+ expect(logger.info).toHaveBeenNthCalledWith(
78
78
  1,
79
79
  expect.objectContaining({ status: 404 }),
80
80
  'Client error',
@@ -102,9 +102,9 @@ describe('createApp', () => {
102
102
  .expect('server', /.+/)
103
103
  .expect('x-api-version', /.+/);
104
104
 
105
- expect(logger.error).not.toBeCalled();
105
+ expect(logger.error).not.toHaveBeenCalled();
106
106
 
107
- expect(logger.info).nthCalledWith(
107
+ expect(logger.info).toHaveBeenNthCalledWith(
108
108
  1,
109
109
  expect.objectContaining({ status: 400 }),
110
110
  'Client error',
@@ -129,9 +129,9 @@ describe('createApp', () => {
129
129
  .expect('server', /.+/)
130
130
  .expect('x-api-version', /.+/);
131
131
 
132
- expect(logger.error).not.toBeCalled();
132
+ expect(logger.error).not.toHaveBeenCalled();
133
133
 
134
- expect(logger.info).nthCalledWith(
134
+ expect(logger.info).toHaveBeenNthCalledWith(
135
135
  1,
136
136
  expect.objectContaining({ err: expect.any(Error), status: 400 }),
137
137
  'Client error',
@@ -156,13 +156,13 @@ describe('createApp', () => {
156
156
  .expect('server', /.+/)
157
157
  .expect('x-api-version', /.+/);
158
158
 
159
- expect(logger.error).nthCalledWith(
159
+ expect(logger.error).toHaveBeenNthCalledWith(
160
160
  1,
161
161
  expect.objectContaining({ err: expect.any(Error), status: 500 }),
162
162
  'Server error',
163
163
  );
164
164
 
165
- expect(logger.info).not.toBeCalled();
165
+ expect(logger.info).not.toHaveBeenCalled();
166
166
 
167
167
  metricsClient.expectTagSubset([
168
168
  'http_method:get',
@@ -185,13 +185,13 @@ describe('createApp', () => {
185
185
  .expect('server', /.+/)
186
186
  .expect('x-api-version', /.+/);
187
187
 
188
- expect(logger.error).nthCalledWith(
188
+ expect(logger.error).toHaveBeenNthCalledWith(
189
189
  1,
190
190
  expect.objectContaining({ err, status: 500 }),
191
191
  'Server error',
192
192
  );
193
193
 
194
- expect(logger.info).not.toBeCalled();
194
+ expect(logger.info).not.toHaveBeenCalled();
195
195
 
196
196
  metricsClient.expectTagSubset([
197
197
  'http_method:get',
@@ -213,13 +213,13 @@ describe('createApp', () => {
213
213
  .expect('server', /.+/)
214
214
  .expect('x-api-version', /.+/);
215
215
 
216
- expect(logger.error).nthCalledWith(
216
+ expect(logger.error).toHaveBeenNthCalledWith(
217
217
  1,
218
218
  expect.objectContaining({ err: null, status: 500 }),
219
219
  'Server error',
220
220
  );
221
221
 
222
- expect(logger.info).not.toBeCalled();
222
+ expect(logger.info).not.toHaveBeenCalled();
223
223
 
224
224
  metricsClient.expectTagSubset([
225
225
  'http_method:get',
@@ -242,13 +242,13 @@ describe('createApp', () => {
242
242
  .expect('server', /.+/)
243
243
  .expect('x-api-version', /.+/);
244
244
 
245
- expect(logger.error).nthCalledWith(
245
+ expect(logger.error).toHaveBeenNthCalledWith(
246
246
  1,
247
247
  expect.objectContaining({ err, status: 500 }),
248
248
  'Server error',
249
249
  );
250
250
 
251
- expect(logger.info).not.toBeCalled();
251
+ expect(logger.info).not.toHaveBeenCalled();
252
252
 
253
253
  metricsClient.expectTagSubset([
254
254
  'http_method:get',
@@ -42,9 +42,13 @@ describe('validate', () => {
42
42
  .send({ ...idDescription, id: null })
43
43
  .expect(422)
44
44
  .expect(({ text }) =>
45
- expect(text).toMatchInlineSnapshot(
46
- `"Expected { id: string; description: string; }, but was incompatible"`,
47
- ),
45
+ expect(text).toMatchInlineSnapshot(`
46
+ "Validation failed:
47
+ {
48
+ "id": "Expected string, but was null"
49
+ }.
50
+ Object should match { id: string; description: string; }"
51
+ `),
48
52
  );
49
53
  });
50
54
 
@@ -54,8 +58,13 @@ describe('validate', () => {
54
58
  .send({})
55
59
  .expect(422)
56
60
  .expect(({ text }) =>
57
- expect(text).toMatchInlineSnapshot(
58
- `"Expected { id: string; description: string; }, but was incompatible"`,
59
- ),
61
+ expect(text).toMatchInlineSnapshot(`
62
+ "Validation failed:
63
+ {
64
+ "id": "Expected string, but was missing",
65
+ "description": "Expected string, but was missing"
66
+ }.
67
+ Object should match { id: string; description: string; }"
68
+ `),
60
69
  ));
61
70
  });
@@ -1,11 +1,11 @@
1
- import { v4 as uuidv4 } from 'uuid';
1
+ import { randomUUID } from 'crypto';
2
2
 
3
3
  import { Job, JobInput } from 'src/types/jobs';
4
4
 
5
5
  const jobStore: Record<string, Job> = {};
6
6
 
7
7
  export const createJob = (jobInput: JobInput): Promise<Job> => {
8
- const id = uuidv4();
8
+ const id = randomUUID();
9
9
 
10
10
  const job = { ...jobInput, id };
11
11
 
@@ -32,10 +32,9 @@ configs:
32
32
  - *aws-sm
33
33
  - *private-npm
34
34
  - *docker-ecr-cache
35
- - docker-compose#v3.9.0:
35
+ - docker-compose#v3.10.0:
36
36
  dependencies: false
37
37
  run: app
38
- tty: false
39
38
  retry:
40
39
  manual:
41
40
  # Only use this if you need to roll back a deployment ASAP.
@@ -61,7 +60,7 @@ steps:
61
60
  - *aws-sm
62
61
  - *private-npm
63
62
  - *docker-ecr-cache
64
- - docker-compose#v3.9.0:
63
+ - docker-compose#v3.10.0:
65
64
  run: app
66
65
  timeout_in_minutes: 10
67
66
 
@@ -15,18 +15,14 @@ Next steps:
15
15
  3. [ ] Create a new repository in the appropriate GitHub organisation.
16
16
  4. [ ] Add the repository to BuildAgency;
17
17
  see [Builds at SEEK] for more information.
18
- 5. [ ] Add deployment bucket configuration and data classification tags to [serverless.yml](serverless.yml).
18
+ 5. [ ] Add Datadog extension, deployment bucket configuration and data classification tags to [serverless.yml](serverless.yml).
19
19
  6. [ ] Push local commits to the upstream GitHub branch.
20
20
  7. [ ] Configure [GitHub repository settings].
21
- 8. [ ] Keep dependencies up to date with [Renovate];
22
- request installation in [SEEK-Jobs/renovate].
23
- 9. [ ] Delete this checklist 😌.
21
+ 8. [ ] Delete this checklist 😌.
24
22
 
25
- [arm64]: https://seek-oss.github.io/skuba/docs/deep-dives/arm64.html
23
+ [arm64 guide]: https://seek-oss.github.io/skuba/docs/deep-dives/arm64.html
26
24
  [builds at seek]: https://builds-at-seek.ssod.skinfra.xyz
27
25
  [github repository settings]: https://github.com/<%-orgName%>/<%-repoName%>/settings
28
- [renovate]: https://github.com/apps/renovate
29
- [seek-jobs/renovate]: https://github.com/SEEK-Jobs/renovate
30
26
 
31
27
  ## Design
32
28
 
@@ -2,7 +2,7 @@
2
2
  "dependencies": {
3
3
  "@seek/logger": "^5.0.1",
4
4
  "aws-sdk": "^2.1011.0",
5
- "seek-datadog-custom-metrics": "^4.0.0",
5
+ "datadog-lambda-js": "^6.83.0",
6
6
  "skuba-dive": "^2.0.0",
7
7
  "runtypes": "^6.4.1",
8
8
  "runtypes-filter": "^0.6.0"
@@ -12,9 +12,10 @@
12
12
  "@types/chance": "^1.1.3",
13
13
  "@types/node": "^16.0.0",
14
14
  "chance": "^1.1.8",
15
- "pino-pretty": "^8.0.0",
15
+ "pino-pretty": "^9.0.0",
16
16
  "serverless": "^3.17.0",
17
17
  "serverless-plugin-canary-deployments": "^0.8.0",
18
+ "serverless-plugin-datadog": "^5.7.0",
18
19
  "serverless-prune-plugin": "^2.0.0",
19
20
  "skuba": "*"
20
21
  },
@@ -23,6 +24,9 @@
23
24
  },
24
25
  "license": "UNLICENSED",
25
26
  "private": true,
27
+ "resolutions": {
28
+ "@types/responselike": "1.0.0"
29
+ },
26
30
  "scripts": {
27
31
  "build": "skuba build",
28
32
  "deploy": "serverless deploy --force --verbose",
@@ -4,21 +4,33 @@ configValidationMode: error
4
4
 
5
5
  params:
6
6
  default:
7
+ datadogApiKeySecretArn: 'TODO: arn:aws:secretsmanager:${aws:region}:${aws:accountId}:secret:SECRET-NAME'
7
8
  description: <%- description %>
8
9
  dev:
9
- deploymentBucket: 'TODO: deploymentBucketName'
10
+ deploymentBucket: 'TODO: deployment-bucket-name'
10
11
  isProduction: false
11
12
  prod:
12
- deploymentBucket: 'TODO: deploymentBucketName'
13
+ deploymentBucket: 'TODO: deployment-bucket-name'
13
14
  isProduction: true
14
15
 
15
16
  custom:
17
+ datadog:
18
+ addLayers: false
19
+ apiKeySecretArn: ${param:datadogApiKeySecretArn}
20
+ enableDDLogs: false
21
+ # TODO: enable Datadog extension
22
+ enabled: false
23
+ exclude:
24
+ - WorkerPreHook
25
+ injectLogContext: false
26
+ version: ${env:VERSION}
16
27
  prune:
17
28
  automatic: true
18
29
  number: 3
19
30
 
20
31
  plugins:
21
32
  - serverless-plugin-canary-deployments
33
+ - serverless-plugin-datadog
22
34
  - serverless-prune-plugin
23
35
 
24
36
  provider:
@@ -51,12 +63,12 @@ provider:
51
63
  - Action: lambda:InvokeFunction
52
64
  Effect: Allow
53
65
  Resource: !Sub arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:${self:functions.Worker.name}
66
+ - Action: secretsmanager:GetSecretValue
67
+ Effect: Allow
68
+ Resource: ${param:datadogApiKeySecretArn}-??????
54
69
  - Action: sns:Publish
55
70
  Effect: Allow
56
71
  Resource: !Ref DestinationTopic
57
- - Action: sqs:SendMessage*
58
- Effect: Allow
59
- Resource: !GetAtt DeadLetterQueue.Arn
60
72
  stackTags:
61
73
  # TODO: add data classification tags
62
74
  # https://rfc.skinfra.xyz/RFC019-AWS-Tagging-Standard.html#seekdataconsumers
@@ -17,7 +17,9 @@ describe('handler', () => {
17
17
 
18
18
  const score = chance.floating({ max: 1, min: 0 });
19
19
 
20
- const increment = jest.spyOn(metricsClient, 'increment').mockReturnValue();
20
+ const distribution = jest
21
+ .spyOn(metricsClient, 'distribution')
22
+ .mockReturnValue();
21
23
 
22
24
  beforeAll(logger.spy);
23
25
  beforeAll(scoringService.spy);
@@ -32,7 +34,7 @@ describe('handler', () => {
32
34
 
33
35
  afterEach(() => {
34
36
  logger.clear();
35
- increment.mockClear();
37
+ distribution.mockClear();
36
38
  scoringService.clear();
37
39
  sns.clear();
38
40
  });
@@ -42,28 +44,28 @@ describe('handler', () => {
42
44
 
43
45
  await expect(app.handler(event, ctx)).resolves.toBeUndefined();
44
46
 
45
- expect(scoringService.request).toBeCalledTimes(1);
47
+ expect(scoringService.request).toHaveBeenCalledTimes(1);
46
48
 
47
- expect(logger.error).not.toBeCalled();
49
+ expect(logger.error).not.toHaveBeenCalled();
48
50
 
49
51
  expect(logger.info.mock.calls).toEqual([
50
- [{ count: 1 }, 'received jobs'],
51
- [{ snsMessageId: expect.any(String) }, 'scored job'],
52
- ['request'],
52
+ [{ count: 1 }, 'Received jobs'],
53
+ [{ snsMessageId: expect.any(String) }, 'Scored job'],
54
+ ['Function succeeded'],
53
55
  ]);
54
56
 
55
- expect(increment.mock.calls).toEqual([
57
+ expect(distribution.mock.calls).toEqual([
56
58
  ['job.received', 1],
57
59
  ['job.scored', 1],
58
60
  ]);
59
61
 
60
- expect(sns.publish).toBeCalledTimes(1);
62
+ expect(sns.publish).toHaveBeenCalledTimes(1);
61
63
  });
62
64
 
63
65
  it('throws on invalid input', () => {
64
66
  const event = createSqsEvent(['}']);
65
67
 
66
- return expect(app.handler(event, ctx)).rejects.toThrow('invoke error');
68
+ return expect(app.handler(event, ctx)).rejects.toThrow('Function failed');
67
69
  });
68
70
 
69
71
  it('bubbles up scoring service error', async () => {
@@ -73,9 +75,9 @@ describe('handler', () => {
73
75
 
74
76
  const event = createSqsEvent([JSON.stringify(jobPublished)]);
75
77
 
76
- await expect(app.handler(event, ctx)).rejects.toThrow('invoke error');
78
+ await expect(app.handler(event, ctx)).rejects.toThrow('Function failed');
77
79
 
78
- expect(logger.error).toBeCalledWith({ err }, 'request');
80
+ expect(logger.error).toHaveBeenCalledWith({ err }, 'Function failed');
79
81
  });
80
82
 
81
83
  it('bubbles up SNS error', async () => {
@@ -85,31 +87,31 @@ describe('handler', () => {
85
87
 
86
88
  const event = createSqsEvent([JSON.stringify(jobPublished)]);
87
89
 
88
- await expect(app.handler(event, ctx)).rejects.toThrow('invoke error');
90
+ await expect(app.handler(event, ctx)).rejects.toThrow('Function failed');
89
91
 
90
- expect(logger.error).toBeCalledWith({ err }, 'request');
92
+ expect(logger.error).toHaveBeenCalledWith({ err }, 'Function failed');
91
93
  });
92
94
 
93
95
  it('throws on zero records', async () => {
94
- const err = new Error('received 0 records');
96
+ const err = new Error('Received 0 records');
95
97
 
96
98
  const event = createSqsEvent([]);
97
99
 
98
- await expect(app.handler(event, ctx)).rejects.toThrow('invoke error');
100
+ await expect(app.handler(event, ctx)).rejects.toThrow('Function failed');
99
101
 
100
- expect(logger.error).toBeCalledWith({ err }, 'request');
102
+ expect(logger.error).toHaveBeenCalledWith({ err }, 'Function failed');
101
103
  });
102
104
 
103
105
  it('throws on multiple records', async () => {
104
- const err = new Error('received 2 records');
106
+ const err = new Error('Received 2 records');
105
107
 
106
108
  const event = createSqsEvent([
107
109
  JSON.stringify(jobPublished),
108
110
  JSON.stringify(jobPublished),
109
111
  ]);
110
112
 
111
- await expect(app.handler(event, ctx)).rejects.toThrow('invoke error');
113
+ await expect(app.handler(event, ctx)).rejects.toThrow('Function failed');
112
114
 
113
- expect(logger.error).toBeCalledWith({ err }, 'request');
115
+ expect(logger.error).toHaveBeenCalledWith({ err }, 'Function failed');
114
116
  });
115
117
  });
@@ -20,19 +20,19 @@ const smokeTest = async () => {
20
20
  export const handler = createHandler<SQSEvent>(async (event) => {
21
21
  // Treat an empty object as our smoke test event.
22
22
  if (!Object.keys(event).length) {
23
- logger.info('received smoke test request');
23
+ logger.info('Received smoke test request');
24
24
  return smokeTest();
25
25
  }
26
26
 
27
27
  const count = event.Records.length;
28
28
 
29
29
  if (count !== 1) {
30
- throw Error(`received ${count} records`);
30
+ throw Error(`Received ${count} records`);
31
31
  }
32
32
 
33
- logger.info({ count }, 'received jobs');
33
+ logger.info({ count }, 'Received jobs');
34
34
 
35
- metricsClient.increment('job.received', event.Records.length);
35
+ metricsClient.distribution('job.received', event.Records.length);
36
36
 
37
37
  const record = event.Records[0];
38
38
 
@@ -46,7 +46,7 @@ export const handler = createHandler<SQSEvent>(async (event) => {
46
46
 
47
47
  const snsMessageId = await sendPipelineEvent(scoredJob);
48
48
 
49
- logger.info({ snsMessageId }, 'scored job');
49
+ logger.info({ snsMessageId }, 'Scored job');
50
50
 
51
- metricsClient.increment('job.scored', 1);
51
+ metricsClient.distribution('job.scored', 1);
52
52
  });