@tomassabol/aws-services 1.8.0
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.
- package/.eslintignore +1 -0
- package/.eslintrc.json +75 -0
- package/.husky/pre-commit +4 -0
- package/.lintstagedrc.json +9 -0
- package/.nvmrc +1 -0
- package/.prettierignore +2 -0
- package/.prettierrc.json +3 -0
- package/.vscode/settings.json +6 -0
- package/CHANGELOG.md +96 -0
- package/README.md +28 -0
- package/bitbucket-pipelines.yml +65 -0
- package/jest.config.js +62 -0
- package/package.json +63 -0
- package/sonar-project.properties +8 -0
- package/src/appconfig.ts +20 -0
- package/src/dynamodb.mock.ts +10 -0
- package/src/dynamodb.ts +7 -0
- package/src/eventbridge.mock.ts +5 -0
- package/src/eventbridge.ts +47 -0
- package/src/logger.ts +14 -0
- package/src/s3.ts +56 -0
- package/src/secrets-manager.mock.ts +9 -0
- package/src/secrets-manager.ts +37 -0
- package/src/sns.ts +24 -0
- package/src/sqs.mock.ts +5 -0
- package/src/sqs.ts +32 -0
- package/src/ssm.mock.ts +5 -0
- package/src/ssm.ts +70 -0
- package/test/appconfig.test.ts +45 -0
- package/test/dynamodb.test.ts +13 -0
- package/test/eventbridge.test.ts +35 -0
- package/test/s3.test.ts +78 -0
- package/test/secrets-manager.test.ts +55 -0
- package/test/sns.test.ts +59 -0
- package/test/sqs.test.ts +46 -0
- package/test/ssm.test.ts +85 -0
- package/tsconfig.build.json +4 -0
- package/tsconfig.json +9 -0
- package/typedoc.json +4 -0
package/.eslintignore
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
dist
|
package/.eslintrc.json
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
{
|
|
2
|
+
"root": true,
|
|
3
|
+
"env": {
|
|
4
|
+
"node": true
|
|
5
|
+
},
|
|
6
|
+
"overrides": [
|
|
7
|
+
{
|
|
8
|
+
"files": ["**/*.ts", "**/*.tsx"],
|
|
9
|
+
"parser": "@typescript-eslint/parser",
|
|
10
|
+
"parserOptions": {
|
|
11
|
+
"project": "./tsconfig.json"
|
|
12
|
+
},
|
|
13
|
+
"plugins": ["@typescript-eslint"],
|
|
14
|
+
"extends": [
|
|
15
|
+
"eslint:recommended",
|
|
16
|
+
"plugin:import/recommended",
|
|
17
|
+
"plugin:import/typescript",
|
|
18
|
+
"plugin:@typescript-eslint/eslint-recommended",
|
|
19
|
+
"plugin:@typescript-eslint/recommended",
|
|
20
|
+
"prettier"
|
|
21
|
+
],
|
|
22
|
+
"rules": {
|
|
23
|
+
"@typescript-eslint/no-empty-interface": "warn",
|
|
24
|
+
"@typescript-eslint/no-unused-vars": [
|
|
25
|
+
"warn",
|
|
26
|
+
{ "argsIgnorePattern": "^_" }
|
|
27
|
+
],
|
|
28
|
+
"@typescript-eslint/no-floating-promises": "error",
|
|
29
|
+
"require-await": "warn",
|
|
30
|
+
"no-useless-catch": "warn",
|
|
31
|
+
"no-console": "warn",
|
|
32
|
+
"no-new-object": "warn",
|
|
33
|
+
"object-shorthand": ["warn", "always"],
|
|
34
|
+
"quote-props": ["warn", "as-needed"],
|
|
35
|
+
"prefer-object-spread": "warn",
|
|
36
|
+
"prefer-destructuring": [
|
|
37
|
+
"warn",
|
|
38
|
+
{
|
|
39
|
+
"VariableDeclarator": {
|
|
40
|
+
"array": false,
|
|
41
|
+
"object": true
|
|
42
|
+
},
|
|
43
|
+
"AssignmentExpression": {
|
|
44
|
+
"array": false,
|
|
45
|
+
"object": false
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
],
|
|
49
|
+
"default-param-last": "warn",
|
|
50
|
+
"no-param-reassign": [
|
|
51
|
+
"warn",
|
|
52
|
+
{ "props": true, "ignorePropertyModificationsFor": ["acc"] }
|
|
53
|
+
],
|
|
54
|
+
"prefer-arrow-callback": "warn",
|
|
55
|
+
"no-duplicate-imports": "warn",
|
|
56
|
+
"import/no-mutable-exports": "warn",
|
|
57
|
+
"import/first": "warn",
|
|
58
|
+
"no-iterator": "warn",
|
|
59
|
+
"dot-notation": "warn",
|
|
60
|
+
"one-var": ["warn", { "initialized": "never" }],
|
|
61
|
+
"no-multi-assign": "warn",
|
|
62
|
+
"no-plusplus": ["warn", { "allowForLoopAfterthoughts": true }],
|
|
63
|
+
"eqeqeq": "warn"
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"files": ["**/*.md"],
|
|
68
|
+
"extends": "plugin:markdown/recommended"
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
"files": ["**/*.json"],
|
|
72
|
+
"extends": "plugin:json/recommended"
|
|
73
|
+
}
|
|
74
|
+
]
|
|
75
|
+
}
|
package/.nvmrc
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
v18.18.2
|
package/.prettierignore
ADDED
package/.prettierrc.json
ADDED
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [1.8.0] - 2024-06-24
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
|
|
12
|
+
- WMS-488 Improve sendEvent error handling
|
|
13
|
+
|
|
14
|
+
## [1.7.1] - 2024-06-22
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
|
|
18
|
+
- Bump @aws-lambda-powertools/logger
|
|
19
|
+
|
|
20
|
+
### Security
|
|
21
|
+
|
|
22
|
+
- Remove NPM token
|
|
23
|
+
|
|
24
|
+
## [1.7.0] - 2023-11-18
|
|
25
|
+
|
|
26
|
+
### Added
|
|
27
|
+
|
|
28
|
+
- Add Jest mock for SSM
|
|
29
|
+
|
|
30
|
+
## [1.6.1] - 2023-11-04
|
|
31
|
+
|
|
32
|
+
### Added
|
|
33
|
+
|
|
34
|
+
- Add getSecret method to Secrets Manager mock
|
|
35
|
+
|
|
36
|
+
### Security
|
|
37
|
+
|
|
38
|
+
- Fix vulnerabilities
|
|
39
|
+
|
|
40
|
+
## [1.6.0] - 2023-08-18
|
|
41
|
+
|
|
42
|
+
### Added
|
|
43
|
+
|
|
44
|
+
- Add Jest mocks for DynamoDB, EventBridge, Secrets Manager and SQS
|
|
45
|
+
|
|
46
|
+
## [1.5.0] - 2023-08-14
|
|
47
|
+
|
|
48
|
+
### Changed
|
|
49
|
+
|
|
50
|
+
- Improve S3 client
|
|
51
|
+
|
|
52
|
+
## [1.4.0] - 2023-08-12
|
|
53
|
+
|
|
54
|
+
### Changed
|
|
55
|
+
|
|
56
|
+
- Improve EventBridge client
|
|
57
|
+
|
|
58
|
+
## [1.3.0] - 2023-08-11
|
|
59
|
+
|
|
60
|
+
### Added
|
|
61
|
+
|
|
62
|
+
- Add format check step to Bitbucket pipeline
|
|
63
|
+
- Add nvm configuration file
|
|
64
|
+
|
|
65
|
+
### Changed
|
|
66
|
+
|
|
67
|
+
- Improve SSM client
|
|
68
|
+
- Improve SQS client
|
|
69
|
+
- Improve Secrets Manager client
|
|
70
|
+
- Improve DynamoDB client
|
|
71
|
+
- Upgrade AWS CDK libs
|
|
72
|
+
- Refactor NPM scripts
|
|
73
|
+
- Update nvm configuration file
|
|
74
|
+
- Improve Bitbucket pipeline
|
|
75
|
+
|
|
76
|
+
### Removed
|
|
77
|
+
|
|
78
|
+
- Remove npm-run-all package
|
|
79
|
+
|
|
80
|
+
### Security
|
|
81
|
+
|
|
82
|
+
- Fix vulnerabilities
|
|
83
|
+
|
|
84
|
+
## [1.2.1] - 2023-06-08
|
|
85
|
+
|
|
86
|
+
### Fixed
|
|
87
|
+
|
|
88
|
+
- Fix importing without dist folder
|
|
89
|
+
|
|
90
|
+
### Security
|
|
91
|
+
|
|
92
|
+
- Fix vulnerabilities
|
|
93
|
+
|
|
94
|
+
## [1.1.1]
|
|
95
|
+
|
|
96
|
+
- Initial release
|
package/README.md
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# AWS Services
|
|
2
|
+
|
|
3
|
+
## How to publish
|
|
4
|
+
|
|
5
|
+
To be able to import from path `@tomassabol/aws-services/s3` and not `@tomassabol/aws-services/dist/s3`, you have to publish the package with following command:
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
npm run dist
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## How to use it
|
|
12
|
+
|
|
13
|
+
```sh
|
|
14
|
+
npm install @tomassabol/aws-services
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import { getFromS3 } from "@tomassabol/aws-services/s3"
|
|
19
|
+
|
|
20
|
+
async function getMyObject() {
|
|
21
|
+
const params = {
|
|
22
|
+
bucket: "my-bucket-name",
|
|
23
|
+
key: "my-object-key",
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const data = await getFromS3(params)
|
|
27
|
+
}
|
|
28
|
+
```
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
image: node:18
|
|
2
|
+
|
|
3
|
+
definitions:
|
|
4
|
+
steps:
|
|
5
|
+
- step: &build-step
|
|
6
|
+
name: Build
|
|
7
|
+
caches:
|
|
8
|
+
- node
|
|
9
|
+
script:
|
|
10
|
+
- npm ci
|
|
11
|
+
- step: &format-step
|
|
12
|
+
name: Format
|
|
13
|
+
caches:
|
|
14
|
+
- node
|
|
15
|
+
script:
|
|
16
|
+
- npm run format
|
|
17
|
+
- step: &lint-step
|
|
18
|
+
name: Lint
|
|
19
|
+
caches:
|
|
20
|
+
- node
|
|
21
|
+
script:
|
|
22
|
+
- npm run lint
|
|
23
|
+
- step: &test-step
|
|
24
|
+
name: Test
|
|
25
|
+
caches:
|
|
26
|
+
- node
|
|
27
|
+
script:
|
|
28
|
+
- npm test
|
|
29
|
+
artifacts:
|
|
30
|
+
- .coverage/lcov.info
|
|
31
|
+
- .sonar/test-report.xml
|
|
32
|
+
- step: &code-analysis-step
|
|
33
|
+
name: Code Analysis
|
|
34
|
+
caches:
|
|
35
|
+
- sonar
|
|
36
|
+
script:
|
|
37
|
+
- pipe: sonarsource/sonarqube-scan:2.0.1
|
|
38
|
+
variables:
|
|
39
|
+
SONAR_HOST_URL: ${SONAR_HOST_URL}
|
|
40
|
+
SONAR_TOKEN: ${SONAR_TOKEN}
|
|
41
|
+
|
|
42
|
+
caches:
|
|
43
|
+
sonar: /opt/sonar-scanner/.sonar
|
|
44
|
+
|
|
45
|
+
clone:
|
|
46
|
+
depth: full
|
|
47
|
+
|
|
48
|
+
pipelines:
|
|
49
|
+
branches:
|
|
50
|
+
"{main,test,prod}":
|
|
51
|
+
- step: *build-step
|
|
52
|
+
- parallel:
|
|
53
|
+
- step: *format-step
|
|
54
|
+
- step: *lint-step
|
|
55
|
+
- step: *test-step
|
|
56
|
+
- step: *code-analysis-step
|
|
57
|
+
|
|
58
|
+
pull-requests:
|
|
59
|
+
"**":
|
|
60
|
+
- step: *build-step
|
|
61
|
+
- parallel:
|
|
62
|
+
- step: *format-step
|
|
63
|
+
- step: *lint-step
|
|
64
|
+
- step: *test-step
|
|
65
|
+
- step: *code-analysis-step
|
package/jest.config.js
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* For a detailed explanation regarding each configuration property and type check, visit:
|
|
3
|
+
* https://jestjs.io/docs/en/configuration.html
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
module.exports = {
|
|
7
|
+
/**
|
|
8
|
+
* Paths
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
// A list of paths to directories that Jest should use to search for files in
|
|
12
|
+
roots: ["./test"],
|
|
13
|
+
|
|
14
|
+
// The glob patterns Jest uses to detect test files
|
|
15
|
+
testMatch: ["<rootDir>/test/**/*.test.ts"],
|
|
16
|
+
|
|
17
|
+
// Transformers for files
|
|
18
|
+
transform: {
|
|
19
|
+
"^.+\\.tsx?$": [
|
|
20
|
+
"esbuild-jest",
|
|
21
|
+
{
|
|
22
|
+
sourcemap: true,
|
|
23
|
+
},
|
|
24
|
+
],
|
|
25
|
+
},
|
|
26
|
+
|
|
27
|
+
/*
|
|
28
|
+
* Results processing
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
// Indicates whether the coverage information should be collected while executing the test
|
|
32
|
+
collectCoverage: true,
|
|
33
|
+
|
|
34
|
+
// An array of glob patterns indicating a set of files for which coverage information should be collected
|
|
35
|
+
collectCoverageFrom: ["src/**/*.ts"],
|
|
36
|
+
|
|
37
|
+
// An array of regexp pattern strings used to skip coverage for certain files
|
|
38
|
+
coveragePathIgnorePatterns: ["src/logger.ts"],
|
|
39
|
+
|
|
40
|
+
// The directory where Jest should output its coverage files
|
|
41
|
+
coverageDirectory: ".coverage",
|
|
42
|
+
|
|
43
|
+
// Export test results fo SonarQube scanner to process test results
|
|
44
|
+
testResultsProcessor: "jest-sonar-reporter",
|
|
45
|
+
|
|
46
|
+
/*
|
|
47
|
+
* Test environment
|
|
48
|
+
*/
|
|
49
|
+
|
|
50
|
+
// The test environment that will be used for testing
|
|
51
|
+
testEnvironment: "node",
|
|
52
|
+
|
|
53
|
+
// Run for all tests
|
|
54
|
+
// setupFiles: ["<rootDir>/test/env.ts"],
|
|
55
|
+
|
|
56
|
+
/*
|
|
57
|
+
* Mocks
|
|
58
|
+
*/
|
|
59
|
+
|
|
60
|
+
// Automatically clear mock calls and instances between every test
|
|
61
|
+
clearMocks: true,
|
|
62
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@tomassabol/aws-services",
|
|
3
|
+
"version": "1.8.0",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"description": "AWS Services",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"build": "tsc -b tsconfig.build.json",
|
|
8
|
+
"clean": "rimraf dist .coverage .sonar",
|
|
9
|
+
"coverage": "open .coverage/lcov-report/index.html",
|
|
10
|
+
"dist": "tsc -b tsconfig.build.json && cp package.json dist && cp README.md dist && cd dist && npm publish",
|
|
11
|
+
"format": "prettier --check .",
|
|
12
|
+
"husky-install": "husky install",
|
|
13
|
+
"lint": "eslint . --max-warnings 0",
|
|
14
|
+
"test": "jest"
|
|
15
|
+
},
|
|
16
|
+
"peerDependencies": {
|
|
17
|
+
"@aws-lambda-powertools/logger": "^2.2.0",
|
|
18
|
+
"@aws-sdk/client-appconfig": "^3.388.0",
|
|
19
|
+
"@aws-sdk/client-dynamodb": "^3.388.0",
|
|
20
|
+
"@aws-sdk/client-eventbridge": "^3.388.0",
|
|
21
|
+
"@aws-sdk/client-s3": "^3.388.0",
|
|
22
|
+
"@aws-sdk/client-secrets-manager": "^3.388.0",
|
|
23
|
+
"@aws-sdk/client-sns": "^3.388.0",
|
|
24
|
+
"@aws-sdk/client-sqs": "^3.388.0",
|
|
25
|
+
"@aws-sdk/client-ssm": "^3.388.0",
|
|
26
|
+
"@aws-sdk/lib-dynamodb": "^3.388.0"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@aws-sdk/util-stream-node": "^3.374.0",
|
|
30
|
+
"@tsconfig/node18": "^1.0.1",
|
|
31
|
+
"@types/aws-sdk": "^2.7.0",
|
|
32
|
+
"@types/jest": "^29.4.0",
|
|
33
|
+
"@types/node": "^18.14.2",
|
|
34
|
+
"@typescript-eslint/eslint-plugin": "^5.25.0",
|
|
35
|
+
"@typescript-eslint/parser": "^5.25.0",
|
|
36
|
+
"aws-sdk-client-mock": "^3.0.0",
|
|
37
|
+
"esbuild-jest": "^0.4.0",
|
|
38
|
+
"eslint": "^8.11.0",
|
|
39
|
+
"eslint-config-prettier": "^8.3.0",
|
|
40
|
+
"eslint-plugin-import": "^2.25.4",
|
|
41
|
+
"eslint-plugin-json": "^3.1.0",
|
|
42
|
+
"eslint-plugin-markdown": "^3.0.0",
|
|
43
|
+
"husky": "^8.0.1",
|
|
44
|
+
"jest": "^29.4.3",
|
|
45
|
+
"jest-cli": "^29.4.3",
|
|
46
|
+
"jest-sonar-reporter": "^2.0.0",
|
|
47
|
+
"lint-staged": "^13.1.2",
|
|
48
|
+
"prettier": "^2.6.2",
|
|
49
|
+
"prettier-plugin-sh": "^0.12.8",
|
|
50
|
+
"rimraf": "^5.0.0",
|
|
51
|
+
"typedoc": "^0.24.4",
|
|
52
|
+
"typedoc-plugin-markdown": "^3.14.0",
|
|
53
|
+
"typescript": "^5.0.4"
|
|
54
|
+
},
|
|
55
|
+
"jestSonar": {
|
|
56
|
+
"reportPath": ".sonar",
|
|
57
|
+
"reportFile": "test-report.xml"
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
,"publishConfig": {
|
|
61
|
+
"access": "public"
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
sonar.projectKey=tomassaboldev_aws-services_bb5f4f68-64f8-4726-bdd4-579ceadf172d
|
|
2
|
+
sonar.sources=src
|
|
3
|
+
sonar.tests=test
|
|
4
|
+
sonar.exclusions=src/logger.ts
|
|
5
|
+
sonar.scm.provider=git
|
|
6
|
+
sonar.qualitygate.wait=true
|
|
7
|
+
sonar.javascript.lcov.reportPaths=.coverage/lcov.info
|
|
8
|
+
sonar.testExecutionReportPaths=.sonar/test-report.xml
|
package/src/appconfig.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AppConfigClient,
|
|
3
|
+
GetApplicationCommand,
|
|
4
|
+
} from "@aws-sdk/client-appconfig"
|
|
5
|
+
import { logger } from "./logger"
|
|
6
|
+
|
|
7
|
+
export const appConfigClient = new AppConfigClient({})
|
|
8
|
+
|
|
9
|
+
export const getApplication = async (applicationId: string) => {
|
|
10
|
+
try {
|
|
11
|
+
return await appConfigClient.send(
|
|
12
|
+
new GetApplicationCommand({
|
|
13
|
+
ApplicationId: applicationId,
|
|
14
|
+
})
|
|
15
|
+
)
|
|
16
|
+
} catch (error) {
|
|
17
|
+
logger.error("getApplication", { error, applicationId })
|
|
18
|
+
throw error
|
|
19
|
+
}
|
|
20
|
+
}
|
package/src/dynamodb.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { DynamoDBClient } from "@aws-sdk/client-dynamodb"
|
|
2
|
+
import { DynamoDBDocumentClient } from "@aws-sdk/lib-dynamodb"
|
|
3
|
+
|
|
4
|
+
export const client: DynamoDBClient = new DynamoDBClient({})
|
|
5
|
+
|
|
6
|
+
export const documentClient: DynamoDBDocumentClient =
|
|
7
|
+
DynamoDBDocumentClient.from(client)
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
import {
|
|
3
|
+
EventBridgeClient,
|
|
4
|
+
PutEventsCommand,
|
|
5
|
+
} from "@aws-sdk/client-eventbridge"
|
|
6
|
+
import { logger } from "./logger"
|
|
7
|
+
|
|
8
|
+
export const client = new EventBridgeClient({})
|
|
9
|
+
|
|
10
|
+
export async function sendEvent(params: {
|
|
11
|
+
source: string
|
|
12
|
+
eventBusName: string
|
|
13
|
+
eventType: string
|
|
14
|
+
event: any
|
|
15
|
+
}) {
|
|
16
|
+
const { eventBusName, eventType, event, source } = params
|
|
17
|
+
|
|
18
|
+
const input = {
|
|
19
|
+
Entries: [
|
|
20
|
+
{
|
|
21
|
+
EventBusName: eventBusName,
|
|
22
|
+
Source: source,
|
|
23
|
+
DetailType: eventType,
|
|
24
|
+
Detail: JSON.stringify(event),
|
|
25
|
+
},
|
|
26
|
+
],
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const command = new PutEventsCommand(input)
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
const result = await client.send(command)
|
|
33
|
+
|
|
34
|
+
/* Check if there are any failed events */
|
|
35
|
+
if (result.FailedEntryCount) {
|
|
36
|
+
const errorDetail = result.Entries?.length
|
|
37
|
+
? `${result.Entries[0].ErrorCode}: ${result.Entries[0].ErrorMessage}}`
|
|
38
|
+
: "unknown"
|
|
39
|
+
throw new Error(`PutEventsCommand error ${errorDetail}`)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
logger.debug("sendEvent success", { input, result })
|
|
43
|
+
} catch (error) {
|
|
44
|
+
logger.error("sendEvent error", { input, error })
|
|
45
|
+
throw error
|
|
46
|
+
}
|
|
47
|
+
}
|
package/src/logger.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Logger } from "@aws-lambda-powertools/logger"
|
|
2
|
+
|
|
3
|
+
const noLogger = {
|
|
4
|
+
log: () => undefined,
|
|
5
|
+
debug: () => undefined,
|
|
6
|
+
info: () => undefined,
|
|
7
|
+
error: () => undefined,
|
|
8
|
+
warn: () => undefined,
|
|
9
|
+
} as typeof console
|
|
10
|
+
|
|
11
|
+
export const logger =
|
|
12
|
+
process.env.JEST_WORKER_ID === undefined || process.env.TEST_LOGGER
|
|
13
|
+
? new Logger({ logLevel: "DEBUG" })
|
|
14
|
+
: noLogger
|
package/src/s3.ts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { S3, PutObjectCommandOutput } from "@aws-sdk/client-s3"
|
|
2
|
+
import { logger } from "./logger"
|
|
3
|
+
|
|
4
|
+
export const client = new S3({})
|
|
5
|
+
|
|
6
|
+
export async function putObjectToS3(params: {
|
|
7
|
+
bucket: string
|
|
8
|
+
key: string
|
|
9
|
+
body: string | Uint8Array | Buffer
|
|
10
|
+
contentEncoding: string
|
|
11
|
+
contentType: string
|
|
12
|
+
}): Promise<PutObjectCommandOutput> {
|
|
13
|
+
const { bucket, key, body, contentEncoding, contentType } = params
|
|
14
|
+
|
|
15
|
+
try {
|
|
16
|
+
return await client.putObject({
|
|
17
|
+
Bucket: bucket,
|
|
18
|
+
Key: key,
|
|
19
|
+
Body: body,
|
|
20
|
+
ContentEncoding: contentEncoding,
|
|
21
|
+
ContentType: contentType,
|
|
22
|
+
})
|
|
23
|
+
} catch (error) {
|
|
24
|
+
logger.error("putObjectToS3", {
|
|
25
|
+
error,
|
|
26
|
+
params: { bucket, key, contentEncoding, contentType },
|
|
27
|
+
})
|
|
28
|
+
throw error
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export async function getObjectFromS3(params: {
|
|
33
|
+
bucket: string
|
|
34
|
+
key: string
|
|
35
|
+
}): Promise<Uint8Array> {
|
|
36
|
+
try {
|
|
37
|
+
const { bucket, key } = params
|
|
38
|
+
|
|
39
|
+
const result = await client.getObject({
|
|
40
|
+
Bucket: bucket,
|
|
41
|
+
Key: key,
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
const body = result.Body
|
|
45
|
+
|
|
46
|
+
if (body === undefined) {
|
|
47
|
+
logger.error("Body not found", { params })
|
|
48
|
+
throw new Error("Body not found")
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return await body.transformToByteArray()
|
|
52
|
+
} catch (error) {
|
|
53
|
+
logger.error("getObjectFromS3", { error, params })
|
|
54
|
+
throw error
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import {
|
|
2
|
+
SecretsManagerClient,
|
|
3
|
+
GetSecretValueCommand,
|
|
4
|
+
} from "@aws-sdk/client-secrets-manager"
|
|
5
|
+
import assert from "assert"
|
|
6
|
+
import { logger } from "./logger"
|
|
7
|
+
|
|
8
|
+
export const client = new SecretsManagerClient({})
|
|
9
|
+
|
|
10
|
+
export async function getSecret(secretName: string): Promise<string> {
|
|
11
|
+
try {
|
|
12
|
+
const response = await client.send(
|
|
13
|
+
new GetSecretValueCommand({
|
|
14
|
+
SecretId: secretName,
|
|
15
|
+
})
|
|
16
|
+
)
|
|
17
|
+
const value = response.SecretString
|
|
18
|
+
assert(value, "SecretString value not found")
|
|
19
|
+
return value
|
|
20
|
+
} catch (error) {
|
|
21
|
+
logger.error("getSecret", { error, secretName })
|
|
22
|
+
// For a list of exceptions thrown, see
|
|
23
|
+
// https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_GetSecretValue.html
|
|
24
|
+
throw error
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export async function getSecretAsObject<T = unknown>(
|
|
29
|
+
secretName: string
|
|
30
|
+
): Promise<T> {
|
|
31
|
+
const value = await getSecret(secretName)
|
|
32
|
+
try {
|
|
33
|
+
return JSON.parse(value)
|
|
34
|
+
} catch (error) {
|
|
35
|
+
throw Error(`Cannot parse secret ${secretName}. Expected JSON.`)
|
|
36
|
+
}
|
|
37
|
+
}
|
package/src/sns.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { SNSClient, PublishCommand } from "@aws-sdk/client-sns"
|
|
2
|
+
import { logger } from "./logger"
|
|
3
|
+
|
|
4
|
+
export const snsClient = new SNSClient({})
|
|
5
|
+
|
|
6
|
+
export const publishMessageToSNS = async (params: {
|
|
7
|
+
topicArn: string
|
|
8
|
+
message: string
|
|
9
|
+
options?: object
|
|
10
|
+
}) => {
|
|
11
|
+
try {
|
|
12
|
+
const { topicArn, message, options } = params
|
|
13
|
+
return await snsClient.send(
|
|
14
|
+
new PublishCommand({
|
|
15
|
+
TopicArn: topicArn,
|
|
16
|
+
Message: message,
|
|
17
|
+
...options,
|
|
18
|
+
})
|
|
19
|
+
)
|
|
20
|
+
} catch (error) {
|
|
21
|
+
logger.error("publishMessageToSNS", { error, params })
|
|
22
|
+
throw error
|
|
23
|
+
}
|
|
24
|
+
}
|
package/src/sqs.mock.ts
ADDED
package/src/sqs.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import {
|
|
2
|
+
SQSClient,
|
|
3
|
+
SendMessageCommand,
|
|
4
|
+
SendMessageCommandInput,
|
|
5
|
+
} from "@aws-sdk/client-sqs"
|
|
6
|
+
import { logger } from "./logger"
|
|
7
|
+
|
|
8
|
+
export const client = new SQSClient({})
|
|
9
|
+
|
|
10
|
+
export async function sendSqsMessage(params: {
|
|
11
|
+
queueUrl: string
|
|
12
|
+
message: string | object
|
|
13
|
+
}): Promise<string | undefined> {
|
|
14
|
+
const { queueUrl, message } = params
|
|
15
|
+
|
|
16
|
+
try {
|
|
17
|
+
const body = typeof message === "object" ? JSON.stringify(message) : message
|
|
18
|
+
|
|
19
|
+
const input: SendMessageCommandInput = {
|
|
20
|
+
QueueUrl: queueUrl,
|
|
21
|
+
MessageBody: body,
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const command = new SendMessageCommand(input)
|
|
25
|
+
|
|
26
|
+
const response = await client.send(command)
|
|
27
|
+
return response.MessageId
|
|
28
|
+
} catch (error) {
|
|
29
|
+
logger.error("sendSqsMessage", { error, params })
|
|
30
|
+
throw error
|
|
31
|
+
}
|
|
32
|
+
}
|
package/src/ssm.mock.ts
ADDED
package/src/ssm.ts
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { SSMClient, GetParametersCommand } from "@aws-sdk/client-ssm"
|
|
2
|
+
import assert = require("assert")
|
|
3
|
+
|
|
4
|
+
export const client = new SSMClient({})
|
|
5
|
+
|
|
6
|
+
export type SSMParameters = {
|
|
7
|
+
[name: string]: string
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Get a list of parameters from SSM Parameter Store
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```typescript
|
|
15
|
+
* const params = await getSsmParameters({
|
|
16
|
+
* first: "/my-params/first",
|
|
17
|
+
* second: "/my-params/second",
|
|
18
|
+
* })
|
|
19
|
+
*
|
|
20
|
+
* // params = { first: "value1", second: "value2" }
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
export async function getSsmParameters<T extends SSMParameters>(
|
|
25
|
+
parameters: T
|
|
26
|
+
): Promise<T> {
|
|
27
|
+
/*
|
|
28
|
+
* Fetch parameters from SSM
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
const command = new GetParametersCommand({
|
|
32
|
+
Names: Object.values(parameters),
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
const output = await client.send(command)
|
|
36
|
+
|
|
37
|
+
/*
|
|
38
|
+
* Map returned parameters to result object
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
const result: Record<string, string> = {}
|
|
42
|
+
output.Parameters?.forEach((outputParam) => {
|
|
43
|
+
assert(
|
|
44
|
+
typeof outputParam.Value === "string",
|
|
45
|
+
`Received invalid value of SSM parameter ${outputParam.Name}`
|
|
46
|
+
)
|
|
47
|
+
const entry = Object.entries(parameters).find(
|
|
48
|
+
([_key, value]) => value === outputParam.Name
|
|
49
|
+
)
|
|
50
|
+
assert(entry, `Received invalid SSM parameter ${outputParam.Name}`)
|
|
51
|
+
const [key] = entry
|
|
52
|
+
result[key] = outputParam.Value
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
/*
|
|
56
|
+
* Check that all parameters were received
|
|
57
|
+
*/
|
|
58
|
+
|
|
59
|
+
const notFoundParams = Object.keys(parameters).filter(
|
|
60
|
+
(key) => false === Object.hasOwn(result, key)
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
if (notFoundParams.length > 0) {
|
|
64
|
+
throw new Error(
|
|
65
|
+
`Cannot obtain SSM parameters: ${notFoundParams.join(", ")}`
|
|
66
|
+
)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return result as T
|
|
70
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { GetApplicationCommand } from "@aws-sdk/client-appconfig"
|
|
2
|
+
import { getApplication, appConfigClient } from "../src/appconfig"
|
|
3
|
+
|
|
4
|
+
jest.mock("@aws-sdk/client-appconfig", () => {
|
|
5
|
+
return {
|
|
6
|
+
AppConfigClient: jest.fn().mockImplementation(() => {
|
|
7
|
+
return { send: jest.fn() }
|
|
8
|
+
}),
|
|
9
|
+
GetApplicationCommand: jest.fn(),
|
|
10
|
+
}
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
describe("AppConfig", () => {
|
|
14
|
+
let getApplicationCommandMock: jest.Mock
|
|
15
|
+
|
|
16
|
+
beforeEach(() => {
|
|
17
|
+
getApplicationCommandMock = GetApplicationCommand as unknown as jest.Mock
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
afterEach(() => {
|
|
21
|
+
jest.clearAllMocks()
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
it("should call AppConfig.getApplication with correct parameters", async () => {
|
|
25
|
+
const applicationId = "test-app"
|
|
26
|
+
|
|
27
|
+
await getApplication(applicationId)
|
|
28
|
+
|
|
29
|
+
expect(appConfigClient.send).toHaveBeenCalledWith(
|
|
30
|
+
expect.any(getApplicationCommandMock)
|
|
31
|
+
)
|
|
32
|
+
expect(getApplicationCommandMock).toHaveBeenCalledWith({
|
|
33
|
+
ApplicationId: applicationId,
|
|
34
|
+
})
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
it("should throw an error if getting application from AppConfig fails", async () => {
|
|
38
|
+
const applicationId = "test-app"
|
|
39
|
+
const error = new Error("Get application failed")
|
|
40
|
+
|
|
41
|
+
;(appConfigClient.send as jest.Mock).mockRejectedValueOnce(error)
|
|
42
|
+
|
|
43
|
+
await expect(getApplication(applicationId)).rejects.toThrow(error)
|
|
44
|
+
})
|
|
45
|
+
})
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { client, documentClient } from "../src/dynamodb"
|
|
2
|
+
import { DynamoDBClient } from "@aws-sdk/client-dynamodb"
|
|
3
|
+
import { DynamoDBDocumentClient } from "@aws-sdk/lib-dynamodb"
|
|
4
|
+
|
|
5
|
+
describe("dynamodb", () => {
|
|
6
|
+
it("should be instance of DynamoDBClient", () => {
|
|
7
|
+
expect(client).toBeInstanceOf(DynamoDBClient)
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
it("should be instance of DynamoDBDocumentClient", () => {
|
|
11
|
+
expect(documentClient).toBeInstanceOf(DynamoDBDocumentClient)
|
|
12
|
+
})
|
|
13
|
+
})
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { mockClient } from "aws-sdk-client-mock"
|
|
2
|
+
import { PutEventsCommand } from "@aws-sdk/client-eventbridge"
|
|
3
|
+
import { client, sendEvent } from "../src/eventbridge"
|
|
4
|
+
|
|
5
|
+
const eventBridgeMock = mockClient(client)
|
|
6
|
+
|
|
7
|
+
describe("eventbridge", () => {
|
|
8
|
+
describe("sendEvent", () => {
|
|
9
|
+
test("should send event to event bus", async () => {
|
|
10
|
+
eventBridgeMock.on(PutEventsCommand).resolves({})
|
|
11
|
+
|
|
12
|
+
const params = {
|
|
13
|
+
source: "source",
|
|
14
|
+
eventBusName: "event-bus-name",
|
|
15
|
+
eventType: "event-type",
|
|
16
|
+
event: { foo: "bar" },
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
await expect(sendEvent(params)).resolves.toBe(undefined)
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
test("should fail", async () => {
|
|
23
|
+
eventBridgeMock.on(PutEventsCommand).rejects(new Error("fail"))
|
|
24
|
+
|
|
25
|
+
const params = {
|
|
26
|
+
source: "source",
|
|
27
|
+
eventBusName: "event-bus-name",
|
|
28
|
+
eventType: "event-type",
|
|
29
|
+
event: { foo: "bar" },
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
await expect(sendEvent(params)).rejects.toThrowError("fail")
|
|
33
|
+
})
|
|
34
|
+
})
|
|
35
|
+
})
|
package/test/s3.test.ts
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
import { mockClient } from "aws-sdk-client-mock"
|
|
3
|
+
import { GetObjectCommand, PutObjectCommand } from "@aws-sdk/client-s3"
|
|
4
|
+
import { Readable } from "stream"
|
|
5
|
+
import { sdkStreamMixin } from "@aws-sdk/util-stream-node"
|
|
6
|
+
import { client, getObjectFromS3, putObjectToS3 } from "../src/s3"
|
|
7
|
+
|
|
8
|
+
const s3Mock = mockClient(client)
|
|
9
|
+
|
|
10
|
+
describe("S3", () => {
|
|
11
|
+
describe("putObjectToS3()", () => {
|
|
12
|
+
test("should put object to S3", async () => {
|
|
13
|
+
s3Mock.on(PutObjectCommand).resolves({
|
|
14
|
+
ETag: "etag",
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
const params = {
|
|
18
|
+
bucket: "bucket",
|
|
19
|
+
key: "key",
|
|
20
|
+
body: "body",
|
|
21
|
+
contentEncoding: "content-encoding",
|
|
22
|
+
contentType: "content-type",
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
await expect(putObjectToS3(params)).resolves.toEqual({ ETag: "etag" })
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
test("should fail when put object to S3 fails", async () => {
|
|
29
|
+
s3Mock.on(PutObjectCommand).rejects(new Error("fail"))
|
|
30
|
+
|
|
31
|
+
await expect(putObjectToS3({} as any)).rejects.toThrowError("fail")
|
|
32
|
+
})
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
describe("getObjectFromS3()", () => {
|
|
36
|
+
test("should get object from S3", async () => {
|
|
37
|
+
s3Mock.on(GetObjectCommand).resolves({
|
|
38
|
+
ETag: "etag",
|
|
39
|
+
Body: sdkStreamMixin(Readable.from("object")),
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
const params = {
|
|
43
|
+
bucket: "bucket",
|
|
44
|
+
key: "key",
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const data = (await getObjectFromS3(params)) as any
|
|
48
|
+
|
|
49
|
+
expect(data).toMatchInlineSnapshot(`
|
|
50
|
+
Uint8Array [
|
|
51
|
+
111,
|
|
52
|
+
98,
|
|
53
|
+
106,
|
|
54
|
+
101,
|
|
55
|
+
99,
|
|
56
|
+
116,
|
|
57
|
+
]
|
|
58
|
+
`)
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
test("should fail when get object body is undefined", async () => {
|
|
62
|
+
s3Mock.on(GetObjectCommand).resolves({
|
|
63
|
+
ETag: "etag",
|
|
64
|
+
Body: undefined,
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
await expect(getObjectFromS3({} as any)).rejects.toThrowError(
|
|
68
|
+
"Body not found"
|
|
69
|
+
)
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
test("should fail when get object from S3 fails", async () => {
|
|
73
|
+
s3Mock.on(GetObjectCommand).rejects(new Error("fail"))
|
|
74
|
+
|
|
75
|
+
await expect(getObjectFromS3({} as any)).rejects.toThrowError("fail")
|
|
76
|
+
})
|
|
77
|
+
})
|
|
78
|
+
})
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { mockClient } from "aws-sdk-client-mock"
|
|
2
|
+
import { GetSecretValueCommand } from "@aws-sdk/client-secrets-manager"
|
|
3
|
+
import { client, getSecret, getSecretAsObject } from "../src/secrets-manager"
|
|
4
|
+
|
|
5
|
+
const secretsManagerMock = mockClient(client)
|
|
6
|
+
|
|
7
|
+
describe("secrets-manager", () => {
|
|
8
|
+
describe("getSecret()", () => {
|
|
9
|
+
test("should retrieve secret string", async () => {
|
|
10
|
+
secretsManagerMock.on(GetSecretValueCommand).resolves({
|
|
11
|
+
SecretString: "secret-string",
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
await expect(getSecret("secret-name")).resolves.toBe("secret-string")
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
test("should fail when string empty", async () => {
|
|
18
|
+
secretsManagerMock.on(GetSecretValueCommand).resolves({
|
|
19
|
+
SecretString: "",
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
await expect(getSecret("secret-name")).rejects.toThrow(
|
|
23
|
+
"SecretString value not found"
|
|
24
|
+
)
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
test("should fail", async () => {
|
|
28
|
+
secretsManagerMock.on(GetSecretValueCommand).rejects(new Error("fail"))
|
|
29
|
+
|
|
30
|
+
await expect(getSecret("secret-name")).rejects.toThrowError("fail")
|
|
31
|
+
})
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
describe("getSecretAsObject()", () => {
|
|
35
|
+
test("should return secret object", async () => {
|
|
36
|
+
secretsManagerMock.on(GetSecretValueCommand).resolves({
|
|
37
|
+
SecretString: JSON.stringify({ foo: "bar" }),
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
await expect(getSecretAsObject("secret-name")).resolves.toEqual({
|
|
41
|
+
foo: "bar",
|
|
42
|
+
})
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
test("should fail when invalid object", async () => {
|
|
46
|
+
secretsManagerMock.on(GetSecretValueCommand).resolves({
|
|
47
|
+
SecretString: "invalid-object",
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
await expect(getSecretAsObject("secret-name")).rejects.toThrowError(
|
|
51
|
+
"Cannot parse secret secret-name. Expected JSON."
|
|
52
|
+
)
|
|
53
|
+
})
|
|
54
|
+
})
|
|
55
|
+
})
|
package/test/sns.test.ts
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { SNSClient, PublishCommand } from "@aws-sdk/client-sns"
|
|
2
|
+
import { publishMessageToSNS, snsClient } from "../src/sns"
|
|
3
|
+
|
|
4
|
+
jest.mock("@aws-sdk/client-sns", () => {
|
|
5
|
+
return {
|
|
6
|
+
SNSClient: jest.fn(() => {
|
|
7
|
+
return {
|
|
8
|
+
send: jest.fn(),
|
|
9
|
+
}
|
|
10
|
+
}),
|
|
11
|
+
PublishCommand: jest.fn(),
|
|
12
|
+
}
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
describe("SNS", () => {
|
|
16
|
+
beforeEach(() => {
|
|
17
|
+
;(SNSClient as jest.Mock).mockClear()
|
|
18
|
+
;(PublishCommand as unknown as jest.Mock).mockClear()
|
|
19
|
+
;(snsClient.send as jest.Mock).mockClear()
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
it("should create instance of SNSClient", () => {
|
|
23
|
+
expect(snsClient).toBeDefined()
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
it("should call publishSNSMessage successfully", async () => {
|
|
27
|
+
const sendMock = jest.fn()
|
|
28
|
+
;(snsClient.send as jest.Mock) = sendMock
|
|
29
|
+
|
|
30
|
+
const params = {
|
|
31
|
+
topicArn: "arn:aws:sns:eu-central-1:123456789012:MyTopic",
|
|
32
|
+
message: "Test message",
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
await publishMessageToSNS(params)
|
|
36
|
+
expect(sendMock).toBeCalledTimes(1)
|
|
37
|
+
expect(PublishCommand).toHaveBeenCalledWith({
|
|
38
|
+
TopicArn: params.topicArn,
|
|
39
|
+
Message: params.message,
|
|
40
|
+
})
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
it("should throw an error if publishSNSMessage fails", async () => {
|
|
44
|
+
const sendMock = jest
|
|
45
|
+
.fn()
|
|
46
|
+
.mockRejectedValue(new Error("Failed to send message"))
|
|
47
|
+
;(snsClient.send as jest.Mock) = sendMock
|
|
48
|
+
|
|
49
|
+
const params = {
|
|
50
|
+
topicArn: "arn:aws:sns:euj-central-1:123456789012:MyTopic",
|
|
51
|
+
message: "Test message",
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
await expect(publishMessageToSNS(params)).rejects.toThrow(
|
|
55
|
+
"Failed to send message"
|
|
56
|
+
)
|
|
57
|
+
expect(sendMock).toBeCalledTimes(1)
|
|
58
|
+
})
|
|
59
|
+
})
|
package/test/sqs.test.ts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { mockClient } from "aws-sdk-client-mock"
|
|
2
|
+
import { SendMessageCommand } from "@aws-sdk/client-sqs"
|
|
3
|
+
import { client, sendSqsMessage } from "../src/sqs"
|
|
4
|
+
|
|
5
|
+
const sqsMock = mockClient(client)
|
|
6
|
+
|
|
7
|
+
describe("sqs", () => {
|
|
8
|
+
describe("sendSqsMessage()", () => {
|
|
9
|
+
test("should send string as message to SQS", async () => {
|
|
10
|
+
sqsMock.on(SendMessageCommand).resolves({
|
|
11
|
+
MessageId: "message-id",
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
const params = {
|
|
15
|
+
queueUrl: "queue-url",
|
|
16
|
+
message: "message",
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
await expect(sendSqsMessage(params)).resolves.toBe("message-id")
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
test("should send stringified object as message to SQS", async () => {
|
|
23
|
+
sqsMock.on(SendMessageCommand).resolves({
|
|
24
|
+
MessageId: "message-id",
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
const params = {
|
|
28
|
+
queueUrl: "queue-url",
|
|
29
|
+
message: { foo: "bar" },
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
await expect(sendSqsMessage(params)).resolves.toBe("message-id")
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
test("should fail", async () => {
|
|
36
|
+
sqsMock.on(SendMessageCommand).rejects(new Error("fail"))
|
|
37
|
+
|
|
38
|
+
const params = {
|
|
39
|
+
queueUrl: "queue-url",
|
|
40
|
+
message: "message",
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
await expect(sendSqsMessage(params)).rejects.toThrowError("fail")
|
|
44
|
+
})
|
|
45
|
+
})
|
|
46
|
+
})
|
package/test/ssm.test.ts
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
import { mockClient } from "aws-sdk-client-mock"
|
|
3
|
+
import { GetParametersCommand } from "@aws-sdk/client-ssm"
|
|
4
|
+
import { client, getSsmParameters } from "../src/ssm"
|
|
5
|
+
|
|
6
|
+
const ssmMock = mockClient(client)
|
|
7
|
+
|
|
8
|
+
describe("ssm", () => {
|
|
9
|
+
describe("getSsmParameters()", () => {
|
|
10
|
+
test("should return paramaters", async () => {
|
|
11
|
+
ssmMock.on(GetParametersCommand).resolves({
|
|
12
|
+
Parameters: [
|
|
13
|
+
{ Name: "/my-params/foo", Value: "bar" },
|
|
14
|
+
{ Name: "/my-params/baz", Value: "qux" },
|
|
15
|
+
],
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
const params = {
|
|
19
|
+
foo: "/my-params/foo",
|
|
20
|
+
baz: "/my-params/baz",
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
await expect(getSsmParameters(params)).resolves.toEqual({
|
|
24
|
+
foo: "bar",
|
|
25
|
+
baz: "qux",
|
|
26
|
+
})
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
test("should fail when SSM parameter name is not string", async () => {
|
|
30
|
+
ssmMock.on(GetParametersCommand).resolves({
|
|
31
|
+
Parameters: [{ Name: 42 as any, Value: "bar" }],
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
const params = {
|
|
35
|
+
foo: "/my-params/foo",
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
await expect(getSsmParameters(params)).rejects.toThrow(
|
|
39
|
+
"Received invalid SSM parameter 42"
|
|
40
|
+
)
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
test("should fail when SSM parameter does not contain Parameters", async () => {
|
|
44
|
+
ssmMock.on(GetParametersCommand).resolves({})
|
|
45
|
+
|
|
46
|
+
const params = {
|
|
47
|
+
foo: "/my-params/foo",
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
await expect(getSsmParameters(params)).rejects.toThrow(
|
|
51
|
+
"Cannot obtain SSM parameters: foo"
|
|
52
|
+
)
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
test("should fail when SSM parameter value is not string", async () => {
|
|
56
|
+
ssmMock.on(GetParametersCommand).resolves({
|
|
57
|
+
Parameters: [{ Name: "/my-params/foo", Value: 42 as any }],
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
const params = {
|
|
61
|
+
foo: "/my-params/foo",
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
await expect(getSsmParameters(params)).rejects.toThrow(
|
|
65
|
+
"Received invalid value of SSM parameter /my-params/foo"
|
|
66
|
+
)
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
test("should fail when not all parameters are found", async () => {
|
|
70
|
+
ssmMock.on(GetParametersCommand).resolves({
|
|
71
|
+
Parameters: [{ Name: "/my-params/foo", Value: "bar" }],
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
const params = {
|
|
75
|
+
foo: "/my-params/foo",
|
|
76
|
+
baz: "/my-params/baz",
|
|
77
|
+
quux: "/my-params/quux",
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
await expect(getSsmParameters(params)).rejects.toThrow(
|
|
81
|
+
"Cannot obtain SSM parameters: baz, quux"
|
|
82
|
+
)
|
|
83
|
+
})
|
|
84
|
+
})
|
|
85
|
+
})
|
package/tsconfig.json
ADDED
package/typedoc.json
ADDED