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
@@ -4,6 +4,7 @@ interface Config {
4
4
  environment: Environment;
5
5
 
6
6
  logLevel: string;
7
+ metrics: boolean;
7
8
  name: string;
8
9
  version: string;
9
10
 
@@ -20,6 +21,7 @@ const environment = Env.oneOf(environments)('ENVIRONMENT');
20
21
  const configs: Record<Environment, () => Omit<Config, 'environment'>> = {
21
22
  local: () => ({
22
23
  logLevel: 'debug',
24
+ metrics: false,
23
25
  name: '<%- serviceName %>',
24
26
  version: 'local',
25
27
 
@@ -41,6 +43,7 @@ const configs: Record<Environment, () => Omit<Config, 'environment'>> = {
41
43
 
42
44
  prod: () => ({
43
45
  logLevel: 'info',
46
+ metrics: true,
44
47
  name: Env.string('SERVICE'),
45
48
  version: Env.string('VERSION'),
46
49
 
@@ -18,18 +18,18 @@ describe('createHandler', () => {
18
18
  const handler = createHandler((event) => {
19
19
  expect(event).toBe(input);
20
20
 
21
- logger.info('hello from handler');
21
+ logger.info('Handler invoked');
22
22
 
23
23
  return Promise.resolve(output);
24
24
  });
25
25
 
26
26
  await expect(handler(input, ctx)).resolves.toBe(output);
27
27
 
28
- expect(logger.error).not.toBeCalled();
28
+ expect(logger.error).not.toHaveBeenCalled();
29
29
 
30
30
  expect(logger.info.mock.calls).toEqual([
31
- ['hello from handler'],
32
- ['request'],
31
+ ['Handler invoked'],
32
+ ['Function succeeded'],
33
33
  ]);
34
34
  });
35
35
 
@@ -38,11 +38,11 @@ describe('createHandler', () => {
38
38
 
39
39
  const handler = createHandler(() => Promise.reject(err));
40
40
 
41
- await expect(handler(input, ctx)).rejects.toThrow('invoke error');
41
+ await expect(handler(input, ctx)).rejects.toThrow('Function failed');
42
42
 
43
- expect(logger.error.mock.calls).toEqual([[{ err }, 'request']]);
43
+ expect(logger.error.mock.calls).toEqual([[{ err }, 'Function failed']]);
44
44
 
45
- expect(logger.info).not.toBeCalled();
45
+ expect(logger.info).not.toHaveBeenCalled();
46
46
  });
47
47
 
48
48
  it('handles sync error', async () => {
@@ -52,10 +52,10 @@ describe('createHandler', () => {
52
52
  throw err;
53
53
  });
54
54
 
55
- await expect(handler(input, ctx)).rejects.toThrow('invoke error');
55
+ await expect(handler(input, ctx)).rejects.toThrow('Function failed');
56
56
 
57
- expect(logger.error.mock.calls).toEqual([[{ err }, 'request']]);
57
+ expect(logger.error.mock.calls).toEqual([[{ err }, 'Function failed']]);
58
58
 
59
- expect(logger.info).not.toBeCalled();
59
+ expect(logger.info).not.toHaveBeenCalled();
60
60
  });
61
61
  });
@@ -1,20 +1,43 @@
1
- import { Context } from 'aws-lambda';
1
+ import { datadog } from 'datadog-lambda-js';
2
2
 
3
+ import { config } from 'src/config';
3
4
  import { logger, loggerContext } from 'src/framework/logging';
4
5
 
5
- export const createHandler =
6
- <Event, Output = unknown>(fn: (event: Event) => Promise<Output>) =>
7
- (event: Event, { awsRequestId }: Context) =>
6
+ interface LambdaContext {
7
+ awsRequestId: string;
8
+ }
9
+
10
+ type Handler<Event, Output> = (
11
+ event: Event,
12
+ ctx: LambdaContext,
13
+ ) => Promise<Output>;
14
+
15
+ /**
16
+ * Conditionally applies the Datadog wrapper to a Lambda handler.
17
+ *
18
+ * This also "fixes" its broken type definitions.
19
+ */
20
+ const withDatadog = <Event, Output = unknown>(
21
+ fn: Handler<Event, Output>,
22
+ ): Handler<Event, Output> =>
23
+ // istanbul ignore next
24
+ config.metrics ? (datadog(fn) as Handler<Event, Output>) : fn;
25
+
26
+ export const createHandler = <Event, Output = unknown>(
27
+ fn: (event: Event) => Promise<Output>,
28
+ ) =>
29
+ withDatadog<Event>((event, { awsRequestId }) =>
8
30
  loggerContext.run({ awsRequestId }, async () => {
9
31
  try {
10
32
  const output = await fn(event);
11
33
 
12
- logger.info('request');
34
+ logger.info('Function succeeded');
13
35
 
14
36
  return output;
15
37
  } catch (err) {
16
- logger.error({ err }, 'request');
38
+ logger.error({ err }, 'Function failed');
17
39
 
18
- throw new Error('invoke error');
40
+ throw new Error('Function failed');
19
41
  }
20
- });
42
+ }),
43
+ );
@@ -1,10 +1,14 @@
1
- import {
2
- createCloudWatchClient,
3
- createTimedSpan,
4
- } from 'seek-datadog-custom-metrics';
1
+ import { sendDistributionMetric } from 'datadog-lambda-js';
5
2
 
6
3
  import { config } from 'src/config';
7
4
 
8
- export const metricsClient = createCloudWatchClient(config);
5
+ const prefix = `${config.name}.`;
9
6
 
10
- export const timedSpan = createTimedSpan(metricsClient);
7
+ export const metricsClient = {
8
+ distribution: (
9
+ ...[name, ...rest]: Parameters<typeof sendDistributionMetric>
10
+ ) =>
11
+ config.metrics
12
+ ? sendDistributionMetric(`${prefix}${name}`, ...rest)
13
+ : undefined,
14
+ };
@@ -28,21 +28,28 @@ describe('validateJson', () => {
28
28
  it('blocks mistyped prop', () => {
29
29
  const input = JSON.stringify({ ...idDescription, id: null });
30
30
 
31
- expect(() =>
32
- validateJson(input, filterIdDescription),
33
- ).toThrowErrorMatchingInlineSnapshot(
34
- `"Expected { id: string; description: string; }, but was incompatible"`,
35
- );
31
+ expect(() => validateJson(input, filterIdDescription))
32
+ .toThrowErrorMatchingInlineSnapshot(`
33
+ "Validation failed:
34
+ {
35
+ "id": "Expected string, but was null"
36
+ }.
37
+ Object should match { id: string; description: string; }"
38
+ `);
36
39
  });
37
40
 
38
41
  it('blocks missing prop', () => {
39
42
  const input = '{}';
40
43
 
41
- expect(() =>
42
- validateJson(input, filterIdDescription),
43
- ).toThrowErrorMatchingInlineSnapshot(
44
- `"Expected { id: string; description: string; }, but was incompatible"`,
45
- );
44
+ expect(() => validateJson(input, filterIdDescription))
45
+ .toThrowErrorMatchingInlineSnapshot(`
46
+ "Validation failed:
47
+ {
48
+ "id": "Expected string, but was missing",
49
+ "description": "Expected string, but was missing"
50
+ }.
51
+ Object should match { id: string; description: string; }"
52
+ `);
46
53
  });
47
54
 
48
55
  it('blocks invalid JSON', () => {
@@ -25,7 +25,7 @@ describe('scoreJobPublishedEvent', () => {
25
25
  eventType: 'JobScored',
26
26
  });
27
27
 
28
- expect(scoringService.request).toBeCalledTimes(1);
28
+ expect(scoringService.request).toHaveBeenCalledTimes(1);
29
29
  });
30
30
 
31
31
  it('bubbles up scoring service error', async () => {
@@ -39,6 +39,6 @@ describe('scoreJobPublishedEvent', () => {
39
39
  ),
40
40
  ).rejects.toThrow(err);
41
41
 
42
- expect(scoringService.request).toBeCalledTimes(1);
42
+ expect(scoringService.request).toHaveBeenCalledTimes(1);
43
43
  });
44
44
  });
@@ -15,7 +15,7 @@ describe('sendPipelineEvent', () => {
15
15
 
16
16
  await expect(sendPipelineEvent({})).resolves.toBe(messageId);
17
17
 
18
- expect(sns.publish).toBeCalledTimes(1);
18
+ expect(sns.publish).toHaveBeenCalledTimes(1);
19
19
  });
20
20
 
21
21
  it('bubbles up SNS error', () => {
@@ -32,7 +32,7 @@ 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
38
  retry:
@@ -57,7 +57,7 @@ steps:
57
57
  - *aws-sm
58
58
  - *private-npm
59
59
  - *docker-ecr-cache
60
- - docker-compose#v3.9.0:
60
+ - docker-compose#v3.10.0:
61
61
  run: app
62
62
  timeout_in_minutes: 10
63
63