├── .eslintrc.js ├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── PULL_REQUEST_TEMPLATE.md ├── dependabot.yml ├── mergify.yml ├── renovate.json5 └── workflows │ ├── SUBSTRATE_CODEQL.yml │ ├── commitlint.yml │ ├── pr-success.yml │ └── release.yml ├── .gitignore ├── .mergify.yml ├── .nvmrc ├── .prettierrc ├── .releaserc.json ├── CHANGELOG.md ├── README.md ├── commitlint.config.js ├── docs └── rules │ ├── instrument-aws-clients.md │ ├── instrument-document-clients.md │ ├── no-aws-import.md │ └── no-direct-lambda-invoke.md ├── lib ├── index.js └── rules │ ├── instrument-aws-clients.js │ ├── instrument-document-clients.js │ ├── no-aws-import.js │ └── no-direct-lambda-invoke.js ├── package-lock.json ├── package.json └── tests └── lib └── rules ├── instrument-aws-clients.js ├── instrument-document-clients.js ├── no-aws-import.js └── no-direct-lambda-invoke.js /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | node: true, 4 | es2021: true, 5 | }, 6 | extends: "eslint:recommended", 7 | parserOptions: { 8 | ecmaVersion: 12, 9 | sourceType: "module", 10 | }, 11 | rules: {}, 12 | } 13 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @Stedi/cloud 2 | 3 | # Allow bots to modify and merge these files without explicit PR approval from the team 4 | package.json 5 | package-lock.json 6 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "🐛 Bug report" 3 | about: Create a bug report 4 | --- 5 | 6 | 7 | 8 | 9 | 10 | 11 | 14 | 15 | ⚠️⚠️ REPLACE WITH DESCRIPTION 16 | 17 | 20 | 21 |
22 | problem.js 23 | 24 | ```yaml 25 | # ⚠️⚠️ REPLACE THIS COMMENT WITH FULL CONTENT 26 | ``` 27 | 28 |
29 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "🎉 Feature/rule request" 3 | about: Suggest an idea 4 | --- 5 | 6 | 7 | 8 | 9 | 12 | 13 | ### Rule name 14 | 15 | ``` 16 | ⚠️ REPLACE WITH NAME 17 | ``` 18 | 19 | 22 | 23 | ### Use case description 24 | 25 | ⚠️ REPLACE WITH DESCRIPTION 26 | 27 | 30 | 31 | ### Examples 32 | 33 | #### Incorrect 34 | 35 | ```js 36 | ⚠️ REPLACE WITH CODE 37 | ``` 38 | 39 | #### Correct 40 | 41 | ```js 42 | ⚠️ REPLACE WITH CODE 43 | ``` 44 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Closes: #{ISSUE_NUMBER} 2 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | registries: 3 | npm-registry-npm-pkg-github-com: 4 | url: https://githubproxy.dev.terminal.stedi.com 5 | type: npm-registry 6 | token: ${{secrets.DEPENDABOT_PACKAGE_TOKEN}} 7 | npm-registry-npm-pkg-github-com-stedi: 8 | url: https://githubproxy.dev.terminal.stedi.com/Stedi 9 | type: npm-registry 10 | token: ${{secrets.DEPENDABOT_PACKAGE_TOKEN}} 11 | updates: 12 | - package-ecosystem: npm 13 | directory: / 14 | schedule: 15 | interval: daily 16 | time: "07:00" 17 | ignore: 18 | registries: "*" 19 | -------------------------------------------------------------------------------- /.github/mergify.yml: -------------------------------------------------------------------------------- 1 | # https://github.com/Stedi/renovate-config/blob/main/.mergify.yml 2 | extends: renovate-config 3 | 4 | defaults: 5 | actions: 6 | request_reviews: 7 | teams: 8 | - cloud 9 | 10 | pull_request_rules: 11 | - name: auto-approve release 12 | conditions: 13 | - author=release-please[bot] 14 | - label~=autorelease 15 | actions: 16 | review: 17 | type: APPROVE 18 | message: Automatically approving release 19 | 20 | - name: Team member approval 21 | conditions: 22 | - author=@cloud 23 | - label=auto-approve 24 | actions: 25 | review: 26 | type: APPROVE 27 | message: Auto-approving based on team member request. -------------------------------------------------------------------------------- /.github/renovate.json5: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": ["local>Stedi/renovate-config", ":semanticCommits"], 4 | "schedule": "after 10:00 and before 16:00 every weekday", 5 | "commitMessageExtra": "from {{displayFrom}} to {{#if isPinDigest}}{{{newDigestShort}}}{{else}}{{#if isMajor}}{{prettyNewMajor}}{{else}}{{#if isSingleVersion}}{{prettyNewVersion}}{{else}}{{#if newValue}}{{{newValue}}}{{else}}{{{newDigestShort}}}{{/if}}{{/if}}{{/if}}{{/if}}", 6 | "ignorePaths": ["generated-sdk/**"] 7 | } 8 | -------------------------------------------------------------------------------- /.github/workflows/SUBSTRATE_CODEQL.yml: -------------------------------------------------------------------------------- 1 | name: "CodeQL" 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | # The branches below must be a subset of the branches above 8 | branches: [main] 9 | schedule: 10 | - cron: "27 2 * * 1" 11 | 12 | jobs: 13 | analyze: 14 | name: Analyze 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - name: Checkout repository 19 | uses: actions/checkout@ee0669bd1cc54295c223e0bb666b733df41de1c5 # v2 20 | 21 | # Initializes the CodeQL tools for scanning. 22 | - name: Initialize CodeQL 23 | uses: github/codeql-action/init@231aa2c8a89117b126725a0e11897209b7118144 # v1 24 | # If you wish to specify custom queries, you can do so here or in a config file. 25 | # By default, queries listed here will override any specified in a config file. 26 | # Prefix the list here with "+" to use these queries and those in the config file. 27 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 28 | 29 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 30 | # If this step fails, then you should remove it and run the build manually (see below) 31 | - name: Autobuild 32 | uses: github/codeql-action/autobuild@231aa2c8a89117b126725a0e11897209b7118144 # v1 33 | 34 | # ℹ️ Command-line programs to run using the OS shell. 35 | # 📚 https://git.io/JvXDl 36 | 37 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 38 | # and modify them (or add more) to build your code if your project 39 | # uses a compiled language 40 | 41 | #- run: | 42 | # make bootstrap 43 | # make release 44 | 45 | - name: Perform CodeQL Analysis 46 | uses: github/codeql-action/analyze@231aa2c8a89117b126725a0e11897209b7118144 # v1 47 | -------------------------------------------------------------------------------- /.github/workflows/commitlint.yml: -------------------------------------------------------------------------------- 1 | name: Lint Commit Messages 2 | on: [pull_request] 3 | 4 | jobs: 5 | commitlint: 6 | runs-on: ubuntu-latest 7 | steps: 8 | - uses: actions/checkout@ee0669bd1cc54295c223e0bb666b733df41de1c5 # v2 9 | with: 10 | fetch-depth: 0 11 | - uses: wagoid/commitlint-github-action@4b1bcb1c72f99fbd6aa6b34cc3fb59200f01f993 # v2 12 | -------------------------------------------------------------------------------- /.github/workflows/pr-success.yml: -------------------------------------------------------------------------------- 1 | name: pr-success 2 | on: 3 | pull_request: {} 4 | merge_group: 5 | types: [ checks_requested ] 6 | 7 | jobs: 8 | pr-success: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@50fbc622fc4ef5163becd7fab6573eac35f8462e # v1 12 | - name: Install Deps 13 | run: npm ci 14 | - name: Test 15 | run: npm test 16 | reviewdog: 17 | runs-on: ubuntu-latest 18 | steps: 19 | - uses: actions/checkout@50fbc622fc4ef5163becd7fab6573eac35f8462e # v1 20 | - name: Lint 21 | run: npm run lint 22 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | on: 3 | push: 4 | branches: 5 | - main 6 | jobs: 7 | release: 8 | name: Release 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Checkout 12 | uses: actions/checkout@ee0669bd1cc54295c223e0bb666b733df41de1c5 # v2 13 | with: 14 | fetch-depth: 0 15 | - name: Setup Node.js 16 | uses: actions/setup-node@f1f314fca9dfce2769ece7d933488f076716723e # v1 17 | with: 18 | node-version: 14 19 | - name: Install dependencies 20 | run: npm ci 21 | env: 22 | GITHUB_TOKEN: ${{ secrets.ENG_GITHUB_TOKEN }} 23 | - name: Release 24 | env: 25 | GITHUB_TOKEN: ${{ secrets.ENG_GITHUB_TOKEN }} 26 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 27 | run: npm run release 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .history 3 | .idea 4 | -------------------------------------------------------------------------------- /.mergify.yml: -------------------------------------------------------------------------------- 1 | pull_request_rules: 2 | - name: Dependabot 3 | conditions: 4 | - author=dependabot[bot] 5 | - status-success=Test 6 | actions: 7 | merge: 8 | method: squash 9 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 6.17.1 2 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "all", 3 | "tabWidth": 2, 4 | "semi": false, 5 | "singleQuote": false 6 | } 7 | -------------------------------------------------------------------------------- /.releaserc.json: -------------------------------------------------------------------------------- 1 | { 2 | "branches": ["main"], 3 | "plugins": [ 4 | "@semantic-release/commit-analyzer", 5 | "@semantic-release/release-notes-generator", 6 | [ 7 | "@semantic-release/changelog", 8 | { 9 | "changelogFile": "CHANGELOG.md" 10 | } 11 | ], 12 | [ 13 | "@semantic-release/git", 14 | { 15 | "assets": ["CHANGELOG.md"] 16 | } 17 | ], 18 | ["@semantic-release/npm"] 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [1.5.2](https://github.com/Stedi/eslint-plugin-stedi-aws-rules/compare/v1.5.1...v1.5.2) (2022-04-20) 2 | 3 | 4 | ### Bug Fixes 5 | 6 | * allow newer versions of eslint ([773aa06](https://github.com/Stedi/eslint-plugin-stedi-aws-rules/commit/773aa06fdbd49bf65253b3329ec9e83394b5731f)) 7 | 8 | ## [1.5.1](https://github.com/Stedi/eslint-plugin-stedi-aws-rules/compare/v1.5.0...v1.5.1) (2020-10-30) 9 | 10 | 11 | ### Bug Fixes 12 | 13 | * fix issue with direct call expressions, add test ([#25](https://github.com/Stedi/eslint-plugin-stedi-aws-rules/issues/25)) ([dfb63bc](https://github.com/Stedi/eslint-plugin-stedi-aws-rules/commit/dfb63bc71c4366cdd23cdd0b7516f1ebd416d26d)) 14 | 15 | # [1.5.0](https://github.com/Stedi/eslint-plugin-stedi-aws-rules/compare/v1.4.0...v1.5.0) (2020-10-30) 16 | 17 | 18 | ### Features 19 | 20 | * publish plugin to the [@stedi-oss](https://github.com/stedi-oss) namespace instead of [@stedi](https://github.com/stedi) ([fe0f4c8](https://github.com/Stedi/eslint-plugin-stedi-aws-rules/commit/fe0f4c8ab0686f400bc71ec79aa28b9c49a2fc6b)) 21 | 22 | # [1.4.0](https://github.com/Stedi/eslint-plugin-stedi-aws-rules/compare/v1.3.1...v1.4.0) (2020-10-30) 23 | 24 | 25 | ### Features 26 | 27 | * new rule no-direct-lambda-invoke ([#23](https://github.com/Stedi/eslint-plugin-stedi-aws-rules/issues/23)) ([112c42d](https://github.com/Stedi/eslint-plugin-stedi-aws-rules/commit/112c42d3a6f3c0f4f7a7ecf0f0e909192a7f17b1)) 28 | 29 | ## [1.3.1](https://github.com/Stedi/eslint-plugin-stedi-aws-rules/compare/v1.3.0...v1.3.1) (2020-10-29) 30 | 31 | 32 | ### Bug Fixes 33 | 34 | * undefined case in instrument-aws-clients rule ([#21](https://github.com/Stedi/eslint-plugin-stedi-aws-rules/issues/21)) ([fd2591a](https://github.com/Stedi/eslint-plugin-stedi-aws-rules/commit/fd2591ae4c67f72d059a6af3f29e37fac38436e4)) 35 | 36 | # [1.3.0](https://github.com/Stedi/eslint-plugin-stedi-aws-rules/compare/v1.2.0...v1.3.0) (2020-10-29) 37 | 38 | 39 | ### Features 40 | 41 | * extend instrumented-document-clients rule, add tests ([#19](https://github.com/Stedi/eslint-plugin-stedi-aws-rules/issues/19)) ([abb165a](https://github.com/Stedi/eslint-plugin-stedi-aws-rules/commit/abb165a965676a45ba48986d823864abb0d20c38)) 42 | 43 | # [1.2.0](https://github.com/Stedi/eslint-plugin-stedi-aws-rules/compare/v1.1.2...v1.2.0) (2020-10-22) 44 | 45 | 46 | ### Features 47 | 48 | * extend instrumented-aws-clients rule to support more cases ([#16](https://github.com/Stedi/eslint-plugin-stedi-aws-rules/issues/16)) ([0599729](https://github.com/Stedi/eslint-plugin-stedi-aws-rules/commit/0599729ddb0b7bd6c46d7a262432a64af4b4e24a)) 49 | 50 | ## [1.1.2](https://github.com/Stedi/eslint-plugin-stedi-aws-rules/compare/v1.1.1...v1.1.2) (2020-10-09) 51 | 52 | 53 | ### Bug Fixes 54 | 55 | * add access=public flag to npm's publishConfig ([b3c15d7](https://github.com/Stedi/eslint-plugin-stedi-aws-rules/commit/b3c15d7cca19f6c362e909a6d4f7552eb004b195)) 56 | 57 | ## [1.1.1](https://github.com/Stedi/eslint-plugin-stedi-aws-rules/compare/v1.1.0...v1.1.1) (2020-10-09) 58 | 59 | 60 | ### Bug Fixes 61 | 62 | * point publishConfig to npmjs ([d5aee29](https://github.com/Stedi/eslint-plugin-stedi-aws-rules/commit/d5aee291f6fb21d9ef9492e551bd31df5f3a49c8)) 63 | * remove publishConfig.registry from package.json in order to publish to NPM ([49735b0](https://github.com/Stedi/eslint-plugin-stedi-aws-rules/commit/49735b0bdf86112e17302562dcf45d22caa6e268)) 64 | * update secret reference ([6f820e0](https://github.com/Stedi/eslint-plugin-stedi-aws-rules/commit/6f820e0ad839780339ec038889f00a8e3bdf9b2d)) 65 | 66 | # [1.1.0](https://github.com/Stedi/eslint-plugin-stedi-aws-rules/compare/v1.0.0...v1.1.0) (2020-10-08) 67 | 68 | 69 | ### Features 70 | 71 | * publish to npmjs ([1c1c611](https://github.com/Stedi/eslint-plugin-stedi-aws-rules/commit/1c1c6111716eb9c7479f32b05ea301b452945d69)) 72 | 73 | # 1.0.0 (2020-10-03) 74 | 75 | 76 | ### Bug Fixes 77 | 78 | * **build:** add .releaserc.json ([074e47d](https://github.com/Stedi/eslint-plugin-stedi-aws-rules/commit/074e47da97f25e232046fa6a5d6bd04ddbf8bdf0)) 79 | * **release:** add .npmrc ([bfab475](https://github.com/Stedi/eslint-plugin-stedi-aws-rules/commit/bfab4758f0bb370d84ea4bdb26c2cb86c2ee97c0)) 80 | * **release:** copy ENG_GITHUB_TOKEN to GITHUB_TOKEN ([1677469](https://github.com/Stedi/eslint-plugin-stedi-aws-rules/commit/1677469239dc7abf6b594cfdc19fb05c87bcd2bb)) 81 | * links to supported rules ([df8ce29](https://github.com/Stedi/eslint-plugin-stedi-aws-rules/commit/df8ce29807ab77bed2ab047f5bd238574e9e9519)) 82 | * prefix package name with stedi scope ([ba37b84](https://github.com/Stedi/eslint-plugin-stedi-aws-rules/commit/ba37b8452e3b79e31cc0518dcdde8ef711faa742)) 83 | * release action syntax ([0b99cb8](https://github.com/Stedi/eslint-plugin-stedi-aws-rules/commit/0b99cb8006c6125312823c10652fb48a99fbf5ad)) 84 | 85 | 86 | ### Features 87 | 88 | * add gpr-publish workflow ([e9b5c65](https://github.com/Stedi/eslint-plugin-stedi-aws-rules/commit/e9b5c658ac261821925e195b674cfc75d2e1842d)) 89 | * add linting ([c114a69](https://github.com/Stedi/eslint-plugin-stedi-aws-rules/commit/c114a6969fbf2e377675a2b78cb908f5c5cc507b)) 90 | * add release action ([55056d7](https://github.com/Stedi/eslint-plugin-stedi-aws-rules/commit/55056d7b5f3f8d5bc98285a84ee9a49ffc5fba99)) 91 | * initial commit ([e43c318](https://github.com/Stedi/eslint-plugin-stedi-aws-rules/commit/e43c31836775e60b14666bae95a73670e2879211)) 92 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # eslint-plugin-stedi-aws-rules 2 | 3 | Best practices around using AWS SDK in Javascript & Typescript projects. 4 | 5 | [![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release) 6 | 7 | References: 8 | 9 | - https://pages.awscloud.com/rs/112-TZM-766/images/2020_0316-SRV_Slide-Deck.pdf 10 | 11 | ## Installation 12 | 13 | You'll first need to install [ESLint](http://eslint.org): 14 | 15 | ``` 16 | $ npm i eslint --save-dev 17 | ``` 18 | 19 | Next, install `@stedi-oss/eslint-plugin-stedi-aws-rules`: 20 | 21 | ``` 22 | npm install @stedi-oss/eslint-plugin-stedi-aws-rules --save-dev 23 | ``` 24 | 25 | ## Usage 26 | 27 | Add `stedi` to the plugins section of your `.eslintrc` configuration file. You can omit the `eslint-plugin-` prefix: 28 | 29 | ```json 30 | { 31 | "plugins": ["@stedi-oss/stedi-aws-rules"] 32 | } 33 | ``` 34 | 35 | Then configure the rules you want to use under the rules section. 36 | 37 | ```json 38 | { 39 | "rules": { 40 | "@stedi-oss/stedi-aws-rules/no-aws-import": "error", 41 | "@stedi-oss/stedi-aws-rules/instrument-aws-clients": "error", 42 | "@stedi-oss/stedi-aws-rules/instrument-document-clients": "error" 43 | } 44 | } 45 | ``` 46 | 47 | ## Supported Rules 48 | 49 | - [@stedi-oss/stedi-aws-rules/no-aws-import](docs/rules/no-aws-import.md) 50 | - [@stedi-oss/stedi-aws-rules/instrument-aws-clients](docs/rules/instrument-aws-clients.md) 51 | - [@stedi-oss/stedi-aws-rules/instrument-document-clients](docs/rules/instrument-document-clients.md) 52 | - [@stedi-oss/stedi-aws-rules/no-direct-lambda-invoke.md](docs/rules/no-direct-lambda-invoke.md) 53 | 54 | ## Contributing 55 | 56 | As Stedi uses this for own projects, we know this might not be the perfect approach for all the projects out there. If you have any ideas, just open an issue and tell us you think. 57 | 58 | If you'd like to contribute, please fork the repository and make changes as you'd like. Pull requests are warmly welcome. 59 | 60 | ### Adding a new rule 61 | 62 | To follow our structure, when bootstraping new rule please use [generator-eslint](https://github.com/eslint/generator-eslint) to do so. 63 | 64 | Make sure you're in the top-level directory of this repo and type: 65 | 66 | ```sh 67 | $ yo eslint:rule 68 | ``` 69 | 70 | ## Releases 71 | 72 | New releases are generated with each commit to the master using [semantic-release](https://github.com/semantic-release/semantic-release) 73 | 74 | ## License 75 | 76 | [MIT License](https://opensource.org/licenses/MIT) © [Stedi](https://stedi.com) 77 | -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { extends: ["@commitlint/config-conventional"] } 2 | -------------------------------------------------------------------------------- /docs/rules/instrument-aws-clients.md: -------------------------------------------------------------------------------- 1 | # Always instrument AWS SDK code with X-Ray (instrument-aws-clients) 2 | 3 | Please describe the origin of the rule here. 4 | 5 | ## Rule Details 6 | 7 | This rule aims to make sure all your AWS SDK client instances are instrumented using AWS X-Ray. This allows you to investigate which parts of your application are bottlenecks. 8 | 9 | Examples of **incorrect** code for this rule: 10 | 11 | ```js 12 | import SecretsManager from "aws-sdk/clients/secretsmanager" 13 | 14 | const ssm = new SecretsManager() 15 | ``` 16 | 17 | Examples of **correct** code for this rule: 18 | 19 | ```js 20 | import SecretsManager from "aws-sdk/clients/secretsmanager" 21 | 22 | const ssm = new SecretsManager() 23 | AWSXRay.captureAWSClient(ssm) 24 | ``` 25 | 26 | ## When Not To Use It 27 | 28 | Turn off this rule if you're using other instrumentation tools. 29 | 30 | ## Further Reading 31 | 32 | - [https://aws.amazon.com/xray/](https://aws.amazon.com/xray/) 33 | - [https://docs.aws.amazon.com/xray-sdk-for-nodejs/latest/reference/index.html](https://docs.aws.amazon.com/xray-sdk-for-nodejs/latest/reference/index.html) 34 | - [https://github.com/aws/aws-xray-sdk-node](https://github.com/aws/aws-xray-sdk-node) 35 | - [https://www.youtube.com/watch?v=JBOo2L4sqt8](https://www.youtube.com/watch?v=JBOo2L4sqt8) 36 | -------------------------------------------------------------------------------- /docs/rules/instrument-document-clients.md: -------------------------------------------------------------------------------- 1 | # Always instrument DynamoDB.DocumentClient code with X-Ray (instrument-document-clients) 2 | 3 | `DocumentClient` is a special case in AWS SDK. It has to be treated differently than the rest of AWS SDK services. 4 | 5 | ## Rule Details 6 | 7 | This rule aims to make sure all your AWS DynamoDB DocumentClient instances are instrumented using AWS X-Ray. This allows you to investigate which calls to DynamoDB are bottlenecks. 8 | 9 | Examples of **incorrect** code for this rule: 10 | 11 | ```js 12 | import DynamoDB from "aws-sdk/clients/dynamodb" 13 | 14 | const dynamoClient = new DynamoDB.DocumentClient() 15 | ``` 16 | 17 | Examples of **correct** code for this rule: 18 | 19 | ```js 20 | import DynamoDB from "aws-sdk/clients/dynamodb" 21 | 22 | const dynamoClient = new DynamoDB.DocumentClient() 23 | AWSXRay.captureAWSClient(dynamoClient.service) 24 | ``` 25 | 26 | ## When Not To Use It 27 | 28 | Turn off this rule if you're using other instrumentation tools. 29 | 30 | ## Further Reading 31 | 32 | - [https://aws.amazon.com/xray/](https://aws.amazon.com/xray/) 33 | - [https://docs.aws.amazon.com/xray-sdk-for-nodejs/latest/reference/index.html](https://docs.aws.amazon.com/xray-sdk-for-nodejs/latest/reference/index.html) 34 | - [https://github.com/aws/aws-xray-sdk-node](https://github.com/aws/aws-xray-sdk-node) 35 | - [https://www.youtube.com/watch?v=JBOo2L4sqt8](https://www.youtube.com/watch?v=JBOo2L4sqt8) 36 | -------------------------------------------------------------------------------- /docs/rules/no-aws-import.md: -------------------------------------------------------------------------------- 1 | # Don't import AWS directly, use clients instead. (no-aws-import) 2 | 3 | ## Rule Details 4 | 5 | This rule aims to reduce cold starts of Lambda functions by reducing the bundle size by only including relevant pieces of AWS SDK. 6 | 7 | Examples of **incorrect** code for this rule: 8 | 9 | ```js 10 | import { DynamoDB } from "aws-sdk" 11 | ``` 12 | 13 | Examples of **correct** code for this rule: 14 | 15 | ```js 16 | import DynamoDB from "aws-sdk/clients/dynamodb" 17 | ``` 18 | 19 | ### Options 20 | 21 | None. 22 | 23 | ## When Not To Use It 24 | 25 | Give a short description of when it would be appropriate to turn off this rule. 26 | 27 | ## Further Reading 28 | 29 | - [https://pages.awscloud.com/rs/112-TZM-766/images/2020_0316-SRV_Slide-Deck.pdf](https://pages.awscloud.com/rs/112-TZM-766/images/2020_0316-SRV_Slide-Deck.pdf) 30 | - [https://theburningmonk.com/2019/03/just-how-expensive-is-the-full-aws-sdk/](https://theburningmonk.com/2019/03/just-how-expensive-is-the-full-aws-sdk/) 31 | -------------------------------------------------------------------------------- /docs/rules/no-direct-lambda-invoke.md: -------------------------------------------------------------------------------- 1 | # Do not invoke Lambdas directly (no-direct-lambda-invoke) 2 | 3 | ## Rule Details 4 | 5 | In most cases invoking Lambda functions directly inside Lambda functions can be considered as an antipattern. 6 | 7 | Examples of **incorrect** code for this rule: 8 | 9 | ```js 10 | new AWS.Lambda().invoke({ ... }); 11 | 12 | new AWS.Lambda().invokeAsync({ ... }); 13 | ``` 14 | 15 | Examples of **correct** code for this rule: 16 | 17 | ```js 18 | // no such code at all 19 | ``` 20 | 21 | ## When Not To Use It 22 | 23 | Turn off this rule if you're using other instrumentation tools. 24 | 25 | ## Further Reading 26 | 27 | - [https://twitter.com/benjamin_l_s/status/1310571746559496198](https://twitter.com/benjamin_l_s/status/1310571746559496198) 28 | - [https://twitter.com/brianleroux/status/1309864339889741824](https://twitter.com/brianleroux/status/1309864339889741824) 29 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview Stedi coding style 3 | * @author Rafal Wilinski 4 | */ 5 | "use strict"; 6 | 7 | //------------------------------------------------------------------------------ 8 | // Requirements 9 | //------------------------------------------------------------------------------ 10 | 11 | var requireIndex = require("requireindex"); 12 | 13 | //------------------------------------------------------------------------------ 14 | // Plugin Definition 15 | //------------------------------------------------------------------------------ 16 | 17 | 18 | // import all rules in lib/rules 19 | module.exports.rules = requireIndex(__dirname + "/rules"); 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /lib/rules/instrument-aws-clients.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview Ensure AWS services clients instances are instrumented. 3 | * @author Tyler van Hensbergen 4 | */ 5 | "use strict" 6 | 7 | //------------------------------------------------------------------------------ 8 | // Rule Definition 9 | //------------------------------------------------------------------------------ 10 | 11 | module.exports = { 12 | meta: { 13 | type: "suggestion", 14 | docs: { 15 | description: "Ensure AWS services clients instances are instrumented.", 16 | category: "Tracing", 17 | recommended: true, 18 | }, 19 | messages: { 20 | clientNotInstrumented: "AWS client not instrumented: {{client}}", 21 | }, 22 | 23 | fixable: null, // or "code" or "whitespace" 24 | schema: [ 25 | // fill in your schema 26 | ], 27 | }, 28 | 29 | create: function (context) { 30 | const code = context.getSourceCode() 31 | let awsClients 32 | let awsClientInstances 33 | 34 | /** 35 | * Reports an AST node as a rule violation. 36 | * @param {ASTNode} node The node to report. 37 | * @param {ASTNode|undefined} data The service client instance to report, defaults to `node` 38 | * @returns {void} 39 | */ 40 | function report(node, data) { 41 | data = data || node 42 | context.report({ 43 | node, 44 | messageId: "clientNotInstrumented", 45 | data: { client: code.getText(data) }, 46 | }) 47 | } 48 | 49 | /** 50 | * Returns a variable for the current scope if exists. 51 | * @param {string} name The node to report. 52 | * @returns {Variable|undefined} 53 | */ 54 | function varInScope(name) { 55 | return context.getScope().set.get(name) 56 | } 57 | 58 | return { 59 | Program: () => { 60 | awsClients = new Set() 61 | awsClientInstances = new Map() 62 | }, 63 | 64 | ImportDeclaration: (node) => { 65 | if (node.source.value.startsWith("aws-sdk/clients/")) { 66 | node.specifiers.map((o) => awsClients.add(o.local.name)) 67 | } 68 | }, 69 | 70 | "CallExpression[callee.property.name!='captureAWSClient'][callee.object.type='NewExpression'][callee.object.callee]": ( 71 | node, 72 | ) => { 73 | const name = node.callee.object.callee.name 74 | if (name && awsClients.has(name)) { 75 | report(node) 76 | } 77 | }, 78 | 79 | "VariableDeclarator[init.callee]:has(NewExpression)": (node) => { 80 | const name = node.init.callee.name 81 | if (name && awsClients.has(name)) { 82 | const scopeVar = varInScope(node.id.name) 83 | awsClientInstances.set(scopeVar, node) 84 | } 85 | }, 86 | 87 | "Program:exit": () => { 88 | awsClientInstances.forEach((node) => report(node)) 89 | }, 90 | 91 | "CallExpression[callee.object]": (node) => { 92 | const scopeVar = varInScope(node.callee.object.name) 93 | if (scopeVar && awsClientInstances.has(scopeVar)) { 94 | report(node, awsClientInstances.get(scopeVar)) 95 | awsClientInstances.delete(scopeVar) 96 | } 97 | }, 98 | 99 | "CallExpression[callee.property.name='captureAWSClient'][arguments.length=1][arguments.0.type=Identifier]": ( 100 | node, 101 | ) => { 102 | const scopeVar = varInScope(node.arguments[0].name) 103 | if (scopeVar && awsClientInstances.has(scopeVar)) { 104 | awsClientInstances.delete(scopeVar) 105 | } 106 | }, 107 | } 108 | }, 109 | } 110 | -------------------------------------------------------------------------------- /lib/rules/instrument-document-clients.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview Ensure AWS DynamoDB Document client instances are instrumented. 3 | * @author Tyler van Hensbergen 4 | */ 5 | "use strict" 6 | 7 | //------------------------------------------------------------------------------ 8 | // Rule Definition 9 | //------------------------------------------------------------------------------ 10 | 11 | module.exports = { 12 | meta: { 13 | type: "suggestion", 14 | docs: { 15 | description: 16 | "Ensure AWS DynamoDB Document client instances are instrumented.", 17 | category: "Tracing", 18 | recommended: true, 19 | }, 20 | messages: { 21 | notInstrumented: "Called {{name}} before it was instrumented.", 22 | instrumentService: 23 | "DocumentClient should be instrumented using `service` property.", 24 | }, 25 | fixable: null, // or "code" or "whitespace" 26 | schema: [ 27 | // fill in your schema 28 | ], 29 | }, 30 | 31 | create: function (context) { 32 | let trackedVariables = new Set() 33 | 34 | /** 35 | * Returns a variable for the current scope if exists. 36 | * @param {string} name The node to report. 37 | * @returns {Variable|undefined} 38 | */ 39 | function varInScope(name) { 40 | return context.getScope().set.get(name) 41 | } 42 | 43 | /** 44 | * Checks whether a variable is being tracked 45 | * @param {string} name Variable name 46 | * @returns {Boolean} 47 | */ 48 | function isTracked(name) { 49 | return trackedVariables.has(varInScope(name)) 50 | } 51 | 52 | function reportInstrumentService(node) { 53 | context.report({ 54 | node, 55 | messageId: "instrumentService", 56 | }) 57 | } 58 | 59 | function checkMemberExpression(node) { 60 | const noProperty = !node.property 61 | const wrongProperty = node.property && node.property.name !== "service" 62 | 63 | if (noProperty || wrongProperty) { 64 | reportInstrumentService(node) 65 | } 66 | } 67 | 68 | function checkIdentifier(node) { 69 | if (node.name) { 70 | reportInstrumentService(node) 71 | } 72 | } 73 | 74 | return { 75 | "VariableDeclarator > NewExpression[callee.property.name='DocumentClient']": ( 76 | node, 77 | ) => { 78 | if (node.parent.id && node.parent.id.name) { 79 | trackedVariables.add(varInScope(node.parent.id.name)) 80 | } 81 | }, 82 | 83 | "CallExpression[callee.property.name='captureAWSClient']:has(NewExpression[callee.property.name='DocumentClient']) .arguments": ( 84 | node, 85 | ) => { 86 | checkMemberExpression(node) 87 | }, 88 | 89 | "CallExpression[callee.property.name='captureAWSClient'][arguments.length=1] .arguments[type!='NewExpression']": ( 90 | node, 91 | ) => { 92 | const name = node.name || node.object.name 93 | if (!isTracked(name)) return 94 | 95 | switch (node.type) { 96 | case "MemberExpression": 97 | return checkMemberExpression(node) 98 | case "Identifier": 99 | return checkIdentifier(node) 100 | } 101 | }, 102 | 103 | "CallExpression[callee.object.name]": (node) => { 104 | const name = node.callee.object.name 105 | if (!isTracked(name)) return 106 | 107 | context.report({ 108 | node, 109 | messageId: "notInstrumented", 110 | data: { name: name }, 111 | }) 112 | }, 113 | } 114 | }, 115 | } 116 | -------------------------------------------------------------------------------- /lib/rules/no-aws-import.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview Don't import AWS directly, use clients instead. 3 | * @author Tyler van Hensbergen 4 | */ 5 | "use strict" 6 | 7 | //------------------------------------------------------------------------------ 8 | // Rule Definition 9 | //------------------------------------------------------------------------------ 10 | 11 | module.exports = { 12 | meta: { 13 | type: "problem", 14 | 15 | docs: { 16 | description: "Don't import AWS directly, use clients instead.", 17 | category: "Cold-start optimization", 18 | recommended: true, 19 | }, 20 | 21 | messages: { 22 | directImport: "'{{modules}}' imported directly. Use clients instead.", 23 | }, 24 | 25 | fixable: null, // or "code" or "whitespace" 26 | schema: [ 27 | // fill in your schema 28 | ], 29 | }, 30 | 31 | create: function (context) { 32 | return { 33 | ImportDeclaration: (node) => { 34 | if (node.source.value == "aws-sdk") { 35 | const modules = node.specifiers.map((o) => o.local.name).join(", ") 36 | context.report({ 37 | node, 38 | messageId: "directImport", 39 | data: { modules }, 40 | }) 41 | } 42 | }, 43 | } 44 | }, 45 | } 46 | -------------------------------------------------------------------------------- /lib/rules/no-direct-lambda-invoke.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview Do not invoke Lambdas directly 3 | * @author Marcin Jan Puhacz 4 | */ 5 | "use strict" 6 | 7 | //------------------------------------------------------------------------------ 8 | // Rule Definition 9 | //------------------------------------------------------------------------------ 10 | 11 | module.exports = { 12 | meta: { 13 | docs: { 14 | description: "Do not invoke Lambdas directly", 15 | category: "Best practices", 16 | recommended: true, 17 | }, 18 | messages: { 19 | directInvocation: "Lambdas should not be invoked directly", 20 | }, 21 | }, 22 | 23 | create: function (context) { 24 | let trackedVariables = new Set() 25 | 26 | /** 27 | * Returns a variable for the current scope if exists. 28 | * @param {string} name The node to report. 29 | * @returns {Variable|undefined} 30 | */ 31 | function varInScope(name) { 32 | return context.getScope().set.get(name) 33 | } 34 | 35 | /** 36 | * Checks whether a variable is being tracked 37 | * @param {string} name Variable name 38 | * @returns {Boolean} 39 | */ 40 | function isTracked(name) { 41 | return trackedVariables.has(varInScope(name)) 42 | } 43 | 44 | function reportDirectInvocation(node) { 45 | context.report({ 46 | node, 47 | messageId: "directInvocation", 48 | }) 49 | } 50 | 51 | return { 52 | "VariableDeclarator > NewExpression[callee.property.name='Lambda']": ( 53 | node, 54 | ) => { 55 | if (node.parent.id && node.parent.id.name) { 56 | trackedVariables.add(varInScope(node.parent.id.name)) 57 | } 58 | }, 59 | 60 | "CallExpression[callee.property.type='Identifier'][callee.property.name=/^invoke$|^invokeAsync$/][callee.object.type='Identifier']": ( 61 | node, 62 | ) => { 63 | if (isTracked(node.callee.object.name)) reportDirectInvocation(node) 64 | }, 65 | 66 | "CallExpression[callee.property.type='Identifier'][callee.property.name=/^invoke$|^invokeAsync$/] NewExpression[callee.property.name='Lambda']": ( 67 | node, 68 | ) => { 69 | reportDirectInvocation(node) 70 | }, 71 | 72 | "CallExpression[callee.property.type='Identifier'][callee.property.name=/^invoke$|^invokeAsync$/] NewExpression[callee.name='Lambda']": ( 73 | node, 74 | ) => { 75 | reportDirectInvocation(node) 76 | }, 77 | } 78 | }, 79 | } 80 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@stedi-oss/eslint-plugin-stedi-aws-rules", 3 | "version": "0.0.1", 4 | "description": "AWS coding best practices by Stedi", 5 | "keywords": [ 6 | "eslint", 7 | "eslintplugin", 8 | "eslint-plugin", 9 | "aws", 10 | "rules" 11 | ], 12 | "author": "Stedi (https://stedi.com/)", 13 | "main": "lib/index.js", 14 | "files": [ 15 | "index.js", 16 | "lib" 17 | ], 18 | "scripts": { 19 | "test": "npx mocha tests --recursive", 20 | "lint": "npx eslint .", 21 | "release": "semantic-release" 22 | }, 23 | "dependencies": { 24 | "requireindex": "~1.2.0" 25 | }, 26 | "peerDependencies": { 27 | "eslint": ">=7.0.0" 28 | }, 29 | "devDependencies": { 30 | "@commitlint/cli": "^16.2.3", 31 | "@commitlint/config-conventional": "^16.2.1", 32 | "@semantic-release/changelog": "^6.0.1", 33 | "@semantic-release/git": "^10.0.1", 34 | "eslint": "^8.13.0", 35 | "husky": "^7.0.4", 36 | "mocha": "^9.2.2", 37 | "npm-check-updates": "^12.5.9", 38 | "semantic-release": "^19.0.2" 39 | }, 40 | "license": "MIT", 41 | "publishConfig": { 42 | "registry": "https://registry.npmjs.org/", 43 | "access": "public" 44 | }, 45 | "config": { 46 | "commitizen": { 47 | "path": "./node_modules/cz-conventional-changelog" 48 | } 49 | }, 50 | "husky": { 51 | "hooks": { 52 | "commit-msg": "commitlint -E HUSKY_GIT_PARAMS" 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /tests/lib/rules/instrument-aws-clients.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview Always instrument AWS SDK code with X-Ray 3 | * @author Tyler van Hensbergen 4 | */ 5 | "use strict" 6 | 7 | //------------------------------------------------------------------------------ 8 | // Requirements 9 | //------------------------------------------------------------------------------ 10 | 11 | var rule = require("../../../lib/rules/instrument-aws-clients"), 12 | RuleTester = require("eslint").RuleTester 13 | 14 | //------------------------------------------------------------------------------ 15 | // Tests 16 | //------------------------------------------------------------------------------ 17 | 18 | var ruleTester = new RuleTester({ 19 | parserOptions: { 20 | ecmaVersion: 12, 21 | sourceType: "module", 22 | }, 23 | }) 24 | 25 | ruleTester.run("instrument-aws-clients", rule, { 26 | valid: [ 27 | "import SecretsManager from 'aws-sdk/clients/secretsmanager'\n" + 28 | "const ssm = new SecretsManager()\n" + 29 | "AWSXRay.captureAWSClient(ssm)", 30 | 31 | "import { SecretsManager, DynamoDB } from 'aws-sdk/clients/secretsmanager'\n" + 32 | "let ddb = new DynamoDB()\n" + 33 | "AWSXRay.captureAWSClient(ddb)\n" + 34 | "AWSXRay.captureAWSClient(new SecretsManager())", 35 | 36 | "import S3 from 'aws-sdk/clients/s3'\n" + 37 | "async function fn() { await AWSXRay.captureAWSClient(new S3()).getObject({}) }", 38 | ], 39 | 40 | invalid: [ 41 | { 42 | code: 43 | "import SecretsManager from 'aws-sdk/clients/secretsmanager'\n" + 44 | "const sm = new SecretsManager()", 45 | errors: [ 46 | { 47 | messageId: "clientNotInstrumented", 48 | data: { client: "sm = new SecretsManager()" }, 49 | }, 50 | ], 51 | }, 52 | 53 | { 54 | code: 55 | "import S3 from 'aws-sdk/clients/s3'\n" + 56 | "async function fn() { await new S3().getObject({}) }", 57 | errors: [ 58 | { 59 | messageId: "clientNotInstrumented", 60 | data: { client: "new S3().getObject({})" }, 61 | }, 62 | ], 63 | }, 64 | 65 | { 66 | code: 67 | "import SecretsManager from 'aws-sdk/clients/secretsmanager'\n" + 68 | "const sm = new SecretsManager().cancelRotateSecret()", 69 | errors: [ 70 | { 71 | messageId: "clientNotInstrumented", 72 | data: { client: "new SecretsManager().cancelRotateSecret()" }, 73 | }, 74 | ], 75 | }, 76 | 77 | { 78 | code: 79 | "import SecretsManager from 'aws-sdk/clients/secretsmanager'\n" + 80 | "var ssm = new SecretsManager()\n" + 81 | "function test() {\n" + 82 | " const ssm = new SecretsManager()\n" + 83 | " AWSXRay.captureAWSClient(ssm)\n" + 84 | "}\n" + 85 | "ssm.cancelRotateSecret()", 86 | errors: [ 87 | { 88 | messageId: "clientNotInstrumented", 89 | data: { client: "ssm = new SecretsManager()" }, 90 | }, 91 | ], 92 | }, 93 | 94 | { 95 | code: 96 | "import SecretsManager from 'aws-sdk/clients/secretsmanager'\n" + 97 | "const ssm = new SecretsManager()\n" + 98 | "ssm.cancelRotateSecret()\n" + 99 | "AWSXRay.captureAWSClient(ssm)", 100 | errors: [ 101 | { 102 | messageId: "clientNotInstrumented", 103 | data: { client: "ssm = new SecretsManager()" }, 104 | }, 105 | ], 106 | }, 107 | ], 108 | }) 109 | -------------------------------------------------------------------------------- /tests/lib/rules/instrument-document-clients.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview Always instrument DynamoDB.DocumentClient code with X-Ray 3 | * @author Tyler van Hensbergen 4 | */ 5 | "use strict" 6 | 7 | //------------------------------------------------------------------------------ 8 | // Requirements 9 | //------------------------------------------------------------------------------ 10 | 11 | var rule = require("../../../lib/rules/instrument-document-clients"), 12 | RuleTester = require("eslint").RuleTester 13 | 14 | //------------------------------------------------------------------------------ 15 | // Tests 16 | //------------------------------------------------------------------------------ 17 | 18 | var ruleTester = new RuleTester({ 19 | parserOptions: { 20 | ecmaVersion: 12, 21 | sourceType: "module", 22 | }, 23 | }) 24 | ruleTester.run("instrument-document-clients", rule, { 25 | valid: [ 26 | "const dynamoClient = new DynamoDB.DocumentClient()\n" + 27 | "let ddb = AWSXRay.captureAWSClient(dynamoClient.service)\n" + 28 | "ddb.scan()", 29 | "let ddb = AWSXRay.captureAWSClient((new DynamoDB.DocumentClient()).service)\n" + 30 | "ddb.scan()", 31 | ], 32 | 33 | invalid: [ 34 | { 35 | code: 36 | "const dynamoClient = new DynamoDB.DocumentClient()\n" + 37 | "let ddb = AWSXRay.captureAWSClient(dynamoClient.wrong)\n" + 38 | "ddb.scan()", 39 | errors: [{ messageId: "instrumentService" }], 40 | }, 41 | { 42 | code: 43 | "const dynamoClient = new DynamoDB.DocumentClient()\n" + 44 | "let ddb = AWSXRay.captureAWSClient(dynamoClient)\n" + 45 | "ddb.scan()", 46 | errors: [{ messageId: "instrumentService" }], 47 | }, 48 | { 49 | code: "const ddb = new DynamoDB.DocumentClient(); ddb.scan()", 50 | errors: [{ messageId: "notInstrumented", data: { name: "ddb" } }], 51 | }, 52 | { 53 | code: 54 | "const ddb = AWSXRay.captureAWSClient(new DynamoDB.DocumentClient()); ddb.scan()", 55 | errors: [{ messageId: "instrumentService" }], 56 | }, 57 | ], 58 | }) 59 | -------------------------------------------------------------------------------- /tests/lib/rules/no-aws-import.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview Don't import AWS directly, use clients instead. 3 | * @author Tyler van Hensbergen 4 | */ 5 | "use strict" 6 | 7 | //------------------------------------------------------------------------------ 8 | // Requirements 9 | //------------------------------------------------------------------------------ 10 | 11 | var rule = require("../../../lib/rules/no-aws-import"), 12 | RuleTester = require("eslint").RuleTester 13 | 14 | //------------------------------------------------------------------------------ 15 | // Tests 16 | //------------------------------------------------------------------------------ 17 | 18 | var ruleTester = new RuleTester({ 19 | parserOptions: { 20 | ecmaVersion: 12, 21 | sourceType: "module", 22 | }, 23 | }) 24 | ruleTester.run("no-aws-import", rule, { 25 | valid: [ 26 | { 27 | code: "import { DynamoDB } from 'aws-sdk/clients/dynamodb'", 28 | }, 29 | { 30 | code: "const DynamoDB = require('aws-sdk/clients/dynamodb')", 31 | }, 32 | ], 33 | 34 | invalid: [ 35 | { 36 | code: 'import { S3 } from "aws-sdk"', 37 | errors: [{ messageId: "directImport", data: { modules: "S3" } }], 38 | }, 39 | { 40 | code: "import { S3, DynamoDB } from 'aws-sdk'", 41 | errors: [ 42 | { messageId: "directImport", data: { modules: "S3, DynamoDB" } }, 43 | ], 44 | }, 45 | ], 46 | }) 47 | -------------------------------------------------------------------------------- /tests/lib/rules/no-direct-lambda-invoke.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview Do not invoke Lambdas directly 3 | * @author Marcin Jan Puhacz 4 | */ 5 | "use strict" 6 | 7 | //------------------------------------------------------------------------------ 8 | // Requirements 9 | //------------------------------------------------------------------------------ 10 | 11 | var rule = require("../../../lib/rules/no-direct-lambda-invoke"), 12 | RuleTester = require("eslint").RuleTester 13 | 14 | //------------------------------------------------------------------------------ 15 | // Tests 16 | //------------------------------------------------------------------------------ 17 | 18 | var ruleTester = new RuleTester({ 19 | parserOptions: { 20 | ecmaVersion: 12, 21 | sourceType: "module", 22 | }, 23 | }) 24 | ruleTester.run("no-direct-lambda-invoke", rule, { 25 | valid: [ 26 | "new SomeClass().invoke({})", 27 | "let obj = new SomeClass(); obj.invoke()", 28 | "let obj = new SomeClass(); obj.invokeAsync()", 29 | ], 30 | 31 | invalid: [ 32 | { 33 | code: "new Lambda().invoke({});", 34 | errors: [{ messageId: "directInvocation" }], 35 | }, 36 | { 37 | code: "new AWS.Lambda().invoke({});", 38 | errors: [{ messageId: "directInvocation" }], 39 | }, 40 | { 41 | code: "let lbd = new AWS.Lambda(); lbd.invoke()", 42 | errors: [{ messageId: "directInvocation" }], 43 | }, 44 | { 45 | code: "let lbd = new AWS.Lambda(); lbd.invokeAsync()", 46 | errors: [{ messageId: "directInvocation" }], 47 | }, 48 | ], 49 | }) 50 | --------------------------------------------------------------------------------