skuba 10.0.0-node-22-20250115223210 → 10.0.0-node-22-20250213035014

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 (84) hide show
  1. package/lib/cli/init/prompts.d.ts +1 -1
  2. package/lib/cli/lint/internalLints/upgrade/patches/10.0.0/index.js +1 -1
  3. package/lib/cli/lint/internalLints/upgrade/patches/10.0.0/index.js.map +1 -1
  4. package/lib/cli/migrate/nodeVersion/getNode22TypesVersion.d.ts +6 -1
  5. package/lib/cli/migrate/nodeVersion/getNode22TypesVersion.js +22 -4
  6. package/lib/cli/migrate/nodeVersion/getNode22TypesVersion.js.map +2 -2
  7. package/lib/cli/migrate/nodeVersion/index.d.ts +0 -6
  8. package/lib/cli/migrate/nodeVersion/index.js +28 -35
  9. package/lib/cli/migrate/nodeVersion/index.js.map +2 -2
  10. package/lib/cli/migrate/nodeVersion/packageJsonChecks.d.ts +2 -2
  11. package/lib/cli/migrate/nodeVersion/packageJsonChecks.js +19 -14
  12. package/lib/cli/migrate/nodeVersion/packageJsonChecks.js.map +2 -2
  13. package/lib/index.js +3 -3
  14. package/lib/utils/template.d.ts +2 -2
  15. package/lib/utils/template.js +0 -5
  16. package/lib/utils/template.js.map +2 -2
  17. package/package.json +14 -14
  18. package/template/base/tsconfig.json +2 -2
  19. package/template/express-rest-api/.buildkite/pipeline.yml +4 -4
  20. package/template/express-rest-api/.gantry/common.yml +1 -2
  21. package/template/express-rest-api/.nvmrc +1 -1
  22. package/template/express-rest-api/Dockerfile +1 -1
  23. package/template/express-rest-api/Dockerfile.dev-deps +2 -2
  24. package/template/express-rest-api/package.json +4 -4
  25. package/template/greeter/.buildkite/pipeline.yml +1 -1
  26. package/template/greeter/.nvmrc +1 -1
  27. package/template/greeter/Dockerfile +2 -2
  28. package/template/greeter/README.md +1 -1
  29. package/template/greeter/package.json +4 -4
  30. package/template/koa-rest-api/.buildkite/pipeline.yml +4 -4
  31. package/template/koa-rest-api/.gantry/common.yml +1 -2
  32. package/template/koa-rest-api/.nvmrc +1 -1
  33. package/template/koa-rest-api/Dockerfile +1 -1
  34. package/template/koa-rest-api/Dockerfile.dev-deps +1 -1
  35. package/template/koa-rest-api/package.json +3 -3
  36. package/template/koa-rest-api/tsconfig.json +2 -2
  37. package/template/lambda-sqs-worker-cdk/.buildkite/pipeline.yml +2 -2
  38. package/template/lambda-sqs-worker-cdk/.nvmrc +1 -1
  39. package/template/lambda-sqs-worker-cdk/Dockerfile +3 -3
  40. package/template/lambda-sqs-worker-cdk/infra/__snapshots__/appStack.test.ts.snap +2 -2
  41. package/template/lambda-sqs-worker-cdk/infra/appStack.ts +2 -2
  42. package/template/lambda-sqs-worker-cdk/infra/config.ts +1 -1
  43. package/template/lambda-sqs-worker-cdk/infra/index.ts +3 -5
  44. package/template/lambda-sqs-worker-cdk/package.json +4 -4
  45. package/template/lambda-sqs-worker-cdk/tsconfig.json +2 -2
  46. package/template/oss-npm-package/.github/workflows/release.yml +1 -1
  47. package/template/oss-npm-package/.github/workflows/validate.yml +1 -1
  48. package/template/oss-npm-package/.nvmrc +1 -1
  49. package/template/oss-npm-package/_package.json +1 -1
  50. package/template/private-npm-package/.nvmrc +1 -1
  51. package/template/private-npm-package/_package.json +2 -2
  52. package/template/lambda-sqs-worker/.buildkite/pipeline.yml +0 -108
  53. package/template/lambda-sqs-worker/.env +0 -1
  54. package/template/lambda-sqs-worker/.nvmrc +0 -1
  55. package/template/lambda-sqs-worker/Dockerfile +0 -17
  56. package/template/lambda-sqs-worker/README.md +0 -132
  57. package/template/lambda-sqs-worker/_.npmrc +0 -13
  58. package/template/lambda-sqs-worker/docker-compose.yml +0 -10
  59. package/template/lambda-sqs-worker/package.json +0 -45
  60. package/template/lambda-sqs-worker/serverless.yml +0 -213
  61. package/template/lambda-sqs-worker/skuba.template.js +0 -33
  62. package/template/lambda-sqs-worker/src/app.test.ts +0 -116
  63. package/template/lambda-sqs-worker/src/app.ts +0 -57
  64. package/template/lambda-sqs-worker/src/config.ts +0 -62
  65. package/template/lambda-sqs-worker/src/framework/handler.test.ts +0 -61
  66. package/template/lambda-sqs-worker/src/framework/handler.ts +0 -43
  67. package/template/lambda-sqs-worker/src/framework/logging.ts +0 -27
  68. package/template/lambda-sqs-worker/src/framework/metrics.ts +0 -14
  69. package/template/lambda-sqs-worker/src/framework/validation.test.ts +0 -84
  70. package/template/lambda-sqs-worker/src/framework/validation.ts +0 -10
  71. package/template/lambda-sqs-worker/src/hooks.ts +0 -95
  72. package/template/lambda-sqs-worker/src/mapping/jobScorer.ts +0 -22
  73. package/template/lambda-sqs-worker/src/services/aws.ts +0 -5
  74. package/template/lambda-sqs-worker/src/services/jobScorer.test.ts +0 -44
  75. package/template/lambda-sqs-worker/src/services/jobScorer.ts +0 -59
  76. package/template/lambda-sqs-worker/src/services/pipelineEventSender.test.ts +0 -40
  77. package/template/lambda-sqs-worker/src/services/pipelineEventSender.ts +0 -33
  78. package/template/lambda-sqs-worker/src/testing/handler.ts +0 -13
  79. package/template/lambda-sqs-worker/src/testing/logging.ts +0 -19
  80. package/template/lambda-sqs-worker/src/testing/services.ts +0 -28
  81. package/template/lambda-sqs-worker/src/testing/types.ts +0 -33
  82. package/template/lambda-sqs-worker/src/types/jobScorer.ts +0 -15
  83. package/template/lambda-sqs-worker/src/types/pipelineEvents.ts +0 -21
  84. package/template/lambda-sqs-worker/tsconfig.json +0 -13
@@ -1,13 +0,0 @@
1
- # managed by skuba
2
- package-manager-strict-version=true
3
- public-hoist-pattern[]="@types*"
4
- public-hoist-pattern[]="*eslint*"
5
- public-hoist-pattern[]="*prettier*"
6
- public-hoist-pattern[]="esbuild"
7
- public-hoist-pattern[]="jest"
8
- public-hoist-pattern[]="tsconfig-seek"
9
- # end managed by skuba
10
-
11
- # Required for Serverless packaging
12
- node-linker=hoisted
13
- shamefully-hoist=true
@@ -1,10 +0,0 @@
1
- services:
2
- app:
3
- image: ${BUILDKITE_PLUGIN_DOCKER_IMAGE:-''}
4
- init: true
5
- volumes:
6
- - ./:/workdir
7
- # Mount agent for Buildkite annotations.
8
- - /usr/bin/buildkite-agent:/usr/bin/buildkite-agent
9
- # Mount cached dependencies.
10
- - /workdir/node_modules
@@ -1,45 +0,0 @@
1
- {
2
- "name": "@seek/<%- serviceName %>",
3
- "private": true,
4
- "license": "UNLICENSED",
5
- "scripts": {
6
- "build": "skuba build",
7
- "deploy": "serverless deploy --force --verbose",
8
- "format": "skuba format",
9
- "lint": "skuba lint",
10
- "smoke": "serverless invoke --data '{}' --function Worker",
11
- "start": "skuba start --port <%- port %>",
12
- "start:debug": "pnpm --silent start --inspect-brk",
13
- "test": "skuba test",
14
- "test:ci": "skuba test --coverage",
15
- "test:watch": "skuba test --watch"
16
- },
17
- "dependencies": {
18
- "@aws-sdk/client-codedeploy": "^3.363.0",
19
- "@aws-sdk/client-lambda": "^3.363.0",
20
- "@aws-sdk/client-sns": "^3.363.0",
21
- "@seek/logger": "^9.0.0",
22
- "datadog-lambda-js": "^9.0.0",
23
- "dd-trace": "^5.0.0",
24
- "skuba-dive": "^2.0.0",
25
- "zod": "^3.19.1"
26
- },
27
- "devDependencies": {
28
- "@types/aws-lambda": "^8.10.84",
29
- "@types/chance": "^1.1.3",
30
- "@types/node": "^20.16.5",
31
- "aws-sdk-client-mock": "^4.0.0",
32
- "aws-sdk-client-mock-jest": "^4.0.0",
33
- "chance": "^1.1.8",
34
- "pino-pretty": "^13.0.0",
35
- "serverless": "^3.39.0",
36
- "serverless-plugin-canary-deployments": "^0.8.0",
37
- "serverless-plugin-datadog": "^5.12.0",
38
- "serverless-prune-plugin": "^2.0.0",
39
- "skuba": "*"
40
- },
41
- "packageManager": "pnpm@9.15.3",
42
- "engines": {
43
- "node": ">=20"
44
- }
45
- }
@@ -1,213 +0,0 @@
1
- service: <%- serviceName %>
2
-
3
- configValidationMode: error
4
-
5
- params:
6
- default:
7
- datadogApiKeySecretArn: 'TODO: arn:aws:secretsmanager:${aws:region}:${aws:accountId}:secret:SECRET-NAME'
8
- dataStoreTags:
9
- - Key: seek:auto:backup:frequency
10
- Value: none
11
- # TODO: https://rfc.skinfra.xyz/RFC019-AWS-Tagging-Standard.html#data-services
12
- # - Key: seek:data:derived
13
- # Value: copy
14
- # - Key: seek:data:domain
15
- # Value: unassigned
16
- # - Key: seek:data:jurisdiction-source
17
- # Value: australia hong-kong indonesia malaysia new-zealand philippines singapore thailand
18
- # - Key: seek:data:types:restricted
19
- # Value: job-ads
20
- description: <%- description %>
21
- dev:
22
- deploymentBucket: 'TODO: deployment-bucket-name'
23
- concurrency: 2
24
- prod:
25
- deploymentBucket: 'TODO: deployment-bucket-name'
26
- concurrency: 20
27
-
28
- custom:
29
- datadog:
30
- addLayers: false
31
- apiKeySecretArn: ${param:datadogApiKeySecretArn}
32
- enableDDLogs: false
33
- # TODO: enable Datadog extension
34
- enabled: false
35
- exclude:
36
- - WorkerPreHook
37
- injectLogContext: false
38
- version: ${env:VERSION}
39
- prune:
40
- automatic: true
41
- number: 3
42
-
43
- plugins:
44
- - serverless-plugin-canary-deployments
45
- - serverless-plugin-datadog
46
- - serverless-prune-plugin
47
-
48
- provider:
49
- logRetentionInDays: 30
50
- name: aws
51
- region: ap-southeast-2
52
- runtime: nodejs20.x
53
- architecture: <%- lambdaServerlessArchitecture %>
54
- deploymentMethod: direct
55
- stackName: ${self:service}
56
- stage: ${env:ENVIRONMENT}
57
- deploymentBucket:
58
- # Use a shared account-level bucket for Lambda bundles and other artefacts.
59
- # This is easier to manage in terms of access, deployment, and tagging.
60
- name: ${param:deploymentBucket}
61
- environment:
62
- NODE_ENV: production
63
- # https://nodejs.org/api/cli.html#cli_node_options_options
64
- NODE_OPTIONS: --enable-source-maps
65
- iam:
66
- role:
67
- statements:
68
- - Action:
69
- - kms:Decrypt
70
- - kms:GenerateDataKey*
71
- Effect: Allow
72
- Resource: !GetAtt EncryptionKey.Arn
73
- - Action: lambda:InvokeFunction
74
- Effect: Allow
75
- Resource: !Sub arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:${self:functions.Worker.name}
76
- - Action: secretsmanager:GetSecretValue
77
- Effect: Allow
78
- Resource: ${param:datadogApiKeySecretArn}-??????
79
- - Action: sns:Publish
80
- Effect: Allow
81
- Resource: !Ref DestinationTopic
82
- stackTags:
83
- seek:env:label: ${env:ENVIRONMENT}
84
- seek:source:sha: ${env:BUILDKITE_COMMIT, 'na'}
85
- seek:source:url: 'https://github.com/SEEK-Jobs/<%- repoName %>'
86
- seek:system:deployed-by: CFN
87
- # seek:system:name: 'TODO: https://rfc.skinfra.xyz/RFC019-AWS-Tagging-Standard.html#required-tags'
88
-
89
- package:
90
- patterns:
91
- - '!**'
92
- - lib/**
93
- - node_modules/**
94
-
95
- functions:
96
- Worker:
97
- name: ${self:service}
98
- handler: lib/app.handler
99
- description: ${param:description}
100
- memorySize: 128
101
- reservedConcurrency: ${param:concurrency}
102
- timeout: 30
103
- deploymentSettings:
104
- type: AllAtOnce
105
- alias: Live
106
- preTrafficHook: WorkerPreHook
107
- environment:
108
- ENVIRONMENT: ${env:ENVIRONMENT}
109
- SERVICE: ${self:service}
110
- VERSION: ${env:VERSION, 'local'}
111
-
112
- DESTINATION_SNS_TOPIC_ARN: !Ref DestinationTopic
113
- events:
114
- - sqs:
115
- arn: !GetAtt MessageQueue.Arn
116
- batchSize: 1
117
- maximumConcurrency: ${param:concurrency}
118
- WorkerPreHook:
119
- name: ${self:functions.Worker.name}-pre-hook
120
- handler: lib/hooks.pre
121
- memorySize: 128
122
- # This is generous because a timeout will hang the deployment
123
- timeout: 300
124
- environment:
125
- FUNCTION_NAME_TO_INVOKE: ${self:functions.Worker.name}
126
-
127
- resources:
128
- # This becomes the Lambda application's description
129
- Description: ${param:description}
130
-
131
- Resources:
132
- DeadLetterQueue:
133
- Type: AWS::SQS::Queue
134
- Properties:
135
- MessageRetentionPeriod: 1209600
136
- KmsDataKeyReusePeriodSeconds: 300
137
- KmsMasterKeyId: !Ref EncryptionKey
138
- QueueName: ${self:service}-dead-letters
139
- Tags: ${param:dataStoreTags}
140
-
141
- MessageQueue:
142
- Type: AWS::SQS::Queue
143
- Properties:
144
- MessageRetentionPeriod: 1209600
145
- KmsDataKeyReusePeriodSeconds: 300
146
- KmsMasterKeyId: !Ref EncryptionKey
147
- QueueName: ${self:service}-messages
148
- RedrivePolicy:
149
- deadLetterTargetArn: !GetAtt DeadLetterQueue.Arn
150
- maxReceiveCount: 3
151
- Tags: ${param:dataStoreTags}
152
- VisibilityTimeout: 180
153
-
154
- # MessageQueuePolicy:
155
- # Type: AWS::SQS::QueuePolicy
156
- # Properties:
157
- # PolicyDocument:
158
- # Version: '2012-10-17'
159
- # Statement:
160
- # - Action: sqs:SendMessage
161
- # Condition:
162
- # ArnEquals:
163
- # aws:SourceArn: 'TODO: sourceSnsTopicArn'
164
- # Effect: 'Allow'
165
- # Principal:
166
- # AWS: '*'
167
- # Resource: '*'
168
- # Queues:
169
- # - !Ref MessageQueue
170
-
171
- # TopicSubscription:
172
- # Type: AWS::SNS::Subscription
173
- # Properties:
174
- # Endpoint: !GetAtt MessageQueue.Arn
175
- # Protocol: sqs
176
- # RawMessageDelivery: true # Remove this property if you require end to end datadog tracing
177
- # TopicArn: 'TODO: sourceSnsTopicArn'
178
-
179
- DestinationTopic:
180
- Type: AWS::SNS::Topic
181
- Properties:
182
- KmsMasterKeyId: alias/aws/sns
183
- Tags: ${param:dataStoreTags}
184
- TopicName: ${self:service}
185
-
186
- EncryptionKey:
187
- Type: AWS::KMS::Key
188
- Properties:
189
- Description: ${self:service}
190
- EnableKeyRotation: true
191
- KeyPolicy:
192
- Version: '2012-10-17'
193
- Statement:
194
- - Sid: Admin access
195
- Effect: Allow
196
- Action: kms:*
197
- Principal:
198
- AWS: !Ref AWS::AccountId
199
- Resource: '*'
200
- - Sid: SNS compatibility
201
- Effect: Allow
202
- Action:
203
- - kms:Decrypt
204
- - kms:GenerateDataKey*
205
- Principal:
206
- Service: sns.amazonaws.com
207
- Resource: '*'
208
-
209
- EncryptionKeyAlias:
210
- Type: AWS::KMS::Alias
211
- Properties:
212
- AliasName: alias/seek/${self:service}
213
- TargetKeyId: !Ref EncryptionKey
@@ -1,33 +0,0 @@
1
- /**
2
- * Run `skuba configure` to finish templating and remove this file.
3
- */
4
-
5
- module.exports = {
6
- entryPoint: 'src/app.ts#handler',
7
- fields: [
8
- {
9
- name: 'serviceName',
10
- message: 'Service slug',
11
- initial: 'my-project',
12
- },
13
- {
14
- name: 'description',
15
- message: 'Description',
16
- initial: 'A project just for me',
17
- },
18
- {
19
- name: 'devBuildkiteQueueName',
20
- message: 'Dev Buildkite queue',
21
- initial: 'my-team-aws-account-dev:cicd',
22
- validate: (value) => /^.+:.+$/.test(value),
23
- },
24
- {
25
- name: 'prodBuildkiteQueueName',
26
- message: 'Prod Buildkite queue',
27
- initial: 'my-team-aws-account-prod:cicd',
28
- validate: (value) => /^.+:.+$/.test(value),
29
- },
30
- ],
31
- packageManager: 'pnpm',
32
- type: 'application',
33
- };
@@ -1,116 +0,0 @@
1
- import { PublishCommand } from '@aws-sdk/client-sns';
2
-
3
- import { metricsClient } from 'src/framework/metrics';
4
- import { createCtx, createSqsEvent } from 'src/testing/handler';
5
- import { logger } from 'src/testing/logging';
6
- import { scoringService, sns } from 'src/testing/services';
7
- import { chance, mockJobPublishedEvent } from 'src/testing/types';
8
-
9
- import * as app from './app';
10
-
11
- describe('app', () => {
12
- it('exports a handler', () => expect(app).toHaveProperty('handler'));
13
- });
14
-
15
- describe('handler', () => {
16
- const ctx = createCtx();
17
-
18
- const jobPublished = mockJobPublishedEvent({ entityId: chance.name() });
19
-
20
- const score = chance.floating({ max: 1, min: 0 });
21
-
22
- const distribution = jest
23
- .spyOn(metricsClient, 'distribution')
24
- .mockReturnValue();
25
-
26
- beforeAll(logger.spy);
27
- beforeAll(scoringService.spy);
28
-
29
- beforeEach(() => {
30
- scoringService.request.mockResolvedValue(score);
31
- sns.publish.resolves({ MessageId: chance.guid({ version: 4 }) });
32
- });
33
-
34
- afterEach(() => {
35
- logger.clear();
36
- distribution.mockClear();
37
- scoringService.clear();
38
- sns.clear();
39
- });
40
-
41
- it('handles one record', async () => {
42
- const event = createSqsEvent([JSON.stringify(jobPublished)]);
43
-
44
- await expect(app.handler(event, ctx)).resolves.toBeUndefined();
45
-
46
- expect(scoringService.request).toHaveBeenCalledTimes(1);
47
-
48
- expect(logger.error).not.toHaveBeenCalled();
49
-
50
- expect(logger.debug.mock.calls).toEqual([
51
- [{ count: 1 }, 'Received jobs'],
52
- [{ snsMessageId: expect.any(String) }, 'Scored job'],
53
- ['Function succeeded'],
54
- ]);
55
-
56
- expect(distribution.mock.calls).toEqual([
57
- ['job.received', 1],
58
- ['job.scored', 1],
59
- ]);
60
-
61
- expect(sns.client).toReceiveCommandTimes(PublishCommand, 1);
62
- });
63
-
64
- it('throws on invalid input', () => {
65
- const event = createSqsEvent(['}']);
66
-
67
- return expect(app.handler(event, ctx)).rejects.toThrow('Function failed');
68
- });
69
-
70
- it('bubbles up scoring service error', async () => {
71
- const err = Error(chance.sentence());
72
-
73
- scoringService.request.mockRejectedValue(err);
74
-
75
- const event = createSqsEvent([JSON.stringify(jobPublished)]);
76
-
77
- await expect(app.handler(event, ctx)).rejects.toThrow('Function failed');
78
-
79
- expect(logger.error).toHaveBeenCalledWith({ err }, 'Function failed');
80
- });
81
-
82
- it('bubbles up SNS error', async () => {
83
- const err = Error(chance.sentence());
84
-
85
- sns.publish.rejects(err);
86
-
87
- const event = createSqsEvent([JSON.stringify(jobPublished)]);
88
-
89
- await expect(app.handler(event, ctx)).rejects.toThrow('Function failed');
90
-
91
- expect(logger.error).toHaveBeenCalledWith({ err }, 'Function failed');
92
- });
93
-
94
- it('throws on zero records', async () => {
95
- const err = new Error('Received 0 records');
96
-
97
- const event = createSqsEvent([]);
98
-
99
- await expect(app.handler(event, ctx)).rejects.toThrow('Function failed');
100
-
101
- expect(logger.error).toHaveBeenCalledWith({ err }, 'Function failed');
102
- });
103
-
104
- it('throws on multiple records', async () => {
105
- const err = new Error('Received 2 records');
106
-
107
- const event = createSqsEvent([
108
- JSON.stringify(jobPublished),
109
- JSON.stringify(jobPublished),
110
- ]);
111
-
112
- await expect(app.handler(event, ctx)).rejects.toThrow('Function failed');
113
-
114
- expect(logger.error).toHaveBeenCalledWith({ err }, 'Function failed');
115
- });
116
- });
@@ -1,57 +0,0 @@
1
- import 'skuba-dive/register';
2
-
3
- import type { SQSEvent } from 'aws-lambda';
4
-
5
- import { createHandler } from 'src/framework/handler';
6
- import { logger } from 'src/framework/logging';
7
- import { metricsClient } from 'src/framework/metrics';
8
- import { validateJson } from 'src/framework/validation';
9
- import { scoreJobPublishedEvent, scoringService } from 'src/services/jobScorer';
10
- import { sendPipelineEvent } from 'src/services/pipelineEventSender';
11
- import { JobPublishedEventSchema } from 'src/types/pipelineEvents';
12
-
13
- /**
14
- * Tests connectivity to ensure appropriate access and network configuration.
15
- */
16
- const smokeTest = async () => {
17
- await Promise.all([scoringService.smokeTest(), sendPipelineEvent({}, true)]);
18
- };
19
-
20
- export const handler = createHandler<SQSEvent>(async (event) => {
21
- // Treat an empty object as our smoke test event.
22
- if (!Object.keys(event).length) {
23
- logger.debug('Received smoke test request');
24
- return smokeTest();
25
- }
26
-
27
- const count = event.Records.length;
28
-
29
- if (count !== 1) {
30
- throw Error(`Received ${count} records`);
31
- }
32
-
33
- logger.debug({ count }, 'Received jobs');
34
-
35
- metricsClient.distribution('job.received', event.Records.length);
36
-
37
- const record = event.Records[0];
38
- if (!record) {
39
- throw new Error('Malformed SQS event with no records');
40
- }
41
-
42
- const { body } = record;
43
-
44
- // TODO: this throws an error, which will cause the Lambda function to retry
45
- // the event and eventually send it to your dead-letter queue. If you don't
46
- // trust your source to provide consistently well-formed input, consider
47
- // catching and handling this error in code.
48
- const publishedJob = validateJson(body, JobPublishedEventSchema);
49
-
50
- const scoredJob = await scoreJobPublishedEvent(publishedJob);
51
-
52
- const snsMessageId = await sendPipelineEvent(scoredJob);
53
-
54
- logger.debug({ snsMessageId }, 'Scored job');
55
-
56
- metricsClient.distribution('job.scored', 1);
57
- });
@@ -1,62 +0,0 @@
1
- import { Env } from 'skuba-dive';
2
-
3
- interface Config {
4
- environment: Environment;
5
-
6
- logLevel: string;
7
- metrics: boolean;
8
- name: string;
9
- version: string;
10
-
11
- destinationSnsTopicArn: string;
12
- }
13
-
14
- type Environment = (typeof environments)[number];
15
-
16
- const environments = ['local', 'test', 'dev', 'prod'] as const;
17
-
18
- const environment = Env.oneOf(environments)('ENVIRONMENT');
19
-
20
- /* istanbul ignore next: config verification makes more sense in a smoke test */
21
- const configs: Record<Environment, () => Omit<Config, 'environment'>> = {
22
- local: () => ({
23
- logLevel: 'debug',
24
- metrics: false,
25
- name: '<%- serviceName %>',
26
- version: 'local',
27
-
28
- destinationSnsTopicArn: 'arn:aws:sns:us-east-2:123456789012:destination',
29
- }),
30
-
31
- test: () => ({
32
- logLevel: Env.string('LOG_LEVEL', { default: 'silent' }),
33
- metrics: false,
34
- name: '<%- serviceName %>',
35
- version: 'test',
36
-
37
- destinationSnsTopicArn: 'arn:aws:sns:us-east-2:123456789012:destination',
38
- }),
39
-
40
- dev: () => ({
41
- logLevel: 'debug',
42
- metrics: true,
43
- name: Env.string('SERVICE'),
44
- version: Env.string('VERSION'),
45
-
46
- destinationSnsTopicArn: Env.string('DESTINATION_SNS_TOPIC_ARN'),
47
- }),
48
-
49
- prod: () => ({
50
- logLevel: 'info',
51
- metrics: true,
52
- name: Env.string('SERVICE'),
53
- version: Env.string('VERSION'),
54
-
55
- destinationSnsTopicArn: Env.string('DESTINATION_SNS_TOPIC_ARN'),
56
- }),
57
- };
58
-
59
- export const config: Config = {
60
- ...configs[environment](),
61
- environment,
62
- };
@@ -1,61 +0,0 @@
1
- import { createCtx } from 'src/testing/handler';
2
- import { logger } from 'src/testing/logging';
3
- import { chance } from 'src/testing/types';
4
-
5
- import { createHandler } from './handler';
6
-
7
- describe('createHandler', () => {
8
- const ctx = createCtx();
9
- const input = chance.paragraph();
10
-
11
- beforeAll(logger.spy);
12
-
13
- afterEach(logger.clear);
14
-
15
- it('handles happy path', async () => {
16
- const output = chance.paragraph();
17
-
18
- const handler = createHandler((event) => {
19
- expect(event).toBe(input);
20
-
21
- logger.debug('Handler invoked');
22
-
23
- return Promise.resolve(output);
24
- });
25
-
26
- await expect(handler(input, ctx)).resolves.toBe(output);
27
-
28
- expect(logger.error).not.toHaveBeenCalled();
29
-
30
- expect(logger.debug.mock.calls).toEqual([
31
- ['Handler invoked'],
32
- ['Function succeeded'],
33
- ]);
34
- });
35
-
36
- it('handles async error', async () => {
37
- const err = Error(chance.sentence());
38
-
39
- const handler = createHandler(() => Promise.reject(err));
40
-
41
- await expect(handler(input, ctx)).rejects.toThrow('Function failed');
42
-
43
- expect(logger.error).toHaveBeenCalledWith({ err }, 'Function failed');
44
-
45
- expect(logger.debug).not.toHaveBeenCalled();
46
- });
47
-
48
- it('handles sync error', async () => {
49
- const err = Error(chance.sentence());
50
-
51
- const handler = createHandler(() => {
52
- throw err;
53
- });
54
-
55
- await expect(handler(input, ctx)).rejects.toThrow('Function failed');
56
-
57
- expect(logger.error).toHaveBeenCalledWith({ err }, 'Function failed');
58
-
59
- expect(logger.debug).not.toHaveBeenCalled();
60
- });
61
- });
@@ -1,43 +0,0 @@
1
- import { datadog } from 'datadog-lambda-js';
2
-
3
- import { config } from 'src/config';
4
- import { logger, loggerContext } from 'src/framework/logging';
5
-
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 }) =>
30
- loggerContext.run({ awsRequestId }, async () => {
31
- try {
32
- const output = await fn(event);
33
-
34
- logger.debug('Function succeeded');
35
-
36
- return output;
37
- } catch (err) {
38
- logger.error({ err }, 'Function failed');
39
-
40
- throw new Error('Function failed');
41
- }
42
- }),
43
- );
@@ -1,27 +0,0 @@
1
- import { AsyncLocalStorage } from 'async_hooks';
2
-
3
- import createLogger from '@seek/logger';
4
-
5
- import { config } from 'src/config';
6
-
7
- interface LoggerContext {
8
- awsRequestId: string;
9
- }
10
-
11
- export const loggerContext = new AsyncLocalStorage<LoggerContext>();
12
-
13
- export const logger = createLogger({
14
- base: {
15
- environment: config.environment,
16
- version: config.version,
17
- },
18
-
19
- level: config.logLevel,
20
-
21
- mixin: () => ({ ...loggerContext.getStore() }),
22
-
23
- name: config.name,
24
-
25
- transport:
26
- config.environment === 'local' ? { target: 'pino-pretty' } : undefined,
27
- });