├── .eslintrc.js ├── .github ├── dependabot.yml └── workflows │ ├── auto-approve.yml │ └── nodejs.yml ├── .gitignore ├── .kodiak.toml ├── .prettierrc.json ├── .releaserc ├── .tool-versions ├── CHANGELOG.md ├── LICENSE ├── README.md ├── commitlint.config.js ├── example-service ├── handler.js ├── package.json ├── serverless.yml └── yarn.lock ├── jest.config.js ├── lib ├── __fixtures__ │ └── handler.js ├── __snapshots__ │ └── serverless-sns-sqs-lambda.test.ts.snap ├── index.js ├── modules.d.ts ├── serverless-sns-sqs-lambda.test.ts └── serverless-sns-sqs-lambda.ts ├── package.json ├── scripts └── plant.sh ├── tsconfig.json └── yarn.lock /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | parser: "@typescript-eslint/parser", 3 | plugins: ["@typescript-eslint"], 4 | extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended"], 5 | env: { 6 | commonjs: true, 7 | browser: true, 8 | jest: true, 9 | es2020: true 10 | }, 11 | parserOptions: { 12 | ecmaVersion: 11 13 | } 14 | }; 15 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: npm 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | time: "19:00" 8 | open-pull-requests-limit: 10 9 | labels: 10 | - "dependencies" 11 | - "automerge" # Used by Kodiak to work out what to automerge 12 | -------------------------------------------------------------------------------- /.github/workflows/auto-approve.yml: -------------------------------------------------------------------------------- 1 | name: auto-merge 2 | 3 | on: 4 | pull_request_target: 5 | 6 | jobs: 7 | auto-merge: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v2 11 | - uses: ahmadnassri/action-dependabot-auto-merge@v2 12 | with: 13 | target: minor 14 | github-token: ${{ secrets.GH_TOKEN }} 15 | -------------------------------------------------------------------------------- /.github/workflows/nodejs.yml: -------------------------------------------------------------------------------- 1 | # This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions 3 | 4 | name: Node.js CI 5 | 6 | on: 7 | push: 8 | branches: ["**"] 9 | pull_request: 10 | branches: [master] 11 | 12 | jobs: 13 | build: 14 | runs-on: ubuntu-latest 15 | # Don't build if Semantic Release pushes a ci skip commit 16 | if: "!contains(github.event.head_commit.message, '[ci skip]')" 17 | 18 | strategy: 19 | matrix: 20 | node-version: [14.x, 16.x, 18.x] 21 | 22 | steps: 23 | - uses: actions/checkout@v2 24 | with: 25 | # Prevents interference with release credentials 26 | # See https://github.com/semantic-release/semantic-release/blob/master/docs/recipes/github-actions.md#pushing-packagejson-changes-to-a-master-branch 27 | persist-credentials: false 28 | - name: Use Node.js ${{ matrix.node-version }} 29 | uses: actions/setup-node@v1 30 | with: 31 | node-version: ${{ matrix.node-version }} 32 | # Skip post-install scripts here, as a malicious 33 | # script could steal NODE_AUTH_TOKEN. 34 | - name: Install dependencies 35 | run: yarn install --frozen-lockfile 36 | - name: Build 37 | run: yarn build 38 | - name: Type Coverage 39 | run: yarn type-coverage 40 | - name: Test 41 | run: yarn test --coverage 42 | - name: Check formatting 43 | run: yarn format-check 44 | - name: Package example service to ensure compatibility 45 | run: cd example-service && yarn install --frozen-lockfile && yarn sls package 46 | - name: Run Semantic Release 47 | env: 48 | # TODO: While not ideal to have to use a developer's token to push to master here, 49 | # it is currently the only way to do it with GitHub actions at the moment if master 50 | # is a protected branch. 51 | # Secrets are not exposed in PR builds for security reasons, but semantic release will 52 | # never have to push or release anything in that case so its fine. 53 | # Not the same as GITHUB_TOKEN 54 | # This token authenticates on behalf of the maintainer, not github actions itself to allow changes to be pushed 55 | GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} 56 | GH_TOKEN: ${{ secrets.GH_TOKEN }} 57 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 58 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 59 | run: yarn semantic-release 60 | if: matrix.node-version == '14.x' 61 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # package directories 2 | jspm_packages 3 | 4 | # Serverless directories 5 | **/.serverless 6 | node_modules 7 | /.idea 8 | 9 | # VS Code 10 | .vscode 11 | *.code-workspace 12 | .history 13 | 14 | # tsc output 15 | dist 16 | 17 | #misc 18 | yarn-error.log 19 | *.tgz 20 | 21 | # workflow specific files 22 | .envrc 23 | 24 | # Jest output 25 | coverage 26 | -------------------------------------------------------------------------------- /.kodiak.toml: -------------------------------------------------------------------------------- 1 | # .kodiak.toml 2 | # Minimal config. version is the only required field. 3 | version = 1 4 | 5 | [merge] 6 | delete_branch_on_merge = true 7 | 8 | [merge.automerge_dependencies] 9 | # only auto merge "minor" and "patch" version upgrades. 10 | # do not automerge "major" version upgrades. 11 | versions = ["minor", "patch"] 12 | usernames = ["dependabot"] 13 | 14 | [approve] 15 | auto_approve_usernames = ["dependabot"] 16 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "none", 3 | "arrowParens": "avoid" 4 | } 5 | -------------------------------------------------------------------------------- /.releaserc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@jedmao/semantic-release-npm-github-config", 3 | "branch": "master" 4 | } -------------------------------------------------------------------------------- /.tool-versions: -------------------------------------------------------------------------------- 1 | nodejs 14.19.0 2 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. See 4 | [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | # [2.1.0](https://github.com/agiledigital/serverless-sns-sqs-lambda/compare/v2.0.1...v2.1.0) (2023-05-24) 7 | 8 | 9 | ### Features 10 | 11 | * empty commit to force release of [#711](https://github.com/agiledigital/serverless-sns-sqs-lambda/issues/711) ([b090fa7](https://github.com/agiledigital/serverless-sns-sqs-lambda/commit/b090fa731aa9add47154634c3041df9ff1f96179)) 12 | 13 | ## [2.0.1](https://github.com/agiledigital/serverless-sns-sqs-lambda/compare/v2.0.0...v2.0.1) (2022-05-24) 14 | 15 | 16 | ### Bug Fixes 17 | 18 | * add 'prefix' to config schema (fixes [#562](https://github.com/agiledigital/serverless-sns-sqs-lambda/issues/562)) ([4602a63](https://github.com/agiledigital/serverless-sns-sqs-lambda/commit/4602a63e5b6e726f8931f3c3a3239117d25fe266)) 19 | 20 | # [2.0.0](https://github.com/agiledigital/serverless-sns-sqs-lambda/compare/v1.0.1...v2.0.0) (2022-05-13) 21 | 22 | 23 | ### Bug Fixes 24 | 25 | * fix case sensitivity issue ([42e9675](https://github.com/agiledigital/serverless-sns-sqs-lambda/commit/42e967520705b0d36fbf8b7b8030d40985e34a3b)) 26 | * improve the handling of encrypted SQS queues (fixes [#555](https://github.com/agiledigital/serverless-sns-sqs-lambda/issues/555)) ([789ea78](https://github.com/agiledigital/serverless-sns-sqs-lambda/commit/789ea786e599aefd8df8d51f4cf8ca70f74810a4)) 27 | 28 | 29 | ### Features 30 | 31 | * upgrade to serverless v3 ([#540](https://github.com/agiledigital/serverless-sns-sqs-lambda/issues/540)) ([cf842f0](https://github.com/agiledigital/serverless-sns-sqs-lambda/commit/cf842f0b461594ea03df0dfee3ff7909d6e2c04b)) 32 | 33 | 34 | ### BREAKING CHANGES 35 | 36 | * If you have implemented workarounds to allow the lambda to subscribe to an encrypted SQS queue, you may get conflicts as the policy to allow the decryption is now added automatically 37 | 38 | - If you provide an key ID, key ARN or reference to a key ARN to the `kmsMasterKeyId` attribute, the relevant 'kms:Decrypt' policy statement should be added automatically to allow the subscription to work correctly 39 | * serverless v2 is no longer supported. It might still work, but bug fixes/new features will mostly be developed for and tested with serverless v3 (important security/bug fixes _may_ be back ported to v2 versions depending on the uptake of v3) 40 | 41 | serverless v3 is now a peer dependency, you will get warnings if you are on earlier versions 42 | 43 | ## [1.0.1](https://github.com/agiledigital/serverless-sns-sqs-lambda/compare/v1.0.0...v1.0.1) (2022-05-13) 44 | 45 | 46 | ### Bug Fixes 47 | 48 | * allow physical ID to be omitted ([#444](https://github.com/agiledigital/serverless-sns-sqs-lambda/issues/444)) ([b22944f](https://github.com/agiledigital/serverless-sns-sqs-lambda/commit/b22944f5094a544fe7b4f1b0358d21967668b6e2)) 49 | * ensure name uniqueness ([#444](https://github.com/agiledigital/serverless-sns-sqs-lambda/issues/444)) ([83617d6](https://github.com/agiledigital/serverless-sns-sqs-lambda/commit/83617d6b98164cf14b14944a3a94e9dec906267e)) 50 | 51 | # [1.0.0](https://github.com/agiledigital/serverless-sns-sqs-lambda/compare/v0.9.1...v1.0.0) (2022-01-07) 52 | 53 | 54 | ### Bug Fixes 55 | 56 | * add another source of stage ([e9c1cb1](https://github.com/agiledigital/serverless-sns-sqs-lambda/commit/e9c1cb185706dca8ffcaf8b5b1a82b80512215c1)), closes [#432](https://github.com/agiledigital/serverless-sns-sqs-lambda/issues/432) 57 | 58 | 59 | ### Features 60 | 61 | * ensure that work done for issue [#432](https://github.com/agiledigital/serverless-sns-sqs-lambda/issues/432) is published as a new major version ([6433354](https://github.com/agiledigital/serverless-sns-sqs-lambda/commit/643335489b6488d8beb4ed2755c60623b16cabe1)) 62 | 63 | 64 | ### BREAKING CHANGES 65 | 66 | * the way that stage is determined has changed which might cause the generated CF template to change, causing issues on the next deploy 67 | 68 | ## [0.9.1](https://github.com/agiledigital/serverless-sns-sqs-lambda/compare/v0.9.0...v0.9.1) (2021-10-23) 69 | 70 | 71 | ### Bug Fixes 72 | 73 | * remove unneccessary 'DependsOn' block ([9276523](https://github.com/agiledigital/serverless-sns-sqs-lambda/commit/9276523d01cb510b6fd2f685f121b1c42a86a77e)) 74 | * support custom role ARNs ([563c202](https://github.com/agiledigital/serverless-sns-sqs-lambda/commit/563c20267151efa7122e8a3f7a5df0ea49a25c37)), closes [#350](https://github.com/agiledigital/serverless-sns-sqs-lambda/issues/350) 75 | 76 | # [0.9.0](https://github.com/agiledigital/serverless-sns-sqs-lambda/compare/v0.8.0...v0.9.0) (2021-10-13) 77 | 78 | 79 | ### Features 80 | 81 | * add fifo queue support ([b1122b0](https://github.com/agiledigital/serverless-sns-sqs-lambda/commit/b1122b0bc01da6c0e84a3f83892017e37c9d88f3)) 82 | 83 | # [0.8.0](https://github.com/agiledigital/serverless-sns-sqs-lambda/compare/v0.7.0...v0.8.0) (2021-06-29) 84 | 85 | 86 | ### Features 87 | 88 | * make queue policy more specific ([6ac0c96](https://github.com/agiledigital/serverless-sns-sqs-lambda/commit/6ac0c9689765205fd6a36c48949ed34d6e0f63ec)) 89 | 90 | # [0.7.0](https://github.com/agiledigital/serverless-sns-sqs-lambda/compare/v0.6.0...v0.7.0) (2021-06-09) 91 | 92 | 93 | ### Features 94 | 95 | * add config validation schema ([a16e1ee](https://github.com/agiledigital/serverless-sns-sqs-lambda/commit/a16e1ee9b40957faf85c3d60e2bd2439e9b9be40)), closes [#58](https://github.com/agiledigital/serverless-sns-sqs-lambda/issues/58) 96 | 97 | # [0.6.0](https://github.com/agiledigital/serverless-sns-sqs-lambda/compare/v0.5.0...v0.6.0) (2021-05-14) 98 | 99 | 100 | ### Features 101 | 102 | * allow CloudFormation overrides ([6d80b18](https://github.com/agiledigital/serverless-sns-sqs-lambda/commit/6d80b18adb6b13a5624177c619f3c6d251da6aea)), closes [#213](https://github.com/agiledigital/serverless-sns-sqs-lambda/issues/213) 103 | 104 | # [0.5.0](https://github.com/agiledigital/serverless-sns-sqs-lambda/compare/v0.4.0...v0.5.0) (2021-04-29) 105 | 106 | 107 | ### Features 108 | 109 | * pass through batching window parameter ([83e2922](https://github.com/agiledigital/serverless-sns-sqs-lambda/commit/83e29228e70b351a1e42236a5d7ac32891a01543)) 110 | 111 | # [0.4.0](https://github.com/agiledigital/serverless-sns-sqs-lambda/compare/v0.3.0...v0.4.0) (2021-04-15) 112 | 113 | 114 | ### Features 115 | 116 | * force release ([26d4e46](https://github.com/agiledigital/serverless-sns-sqs-lambda/commit/26d4e46e50a9c13d5a79326fd88d46d9d883023e)) 117 | 118 | # [0.3.0](https://github.com/agiledigital/serverless-sns-sqs-lambda/compare/v0.2.1...v0.3.0) (2021-03-04) 119 | 120 | 121 | ### Features 122 | 123 | * add `deadLetterMessageRetentionPeriodSeconds` option ([89a17b8](https://github.com/agiledigital/serverless-sns-sqs-lambda/commit/89a17b829c48f66574efcaeea478193a6954ac53)) 124 | 125 | ## [0.2.1](https://github.com/agiledigital/serverless-sns-sqs-lambda/compare/v0.2.0...v0.2.1) (2021-01-20) 126 | 127 | 128 | ### Bug Fixes 129 | 130 | * Fix import of AWS provider class in test ([acd748f](https://github.com/agiledigital/serverless-sns-sqs-lambda/commit/acd748ff7ac5fff87ba9f76d4631d8c2869117af)) 131 | 132 | # [0.2.0](https://github.com/agiledigital/serverless-sns-sqs-lambda/compare/v0.1.12...v0.2.0) (2020-11-03) 133 | 134 | 135 | ### Features 136 | 137 | * force release ([fca8a5a](https://github.com/agiledigital/serverless-sns-sqs-lambda/commit/fca8a5adde9e595c6f99f8e088ac01fad98dbb33)) 138 | 139 | ## [0.1.12](https://github.com/agiledigital/serverless-sns-sqs-lambda/compare/v0.1.11...v0.1.12) (2020-09-11) 140 | 141 | 142 | ### Bug Fixes 143 | 144 | * Use raw ARN to avoid cyclic dependency ([f16ab09](https://github.com/agiledigital/serverless-sns-sqs-lambda/commit/f16ab09fa5872c804cd8ea64771e3cd27ca51865)) 145 | 146 | ## [0.1.11](https://github.com/agiledigital/serverless-sns-sqs-lambda/compare/v0.1.10...v0.1.11) (2020-08-11) 147 | 148 | 149 | ### Bug Fixes 150 | 151 | * don't include cruft in release tarball ([6243ba1](https://github.com/agiledigital/serverless-sns-sqs-lambda/commit/6243ba10b22b038748516d08b875ba654f9a814e)) 152 | 153 | ## [0.1.10](https://github.com/agiledigital/serverless-sns-sqs-lambda/compare/v0.1.9...v0.1.10) (2020-08-11) 154 | 155 | 156 | ### Bug Fixes 157 | 158 | * add badges to README ([0d2549e](https://github.com/agiledigital/serverless-sns-sqs-lambda/commit/0d2549e5f7bf28c20c83089b64914a72b901d035)) 159 | * make style more consistent ([d6248ea](https://github.com/agiledigital/serverless-sns-sqs-lambda/commit/d6248ea2b38a22fd28b956c1b73e6fba4345170d)) 160 | 161 | # 0.0.\* 162 | 163 | First Release and other initial work 164 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Serverless Sns Sqs Lambda 2 | 3 | [![serverless](http://public.serverless.com/badges/v3.svg)](http://www.serverless.com) 4 | [![MIT License](http://img.shields.io/badge/license-MIT-blue.svg?style=flat)](LICENSE) 5 | ![Github Actions Status](https://github.com/agiledigital/serverless-sns-sqs-lambda/workflows/Node.js%20CI/badge.svg?branch=master) 6 | [![Type Coverage](https://img.shields.io/badge/dynamic/json.svg?label=type-coverage&prefix=%E2%89%A5&suffix=%&query=$.typeCoverage.atLeast&uri=https%3A%2F%2Fraw.githubusercontent.com%2Fagiledigital%2Fserverless-sns-sqs-lambda%2Fmaster%2Fpackage.json)](https://github.com/plantain-00/type-coverage) 7 | [![Language grade: JavaScript](https://img.shields.io/lgtm/grade/javascript/g/agiledigital/serverless-sns-sqs-lambda.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/agiledigital/serverless-sns-sqs-lambda/context:javascript) 8 | [![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) 9 | ![npm](https://img.shields.io/npm/v/@agiledigital/serverless-sns-sqs-lambda) 10 | 11 | This is a Serverless Framework plugin for AWS lambda Functions. Currently, it 12 | is possible to subscribe directly to an SNS topic. However, if you want to 13 | provide retry capability and error handling, you need to write a whole lot of 14 | boilerplate to add a Queue and a Dead Letter Queue between the Lambda and the 15 | SNS topic. This plugin allows you to define an sns subscriber with a `batchSize` 16 | and a `maxRetryCount` as simply as subscribing directly to the sns topic. 17 | 18 | # Table of Contents 19 | 20 | - [Install](#install) 21 | - [Setup](#setup) 22 | 23 | ## Install 24 | 25 | Run `npm install` in your Serverless project. 26 | 27 | `$ npm install --save-dev @agiledigital/serverless-sns-sqs-lambda` 28 | 29 | Add the plugin to your serverless.yml file 30 | 31 | ```yml 32 | plugins: 33 | - "@agiledigital/serverless-sns-sqs-lambda" 34 | ``` 35 | 36 | ## Setup 37 | 38 | Provide the lambda function with the snsSqs event, the plugin will add the AWS SNS topic and subscription, SQS queue and dead letter queue, and the role need for the lambda. 39 | 40 | ```yml 41 | functions: 42 | processEvent: 43 | handler: handler.handler 44 | events: 45 | - snsSqs: 46 | name: TestEvent # Required - choose a name prefix for the event queue 47 | topicArn: !Ref Topic # Required - SNS topic to subscribe to 48 | omitPhysicalId: true # Optional - default value is false but recommended to be set to true for new deployments (see below) 49 | batchSize: 2 # Optional - default value is 10 50 | maximumBatchingWindowInSeconds: 10 # optional - default is 0 (no batch window) 51 | maxRetryCount: 2 # Optional - default value is 5 52 | kmsMasterKeyId: !GetAtt SQSQueueKey.Arn # optional - default is none (no encryption) - see Notes on Encryption section below 53 | kmsDataKeyReusePeriodSeconds: 600 # optional - AWS default is 300 seconds 54 | deadLetterMessageRetentionPeriodSeconds: 1209600 # optional - AWS default is 345600 secs (4 days) 55 | deadLetterQueueEnabled: true # optional - default is true 56 | visibilityTimeout: 120 # optional (in seconds) - AWS default is 30 secs 57 | rawMessageDelivery: true # Optional - default value is true 58 | enabled: true # Optional - default value is true 59 | filterPolicy: # Optional - filter messages that are handled 60 | pets: 61 | - dog 62 | - cat 63 | 64 | # Overrides for generated CloudFormation templates 65 | # Mirrors the CloudFormation docs but uses camel case instead of title case 66 | # 67 | # https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-sqs-queues.html 68 | mainQueueOverride: 69 | maximumMessageSize: 1024 70 | ... 71 | deadLetterQueueOverride: 72 | maximumMessageSize: 1024 73 | ... 74 | # https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-eventsourcemapping.html 75 | eventSourceMappingOverride: 76 | sourceAccessConfigurations: 77 | - Type: SASL_SCRAM_256_AUTH 78 | URI: arn:aws:secretsmanager:us-east-1:01234567890:secret:MyBrokerSecretName 79 | # https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-sns-subscription.html 80 | subscriptionOverride: 81 | region: ap-southeast-2 82 | 83 | resources: 84 | Resources: 85 | Topic: 86 | Type: AWS::SNS::Topic 87 | Properties: 88 | TopicName: TestTopic 89 | 90 | plugins: 91 | - "@agiledigital/serverless-sns-sqs-lambda" 92 | ``` 93 | 94 | ### What is the omitPhysicalId option? 95 | 96 | AWS allows you to omit the physical ID (queue name) for SQS resources. 97 | 98 | In this case, the name is automatically generated by AWS based on the logical ID (the CloudFormation resource name). 99 | 100 | This provides some benefits such as the ability to perform updates that require replacement, and not having to worry about the 80 character maximum length for queue names. 101 | 102 | However, if you need to refer to the queue by name (which should be rare), rather than a CloudFormation reference, you might have to switch this off so that the name is stable. 103 | 104 | This would be set to 'true' by default if this was the first version of the plugin. However, since the plugin is already in use and it could break existing stacks and may not be backwards compatible we have set this to 'false' by default. Switching this to 'true' on an existing stack has been tested and it seems to work OK, although it does create new queues and deletes the old ones. Switch the option on an existing stack at your own risk. 105 | 106 | See also: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-name.html 107 | 108 | ### Notes on Encryption 109 | 110 | If you choose to encrypt your SQS queue, the SNS topic will not be able to send it any messages if you use a managed key (alias/aws/sqs). This is due to an AWS limitation. 111 | 112 | See: https://aws.amazon.com/premiumsupport/knowledge-center/sns-topic-sqs-queue-sse-kms-key-policy/ 113 | 114 | You will need to create a CMK like the following: 115 | 116 | ```yaml 117 | # To allow SNS to push messages to an encrypted queue, a CMK must be used 118 | SQSQueueCMK: 119 | Type: AWS::KMS::Key 120 | Properties: 121 | KeyPolicy: 122 | Version: "2012-10-17" 123 | Id: key-default-1 124 | Statement: 125 | - Sid: Enable IAM User Permissions 126 | Effect: Allow 127 | Principal: 128 | AWS: !Join 129 | - "" 130 | - - "arn:aws:iam::" 131 | - !Ref "AWS::AccountId" 132 | - ":root" 133 | Action: "kms:*" 134 | Resource: "*" 135 | - Sid: Allow SNS publish to SQS 136 | Effect: Allow 137 | Principal: 138 | Service: sns.amazonaws.com 139 | Action: 140 | - kms:GenerateDataKey 141 | - kms:Decrypt 142 | Resource: "*" 143 | ``` 144 | 145 | and then reference it in the `snsSqs` config with the `kmsMasterKeyId` attribute. 146 | 147 | ```yaml 148 | functions: 149 | processEvent: 150 | handler: handler.handler 151 | events: 152 | - snsSqs: 153 | # ... 154 | kmsMasterKeyId: !GetAtt SQSQueueKey.Arn 155 | # ... 156 | ``` 157 | 158 | `kmsMasterKeyId` can either be a key ID (simple string) or an ARN or reference to to ARN. Using !Ref on a key will return a key ID and is invalid, so you'll need to use GetAtt and reference the Arn property. 159 | 160 | ### CloudFormation Overrides 161 | 162 | If you would like to override a part of the CloudFormation template 163 | that is generated by this plugin, you can pass raw CloudFormation 164 | to the override config options outlined above. 165 | 166 | The configuration must be provided with camel case keys, 167 | but apart from that, you can use the CloudFormation config 168 | as specified by AWS. 169 | 170 | For example, if you wanted to override the maximumMessageSize for the main queue 171 | you could find the "MaximumMessageSize" config option in the [AWS documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-sqs-queues.html) 172 | make the key camel case ("maximumMessageSize") and pass it into the override section: 173 | 174 | ```yaml 175 | events: 176 | - snsSqs: 177 | name: Example 178 | ... 179 | mainQueueOverride: 180 | maximumMessageSize: 1024 181 | ``` 182 | -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { extends: ["@commitlint/config-conventional"] }; 2 | -------------------------------------------------------------------------------- /example-service/handler.js: -------------------------------------------------------------------------------- 1 | module.exports.handler = async event => { 2 | console.dir(event, { depth: null }); 3 | }; 4 | -------------------------------------------------------------------------------- /example-service/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "test-service", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "handler.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "devDependencies": { 13 | "@agiledigital/serverless-sns-sqs-lambda": "file:../", 14 | "serverless": "^3.16.0" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /example-service/serverless.yml: -------------------------------------------------------------------------------- 1 | service: sns-sqs-service 2 | # Ensure validation issues are treated as errors 3 | configValidationMode: error 4 | frameworkVersion: "3" 5 | 6 | provider: 7 | name: aws 8 | runtime: nodejs14.x 9 | region: ap-southeast-2 10 | stage: ${opt:stage, 'dev'} 11 | 12 | custom: 13 | topicArn: !Ref Topic 14 | 15 | functions: 16 | processEvent: 17 | handler: handler.handler 18 | events: 19 | - snsSqs: 20 | name: ResourcePrefix 21 | topicArn: ${self:custom.topicArn} 22 | omitPhysicalId: true 23 | batchSize: 2 24 | maximumBatchingWindowInSeconds: 30 25 | maxRetryCount: 2 26 | kmsMasterKeyId: !GetAtt SQSQueueKey.Arn 27 | kmsDataKeyReusePeriodSeconds: 600 28 | visibilityTimeout: 120 29 | rawMessageDelivery: true 30 | # filterPolicy: 31 | # pet: 32 | # - dog 33 | # - cat 34 | 35 | # Overrides for generated CloudFormation templates 36 | # Mirrors the CloudFormation docs but uses camel case instead of title case 37 | # 38 | # 39 | # https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-sqs-queues.html 40 | mainQueueOverride: 41 | maximumMessageSize: 1024 42 | deadLetterQueueOverride: 43 | delaySeconds: 120 44 | # https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-eventsourcemapping.html 45 | eventSourceMappingOverride: 46 | enabled: true 47 | # https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-sns-subscription.html 48 | subscriptionOverride: 49 | rawMessageDelivery: true 50 | 51 | resources: 52 | Resources: 53 | Topic: 54 | Type: AWS::SNS::Topic 55 | Properties: 56 | TopicName: TestTopic 57 | # To allow SNS to push messages to an encrypted queue, a CMK must be used 58 | SQSQueueKey: 59 | Type: AWS::KMS::Key 60 | Properties: 61 | KeyPolicy: 62 | Version: "2012-10-17" 63 | Id: key-default-1 64 | Statement: 65 | - Sid: Enable IAM User Permissions 66 | Effect: Allow 67 | Principal: 68 | AWS: !Join 69 | - "" 70 | - - "arn:aws:iam::" 71 | - !Ref "AWS::AccountId" 72 | - ":root" 73 | Action: "kms:*" 74 | Resource: "*" 75 | - Sid: Allow SNS publish to SQS 76 | Effect: Allow 77 | Principal: 78 | Service: sns.amazonaws.com 79 | Action: 80 | - kms:GenerateDataKey 81 | - kms:Decrypt 82 | Resource: "*" 83 | 84 | plugins: 85 | - "@agiledigital/serverless-sns-sqs-lambda" 86 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: "ts-jest", 3 | testEnvironment: "node", 4 | coverageThreshold: { 5 | global: { 6 | branches: 74.5, 7 | functions: 66, 8 | lines: 78, 9 | statements: 76 10 | } 11 | } 12 | }; 13 | -------------------------------------------------------------------------------- /lib/__fixtures__/handler.js: -------------------------------------------------------------------------------- 1 | // Example handler for serverless testing 2 | 3 | module.exports.handler = (event, context, callback) => 4 | callback(null, { 5 | statusCode: 200, 6 | body: JSON.stringify({}) 7 | }); 8 | -------------------------------------------------------------------------------- /lib/__snapshots__/serverless-sns-sqs-lambda.test.ts.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Test Serverless SNS SQS Lambda when no provider is specified when fifo is true stage should default to 'dev' 1`] = ` 4 | Object { 5 | "Resources": Object { 6 | "IamRoleLambdaExecution": Object { 7 | "Properties": Object { 8 | "AssumeRolePolicyDocument": Object { 9 | "Statement": Array [ 10 | Object { 11 | "Action": Array [ 12 | "sts:AssumeRole", 13 | ], 14 | "Effect": "Allow", 15 | "Principal": Object { 16 | "Service": Array [ 17 | "lambda.amazonaws.com", 18 | ], 19 | }, 20 | }, 21 | ], 22 | "Version": "2012-10-17", 23 | }, 24 | "Path": "/", 25 | "Policies": Array [ 26 | Object { 27 | "PolicyDocument": Object { 28 | "Statement": Array [ 29 | Object { 30 | "Action": Array [ 31 | "sqs:ReceiveMessage", 32 | "sqs:DeleteMessage", 33 | "sqs:GetQueueAttributes", 34 | ], 35 | "Effect": "Allow", 36 | "Resource": Array [ 37 | Object { 38 | "Fn::GetAtt": Array [ 39 | "some-nameQueue", 40 | "Arn", 41 | ], 42 | }, 43 | Object { 44 | "Fn::GetAtt": Array [ 45 | "some-nameDeadLetterQueue", 46 | "Arn", 47 | ], 48 | }, 49 | ], 50 | }, 51 | ], 52 | "Version": "2012-10-17", 53 | }, 54 | "PolicyName": Object { 55 | "Fn::Join": Array [ 56 | "-", 57 | Array [ 58 | "sns-sqs-service", 59 | "dev-sd", 60 | "lambda", 61 | ], 62 | ], 63 | }, 64 | }, 65 | ], 66 | "RoleName": Object { 67 | "Fn::Join": Array [ 68 | "-", 69 | Array [ 70 | "sns-sqs-service", 71 | "dev-sd", 72 | Object { 73 | "Ref": "AWS::Region", 74 | }, 75 | "lambdaRole", 76 | ], 77 | ], 78 | }, 79 | }, 80 | "Type": "AWS::IAM::Role", 81 | }, 82 | "Subscribesome-nameTopic": Object { 83 | "Properties": Object { 84 | "Endpoint": Object { 85 | "Fn::GetAtt": Array [ 86 | "some-nameQueue", 87 | "Arn", 88 | ], 89 | }, 90 | "Protocol": "sqs", 91 | "RawMessageDelivery": false, 92 | "TopicArn": "arn:aws:sns:us-east-2:123456789012:MyTopic", 93 | }, 94 | "Type": "AWS::SNS::Subscription", 95 | }, 96 | "Test-functionEventSourceMappingSQSsome-nameQueue": Object { 97 | "Properties": Object { 98 | "BatchSize": 10, 99 | "Enabled": "True", 100 | "EventSourceArn": Object { 101 | "Fn::GetAtt": Array [ 102 | "some-nameQueue", 103 | "Arn", 104 | ], 105 | }, 106 | "FunctionName": Object { 107 | "Fn::GetAtt": Array [ 108 | "Test-functionLambdaFunction", 109 | "Arn", 110 | ], 111 | }, 112 | "MaximumBatchingWindowInSeconds": 0, 113 | }, 114 | "Type": "AWS::Lambda::EventSourceMapping", 115 | }, 116 | "some-nameDeadLetterQueue": Object { 117 | "Properties": Object { 118 | "FifoQueue": true, 119 | "QueueName": "test-service-dev-Test-functionsome-nameDeadLetterQueue.fifo", 120 | }, 121 | "Type": "AWS::SQS::Queue", 122 | }, 123 | "some-nameQueue": Object { 124 | "Properties": Object { 125 | "FifoQueue": true, 126 | "QueueName": "test-service-dev-Test-functionsome-nameQueue.fifo", 127 | "RedrivePolicy": Object { 128 | "deadLetterTargetArn": Object { 129 | "Fn::GetAtt": Array [ 130 | "some-nameDeadLetterQueue", 131 | "Arn", 132 | ], 133 | }, 134 | "maxReceiveCount": 5, 135 | }, 136 | }, 137 | "Type": "AWS::SQS::Queue", 138 | }, 139 | }, 140 | } 141 | `; 142 | 143 | exports[`Test Serverless SNS SQS Lambda when no provider is specified when no optional parameters are provided stage should default to 'dev' 1`] = ` 144 | Object { 145 | "Resources": Object { 146 | "IamRoleLambdaExecution": Object { 147 | "Properties": Object { 148 | "AssumeRolePolicyDocument": Object { 149 | "Statement": Array [ 150 | Object { 151 | "Action": Array [ 152 | "sts:AssumeRole", 153 | ], 154 | "Effect": "Allow", 155 | "Principal": Object { 156 | "Service": Array [ 157 | "lambda.amazonaws.com", 158 | ], 159 | }, 160 | }, 161 | ], 162 | "Version": "2012-10-17", 163 | }, 164 | "Path": "/", 165 | "Policies": Array [ 166 | Object { 167 | "PolicyDocument": Object { 168 | "Statement": Array [ 169 | Object { 170 | "Action": Array [ 171 | "sqs:ReceiveMessage", 172 | "sqs:DeleteMessage", 173 | "sqs:GetQueueAttributes", 174 | ], 175 | "Effect": "Allow", 176 | "Resource": Array [ 177 | Object { 178 | "Fn::GetAtt": Array [ 179 | "some-nameQueue", 180 | "Arn", 181 | ], 182 | }, 183 | Object { 184 | "Fn::GetAtt": Array [ 185 | "some-nameDeadLetterQueue", 186 | "Arn", 187 | ], 188 | }, 189 | ], 190 | }, 191 | ], 192 | "Version": "2012-10-17", 193 | }, 194 | "PolicyName": Object { 195 | "Fn::Join": Array [ 196 | "-", 197 | Array [ 198 | "sns-sqs-service", 199 | "dev-sd", 200 | "lambda", 201 | ], 202 | ], 203 | }, 204 | }, 205 | ], 206 | "RoleName": Object { 207 | "Fn::Join": Array [ 208 | "-", 209 | Array [ 210 | "sns-sqs-service", 211 | "dev-sd", 212 | Object { 213 | "Ref": "AWS::Region", 214 | }, 215 | "lambdaRole", 216 | ], 217 | ], 218 | }, 219 | }, 220 | "Type": "AWS::IAM::Role", 221 | }, 222 | "Subscribesome-nameTopic": Object { 223 | "Properties": Object { 224 | "Endpoint": Object { 225 | "Fn::GetAtt": Array [ 226 | "some-nameQueue", 227 | "Arn", 228 | ], 229 | }, 230 | "Protocol": "sqs", 231 | "RawMessageDelivery": false, 232 | "TopicArn": "arn:aws:sns:us-east-2:123456789012:MyTopic", 233 | }, 234 | "Type": "AWS::SNS::Subscription", 235 | }, 236 | "Test-functionEventSourceMappingSQSsome-nameQueue": Object { 237 | "Properties": Object { 238 | "BatchSize": 10, 239 | "Enabled": "True", 240 | "EventSourceArn": Object { 241 | "Fn::GetAtt": Array [ 242 | "some-nameQueue", 243 | "Arn", 244 | ], 245 | }, 246 | "FunctionName": Object { 247 | "Fn::GetAtt": Array [ 248 | "Test-functionLambdaFunction", 249 | "Arn", 250 | ], 251 | }, 252 | "MaximumBatchingWindowInSeconds": 0, 253 | }, 254 | "Type": "AWS::Lambda::EventSourceMapping", 255 | }, 256 | "some-nameDeadLetterQueue": Object { 257 | "Properties": Object { 258 | "QueueName": "test-service-dev-Test-functionsome-nameDeadLetterQueue", 259 | }, 260 | "Type": "AWS::SQS::Queue", 261 | }, 262 | "some-nameQueue": Object { 263 | "Properties": Object { 264 | "QueueName": "test-service-dev-Test-functionsome-nameQueue", 265 | "RedrivePolicy": Object { 266 | "deadLetterTargetArn": Object { 267 | "Fn::GetAtt": Array [ 268 | "some-nameDeadLetterQueue", 269 | "Arn", 270 | ], 271 | }, 272 | "maxReceiveCount": 5, 273 | }, 274 | }, 275 | "Type": "AWS::SQS::Queue", 276 | }, 277 | }, 278 | } 279 | `; 280 | 281 | exports[`Test Serverless SNS SQS Lambda when the provider is specified via a command line option when a custom role ARN is specified it should not crash and just skip creating the policies 1`] = ` 282 | Object { 283 | "AWSTemplateFormatVersion": "2010-09-09", 284 | "Description": "The AWS CloudFormation template for this Serverless application", 285 | "Outputs": Object { 286 | "ServerlessDeploymentBucketName": Object { 287 | "Export": Object { 288 | "Name": "sls-test-service-dev-test-ServerlessDeploymentBucketName", 289 | }, 290 | "Value": Object { 291 | "Ref": "ServerlessDeploymentBucket", 292 | }, 293 | }, 294 | "TestDashfunctionLambdaFunctionQualifiedArn": Object { 295 | "Description": "Current Lambda function version", 296 | "Export": Object { 297 | "Name": "sls-test-service-dev-test-TestDashfunctionLambdaFunctionQualifiedArn", 298 | }, 299 | "Value": Object { 300 | "Ref": "TestDashfunctionLambdaVersionNhmSWGzAId4Snsrzfb2wH48iEwV8gnTAuJN5hvWQ9Y", 301 | }, 302 | }, 303 | }, 304 | "Resources": Object { 305 | "ServerlessDeploymentBucket": Object { 306 | "Properties": Object { 307 | "BucketEncryption": Object { 308 | "ServerSideEncryptionConfiguration": Array [ 309 | Object { 310 | "ServerSideEncryptionByDefault": Object { 311 | "SSEAlgorithm": "AES256", 312 | }, 313 | }, 314 | ], 315 | }, 316 | }, 317 | "Type": "AWS::S3::Bucket", 318 | }, 319 | "ServerlessDeploymentBucketPolicy": Object { 320 | "Properties": Object { 321 | "Bucket": Object { 322 | "Ref": "ServerlessDeploymentBucket", 323 | }, 324 | "PolicyDocument": Object { 325 | "Statement": Array [ 326 | Object { 327 | "Action": "s3:*", 328 | "Condition": Object { 329 | "Bool": Object { 330 | "aws:SecureTransport": false, 331 | }, 332 | }, 333 | "Effect": "Deny", 334 | "Principal": "*", 335 | "Resource": Array [ 336 | Object { 337 | "Fn::Join": Array [ 338 | "", 339 | Array [ 340 | "arn:", 341 | Object { 342 | "Ref": "AWS::Partition", 343 | }, 344 | ":s3:::", 345 | Object { 346 | "Ref": "ServerlessDeploymentBucket", 347 | }, 348 | "/*", 349 | ], 350 | ], 351 | }, 352 | Object { 353 | "Fn::Join": Array [ 354 | "", 355 | Array [ 356 | "arn:", 357 | Object { 358 | "Ref": "AWS::Partition", 359 | }, 360 | ":s3:::", 361 | Object { 362 | "Ref": "ServerlessDeploymentBucket", 363 | }, 364 | ], 365 | ], 366 | }, 367 | ], 368 | }, 369 | ], 370 | }, 371 | }, 372 | "Type": "AWS::S3::BucketPolicy", 373 | }, 374 | "Subscribesome-nameTopic": Object { 375 | "Properties": Object { 376 | "Endpoint": Object { 377 | "Fn::GetAtt": Array [ 378 | "some-nameQueue", 379 | "Arn", 380 | ], 381 | }, 382 | "Protocol": "sqs", 383 | "RawMessageDelivery": false, 384 | "TopicArn": "arn:aws:sns:us-east-2:123456789012:MyTopic", 385 | }, 386 | "Type": "AWS::SNS::Subscription", 387 | }, 388 | "Test-functionEventSourceMappingSQSsome-nameQueue": Object { 389 | "Properties": Object { 390 | "BatchSize": 10, 391 | "Enabled": "True", 392 | "EventSourceArn": Object { 393 | "Fn::GetAtt": Array [ 394 | "some-nameQueue", 395 | "Arn", 396 | ], 397 | }, 398 | "FunctionName": Object { 399 | "Fn::GetAtt": Array [ 400 | "Test-functionLambdaFunction", 401 | "Arn", 402 | ], 403 | }, 404 | "MaximumBatchingWindowInSeconds": 0, 405 | }, 406 | "Type": "AWS::Lambda::EventSourceMapping", 407 | }, 408 | "TestDashfunctionLambdaFunction": Object { 409 | "DependsOn": Array [ 410 | "TestDashfunctionLogGroup", 411 | ], 412 | "Properties": Object { 413 | "Code": Object { 414 | "S3Bucket": Object { 415 | "Ref": "ServerlessDeploymentBucket", 416 | }, 417 | "S3Key": Any, 418 | }, 419 | "FunctionName": "test-service-dev-test-test-function", 420 | "Handler": "handler.handler", 421 | "MemorySize": 1024, 422 | "Role": "arn:aws:iam::123456789012:role/execution-role", 423 | "Runtime": "nodejs14.x", 424 | "Timeout": 6, 425 | }, 426 | "Type": "AWS::Lambda::Function", 427 | }, 428 | "TestDashfunctionLambdaVersionNhmSWGzAId4Snsrzfb2wH48iEwV8gnTAuJN5hvWQ9Y": Object { 429 | "DeletionPolicy": "Retain", 430 | "Properties": Object { 431 | "CodeSha256": "gxQ2/ARVAXYSjz4OF5PnsOiOB+yUlXG8z5y5h6bNs7U=", 432 | "FunctionName": Object { 433 | "Ref": "TestDashfunctionLambdaFunction", 434 | }, 435 | }, 436 | "Type": "AWS::Lambda::Version", 437 | }, 438 | "TestDashfunctionLogGroup": Object { 439 | "Properties": Object { 440 | "LogGroupName": "/aws/lambda/test-service-dev-test-test-function", 441 | }, 442 | "Type": "AWS::Logs::LogGroup", 443 | }, 444 | "some-nameDeadLetterQueue": Object { 445 | "Properties": Object { 446 | "QueueName": "test-service-dev-test-Test-functionsome-nameDeadLetterQueue", 447 | }, 448 | "Type": "AWS::SQS::Queue", 449 | }, 450 | "some-nameQueue": Object { 451 | "Properties": Object { 452 | "QueueName": "test-service-dev-test-Test-functionsome-nameQueue", 453 | "RedrivePolicy": Object { 454 | "deadLetterTargetArn": Object { 455 | "Fn::GetAtt": Array [ 456 | "some-nameDeadLetterQueue", 457 | "Arn", 458 | ], 459 | }, 460 | "maxReceiveCount": 5, 461 | }, 462 | }, 463 | "Type": "AWS::SQS::Queue", 464 | }, 465 | "some-nameQueuePolicy": Object { 466 | "Properties": Object { 467 | "PolicyDocument": Object { 468 | "Id": "test-service-dev-test-Test-functionsome-nameQueue", 469 | "Statement": Array [ 470 | Object { 471 | "Action": "SQS:SendMessage", 472 | "Condition": Object { 473 | "ArnEquals": Object { 474 | "aws:SourceArn": Array [ 475 | "arn:aws:sns:us-east-2:123456789012:MyTopic", 476 | ], 477 | }, 478 | }, 479 | "Effect": "Allow", 480 | "Principal": Object { 481 | "Service": "sns.amazonaws.com", 482 | }, 483 | "Resource": Object { 484 | "Fn::GetAtt": Array [ 485 | "some-nameQueue", 486 | "Arn", 487 | ], 488 | }, 489 | "Sid": "test-service-dev-test-Test-functionsome-nameSid", 490 | }, 491 | ], 492 | "Version": "2012-10-17", 493 | }, 494 | "Queues": Array [ 495 | Object { 496 | "Ref": "some-nameQueue", 497 | }, 498 | ], 499 | }, 500 | "Type": "AWS::SQS::QueuePolicy", 501 | }, 502 | }, 503 | } 504 | `; 505 | 506 | exports[`Test Serverless SNS SQS Lambda when the provider is specified via a command line option when all parameters are provided should produce valid SQS CF template items 1`] = ` 507 | Object { 508 | "AWSTemplateFormatVersion": "2010-09-09", 509 | "Description": "The AWS CloudFormation template for this Serverless application", 510 | "Outputs": Object { 511 | "ServerlessDeploymentBucketName": Object { 512 | "Export": Object { 513 | "Name": "sls-test-service-dev-test-ServerlessDeploymentBucketName", 514 | }, 515 | "Value": Object { 516 | "Ref": "ServerlessDeploymentBucket", 517 | }, 518 | }, 519 | "TestDashfunctionLambdaFunctionQualifiedArn": Object { 520 | "Description": "Current Lambda function version", 521 | "Export": Object { 522 | "Name": "sls-test-service-dev-test-TestDashfunctionLambdaFunctionQualifiedArn", 523 | }, 524 | "Value": Object { 525 | "Ref": "TestDashfunctionLambdaVersionA6M23sE6AN9SgN5IQgI9bd1tqh7YgxtybZ9LOhkLY4", 526 | }, 527 | }, 528 | }, 529 | "Resources": Object { 530 | "IamRoleLambdaExecution": Object { 531 | "Properties": Object { 532 | "AssumeRolePolicyDocument": Object { 533 | "Statement": Array [ 534 | Object { 535 | "Action": Array [ 536 | "sts:AssumeRole", 537 | ], 538 | "Effect": "Allow", 539 | "Principal": Object { 540 | "Service": Array [ 541 | "lambda.amazonaws.com", 542 | ], 543 | }, 544 | }, 545 | ], 546 | "Version": "2012-10-17", 547 | }, 548 | "Path": "/", 549 | "Policies": Array [ 550 | Object { 551 | "PolicyDocument": Object { 552 | "Statement": Array [ 553 | Object { 554 | "Action": Array [ 555 | "logs:CreateLogStream", 556 | "logs:CreateLogGroup", 557 | ], 558 | "Effect": "Allow", 559 | "Resource": Array [ 560 | Object { 561 | "Fn::Sub": "arn:\${AWS::Partition}:logs:\${AWS::Region}:\${AWS::AccountId}:log-group:/aws/lambda/test-service-dev-test*:*", 562 | }, 563 | ], 564 | }, 565 | Object { 566 | "Action": Array [ 567 | "logs:PutLogEvents", 568 | ], 569 | "Effect": "Allow", 570 | "Resource": Array [ 571 | Object { 572 | "Fn::Sub": "arn:\${AWS::Partition}:logs:\${AWS::Region}:\${AWS::AccountId}:log-group:/aws/lambda/test-service-dev-test*:*:*", 573 | }, 574 | ], 575 | }, 576 | Object { 577 | "Action": Array [ 578 | "sqs:ReceiveMessage", 579 | "sqs:DeleteMessage", 580 | "sqs:GetQueueAttributes", 581 | ], 582 | "Effect": "Allow", 583 | "Resource": Array [ 584 | Object { 585 | "Fn::GetAtt": Array [ 586 | "some-nameQueue", 587 | "Arn", 588 | ], 589 | }, 590 | Object { 591 | "Fn::GetAtt": Array [ 592 | "some-nameDeadLetterQueue", 593 | "Arn", 594 | ], 595 | }, 596 | ], 597 | }, 598 | Object { 599 | "Action": Array [ 600 | "kms:Decrypt", 601 | ], 602 | "Effect": "Allow", 603 | "Resource": "arn:aws:kms:::key/some key", 604 | }, 605 | ], 606 | "Version": "2012-10-17", 607 | }, 608 | "PolicyName": Object { 609 | "Fn::Join": Array [ 610 | "-", 611 | Array [ 612 | "test-service", 613 | "dev-test", 614 | "lambda", 615 | ], 616 | ], 617 | }, 618 | }, 619 | ], 620 | "RoleName": Object { 621 | "Fn::Join": Array [ 622 | "-", 623 | Array [ 624 | "test-service", 625 | "dev-test", 626 | Object { 627 | "Ref": "AWS::Region", 628 | }, 629 | "lambdaRole", 630 | ], 631 | ], 632 | }, 633 | }, 634 | "Type": "AWS::IAM::Role", 635 | }, 636 | "ServerlessDeploymentBucket": Object { 637 | "Properties": Object { 638 | "BucketEncryption": Object { 639 | "ServerSideEncryptionConfiguration": Array [ 640 | Object { 641 | "ServerSideEncryptionByDefault": Object { 642 | "SSEAlgorithm": "AES256", 643 | }, 644 | }, 645 | ], 646 | }, 647 | }, 648 | "Type": "AWS::S3::Bucket", 649 | }, 650 | "ServerlessDeploymentBucketPolicy": Object { 651 | "Properties": Object { 652 | "Bucket": Object { 653 | "Ref": "ServerlessDeploymentBucket", 654 | }, 655 | "PolicyDocument": Object { 656 | "Statement": Array [ 657 | Object { 658 | "Action": "s3:*", 659 | "Condition": Object { 660 | "Bool": Object { 661 | "aws:SecureTransport": false, 662 | }, 663 | }, 664 | "Effect": "Deny", 665 | "Principal": "*", 666 | "Resource": Array [ 667 | Object { 668 | "Fn::Join": Array [ 669 | "", 670 | Array [ 671 | "arn:", 672 | Object { 673 | "Ref": "AWS::Partition", 674 | }, 675 | ":s3:::", 676 | Object { 677 | "Ref": "ServerlessDeploymentBucket", 678 | }, 679 | "/*", 680 | ], 681 | ], 682 | }, 683 | Object { 684 | "Fn::Join": Array [ 685 | "", 686 | Array [ 687 | "arn:", 688 | Object { 689 | "Ref": "AWS::Partition", 690 | }, 691 | ":s3:::", 692 | Object { 693 | "Ref": "ServerlessDeploymentBucket", 694 | }, 695 | ], 696 | ], 697 | }, 698 | ], 699 | }, 700 | ], 701 | }, 702 | }, 703 | "Type": "AWS::S3::BucketPolicy", 704 | }, 705 | "Subscribesome-nameTopic": Object { 706 | "Properties": Object { 707 | "Endpoint": Object { 708 | "Fn::GetAtt": Array [ 709 | "some-nameQueue", 710 | "Arn", 711 | ], 712 | }, 713 | "FilterPolicy": Object { 714 | "pet": Array [ 715 | "dog", 716 | "cat", 717 | ], 718 | }, 719 | "Protocol": "sqs", 720 | "RawMessageDelivery": true, 721 | "TopicArn": "arn:aws:sns:us-east-2:123456789012:MyTopic", 722 | }, 723 | "Type": "AWS::SNS::Subscription", 724 | }, 725 | "Test-functionEventSourceMappingSQSsome-nameQueue": Object { 726 | "Properties": Object { 727 | "BatchSize": 7, 728 | "Enabled": "False", 729 | "EventSourceArn": Object { 730 | "Fn::GetAtt": Array [ 731 | "some-nameQueue", 732 | "Arn", 733 | ], 734 | }, 735 | "FunctionName": Object { 736 | "Fn::GetAtt": Array [ 737 | "Test-functionLambdaFunction", 738 | "Arn", 739 | ], 740 | }, 741 | "MaximumBatchingWindowInSeconds": 99, 742 | }, 743 | "Type": "AWS::Lambda::EventSourceMapping", 744 | }, 745 | "TestDashfunctionLambdaFunction": Object { 746 | "DependsOn": Array [ 747 | "TestDashfunctionLogGroup", 748 | ], 749 | "Properties": Object { 750 | "Code": Object { 751 | "S3Bucket": Object { 752 | "Ref": "ServerlessDeploymentBucket", 753 | }, 754 | "S3Key": Any, 755 | }, 756 | "FunctionName": "test-service-dev-test-test-function", 757 | "Handler": "handler.handler", 758 | "MemorySize": 1024, 759 | "Role": Object { 760 | "Fn::GetAtt": Array [ 761 | "IamRoleLambdaExecution", 762 | "Arn", 763 | ], 764 | }, 765 | "Runtime": "nodejs14.x", 766 | "Timeout": 6, 767 | }, 768 | "Type": "AWS::Lambda::Function", 769 | }, 770 | "TestDashfunctionLambdaVersionA6M23sE6AN9SgN5IQgI9bd1tqh7YgxtybZ9LOhkLY4": Object { 771 | "DeletionPolicy": "Retain", 772 | "Properties": Object { 773 | "CodeSha256": "gxQ2/ARVAXYSjz4OF5PnsOiOB+yUlXG8z5y5h6bNs7U=", 774 | "FunctionName": Object { 775 | "Ref": "TestDashfunctionLambdaFunction", 776 | }, 777 | }, 778 | "Type": "AWS::Lambda::Version", 779 | }, 780 | "TestDashfunctionLogGroup": Object { 781 | "Properties": Object { 782 | "LogGroupName": "/aws/lambda/test-service-dev-test-test-function", 783 | }, 784 | "Type": "AWS::Logs::LogGroup", 785 | }, 786 | "some-nameDeadLetterQueue": Object { 787 | "Properties": Object { 788 | "KmsDataKeyReusePeriodSeconds": 200, 789 | "KmsMasterKeyId": "some key", 790 | "MessageRetentionPeriod": 1209600, 791 | "QueueName": "some prefixsome-nameDeadLetterQueue", 792 | }, 793 | "Type": "AWS::SQS::Queue", 794 | }, 795 | "some-nameQueue": Object { 796 | "Properties": Object { 797 | "KmsDataKeyReusePeriodSeconds": 200, 798 | "KmsMasterKeyId": "some key", 799 | "QueueName": "some prefixsome-nameQueue", 800 | "RedrivePolicy": Object { 801 | "deadLetterTargetArn": Object { 802 | "Fn::GetAtt": Array [ 803 | "some-nameDeadLetterQueue", 804 | "Arn", 805 | ], 806 | }, 807 | "maxReceiveCount": 4, 808 | }, 809 | "VisibilityTimeout": 999, 810 | }, 811 | "Type": "AWS::SQS::Queue", 812 | }, 813 | "some-nameQueuePolicy": Object { 814 | "Properties": Object { 815 | "PolicyDocument": Object { 816 | "Id": "some prefixsome-nameQueue", 817 | "Statement": Array [ 818 | Object { 819 | "Action": "SQS:SendMessage", 820 | "Condition": Object { 821 | "ArnEquals": Object { 822 | "aws:SourceArn": Array [ 823 | "arn:aws:sns:us-east-2:123456789012:MyTopic", 824 | ], 825 | }, 826 | }, 827 | "Effect": "Allow", 828 | "Principal": Object { 829 | "Service": "sns.amazonaws.com", 830 | }, 831 | "Resource": Object { 832 | "Fn::GetAtt": Array [ 833 | "some-nameQueue", 834 | "Arn", 835 | ], 836 | }, 837 | "Sid": "some prefixsome-nameSid", 838 | }, 839 | ], 840 | "Version": "2012-10-17", 841 | }, 842 | "Queues": Array [ 843 | Object { 844 | "Ref": "some-nameQueue", 845 | }, 846 | ], 847 | }, 848 | "Type": "AWS::SQS::QueuePolicy", 849 | }, 850 | }, 851 | } 852 | `; 853 | 854 | exports[`Test Serverless SNS SQS Lambda when the provider is specified via a command line option when dead letter queue is disabled should not produce SQS dead letter queue and related IAM policies in CF template 1`] = ` 855 | Object { 856 | "AWSTemplateFormatVersion": "2010-09-09", 857 | "Description": "The AWS CloudFormation template for this Serverless application", 858 | "Outputs": Object { 859 | "ServerlessDeploymentBucketName": Object { 860 | "Export": Object { 861 | "Name": "sls-test-service-dev-test-ServerlessDeploymentBucketName", 862 | }, 863 | "Value": Object { 864 | "Ref": "ServerlessDeploymentBucket", 865 | }, 866 | }, 867 | "TestDashfunctionLambdaFunctionQualifiedArn": Object { 868 | "Description": "Current Lambda function version", 869 | "Export": Object { 870 | "Name": "sls-test-service-dev-test-TestDashfunctionLambdaFunctionQualifiedArn", 871 | }, 872 | "Value": Object { 873 | "Ref": "TestDashfunctionLambdaVersionA6M23sE6AN9SgN5IQgI9bd1tqh7YgxtybZ9LOhkLY4", 874 | }, 875 | }, 876 | }, 877 | "Resources": Object { 878 | "IamRoleLambdaExecution": Object { 879 | "Properties": Object { 880 | "AssumeRolePolicyDocument": Object { 881 | "Statement": Array [ 882 | Object { 883 | "Action": Array [ 884 | "sts:AssumeRole", 885 | ], 886 | "Effect": "Allow", 887 | "Principal": Object { 888 | "Service": Array [ 889 | "lambda.amazonaws.com", 890 | ], 891 | }, 892 | }, 893 | ], 894 | "Version": "2012-10-17", 895 | }, 896 | "Path": "/", 897 | "Policies": Array [ 898 | Object { 899 | "PolicyDocument": Object { 900 | "Statement": Array [ 901 | Object { 902 | "Action": Array [ 903 | "logs:CreateLogStream", 904 | "logs:CreateLogGroup", 905 | ], 906 | "Effect": "Allow", 907 | "Resource": Array [ 908 | Object { 909 | "Fn::Sub": "arn:\${AWS::Partition}:logs:\${AWS::Region}:\${AWS::AccountId}:log-group:/aws/lambda/test-service-dev-test*:*", 910 | }, 911 | ], 912 | }, 913 | Object { 914 | "Action": Array [ 915 | "logs:PutLogEvents", 916 | ], 917 | "Effect": "Allow", 918 | "Resource": Array [ 919 | Object { 920 | "Fn::Sub": "arn:\${AWS::Partition}:logs:\${AWS::Region}:\${AWS::AccountId}:log-group:/aws/lambda/test-service-dev-test*:*:*", 921 | }, 922 | ], 923 | }, 924 | Object { 925 | "Action": Array [ 926 | "sqs:ReceiveMessage", 927 | "sqs:DeleteMessage", 928 | "sqs:GetQueueAttributes", 929 | ], 930 | "Effect": "Allow", 931 | "Resource": Array [ 932 | Object { 933 | "Fn::GetAtt": Array [ 934 | "some-nameQueue", 935 | "Arn", 936 | ], 937 | }, 938 | ], 939 | }, 940 | ], 941 | "Version": "2012-10-17", 942 | }, 943 | "PolicyName": Object { 944 | "Fn::Join": Array [ 945 | "-", 946 | Array [ 947 | "test-service", 948 | "dev-test", 949 | "lambda", 950 | ], 951 | ], 952 | }, 953 | }, 954 | ], 955 | "RoleName": Object { 956 | "Fn::Join": Array [ 957 | "-", 958 | Array [ 959 | "test-service", 960 | "dev-test", 961 | Object { 962 | "Ref": "AWS::Region", 963 | }, 964 | "lambdaRole", 965 | ], 966 | ], 967 | }, 968 | }, 969 | "Type": "AWS::IAM::Role", 970 | }, 971 | "ServerlessDeploymentBucket": Object { 972 | "Properties": Object { 973 | "BucketEncryption": Object { 974 | "ServerSideEncryptionConfiguration": Array [ 975 | Object { 976 | "ServerSideEncryptionByDefault": Object { 977 | "SSEAlgorithm": "AES256", 978 | }, 979 | }, 980 | ], 981 | }, 982 | }, 983 | "Type": "AWS::S3::Bucket", 984 | }, 985 | "ServerlessDeploymentBucketPolicy": Object { 986 | "Properties": Object { 987 | "Bucket": Object { 988 | "Ref": "ServerlessDeploymentBucket", 989 | }, 990 | "PolicyDocument": Object { 991 | "Statement": Array [ 992 | Object { 993 | "Action": "s3:*", 994 | "Condition": Object { 995 | "Bool": Object { 996 | "aws:SecureTransport": false, 997 | }, 998 | }, 999 | "Effect": "Deny", 1000 | "Principal": "*", 1001 | "Resource": Array [ 1002 | Object { 1003 | "Fn::Join": Array [ 1004 | "", 1005 | Array [ 1006 | "arn:", 1007 | Object { 1008 | "Ref": "AWS::Partition", 1009 | }, 1010 | ":s3:::", 1011 | Object { 1012 | "Ref": "ServerlessDeploymentBucket", 1013 | }, 1014 | "/*", 1015 | ], 1016 | ], 1017 | }, 1018 | Object { 1019 | "Fn::Join": Array [ 1020 | "", 1021 | Array [ 1022 | "arn:", 1023 | Object { 1024 | "Ref": "AWS::Partition", 1025 | }, 1026 | ":s3:::", 1027 | Object { 1028 | "Ref": "ServerlessDeploymentBucket", 1029 | }, 1030 | ], 1031 | ], 1032 | }, 1033 | ], 1034 | }, 1035 | ], 1036 | }, 1037 | }, 1038 | "Type": "AWS::S3::BucketPolicy", 1039 | }, 1040 | "Subscribesome-nameTopic": Object { 1041 | "Properties": Object { 1042 | "Endpoint": Object { 1043 | "Fn::GetAtt": Array [ 1044 | "some-nameQueue", 1045 | "Arn", 1046 | ], 1047 | }, 1048 | "Protocol": "sqs", 1049 | "RawMessageDelivery": false, 1050 | "TopicArn": "arn:aws:sns:us-east-2:123456789012:MyTopic", 1051 | }, 1052 | "Type": "AWS::SNS::Subscription", 1053 | }, 1054 | "Test-functionEventSourceMappingSQSsome-nameQueue": Object { 1055 | "Properties": Object { 1056 | "BatchSize": 10, 1057 | "Enabled": "True", 1058 | "EventSourceArn": Object { 1059 | "Fn::GetAtt": Array [ 1060 | "some-nameQueue", 1061 | "Arn", 1062 | ], 1063 | }, 1064 | "FunctionName": Object { 1065 | "Fn::GetAtt": Array [ 1066 | "Test-functionLambdaFunction", 1067 | "Arn", 1068 | ], 1069 | }, 1070 | "MaximumBatchingWindowInSeconds": 0, 1071 | }, 1072 | "Type": "AWS::Lambda::EventSourceMapping", 1073 | }, 1074 | "TestDashfunctionLambdaFunction": Object { 1075 | "DependsOn": Array [ 1076 | "TestDashfunctionLogGroup", 1077 | ], 1078 | "Properties": Object { 1079 | "Code": Object { 1080 | "S3Bucket": Object { 1081 | "Ref": "ServerlessDeploymentBucket", 1082 | }, 1083 | "S3Key": Any, 1084 | }, 1085 | "FunctionName": "test-service-dev-test-test-function", 1086 | "Handler": "handler.handler", 1087 | "MemorySize": 1024, 1088 | "Role": Object { 1089 | "Fn::GetAtt": Array [ 1090 | "IamRoleLambdaExecution", 1091 | "Arn", 1092 | ], 1093 | }, 1094 | "Runtime": "nodejs14.x", 1095 | "Timeout": 6, 1096 | }, 1097 | "Type": "AWS::Lambda::Function", 1098 | }, 1099 | "TestDashfunctionLambdaVersionA6M23sE6AN9SgN5IQgI9bd1tqh7YgxtybZ9LOhkLY4": Object { 1100 | "DeletionPolicy": "Retain", 1101 | "Properties": Object { 1102 | "CodeSha256": "gxQ2/ARVAXYSjz4OF5PnsOiOB+yUlXG8z5y5h6bNs7U=", 1103 | "FunctionName": Object { 1104 | "Ref": "TestDashfunctionLambdaFunction", 1105 | }, 1106 | }, 1107 | "Type": "AWS::Lambda::Version", 1108 | }, 1109 | "TestDashfunctionLogGroup": Object { 1110 | "Properties": Object { 1111 | "LogGroupName": "/aws/lambda/test-service-dev-test-test-function", 1112 | }, 1113 | "Type": "AWS::Logs::LogGroup", 1114 | }, 1115 | "some-nameQueue": Object { 1116 | "Properties": Object { 1117 | "QueueName": "test-service-dev-test-Test-functionsome-nameQueue", 1118 | }, 1119 | "Type": "AWS::SQS::Queue", 1120 | }, 1121 | "some-nameQueuePolicy": Object { 1122 | "Properties": Object { 1123 | "PolicyDocument": Object { 1124 | "Id": "test-service-dev-test-Test-functionsome-nameQueue", 1125 | "Statement": Array [ 1126 | Object { 1127 | "Action": "SQS:SendMessage", 1128 | "Condition": Object { 1129 | "ArnEquals": Object { 1130 | "aws:SourceArn": Array [ 1131 | "arn:aws:sns:us-east-2:123456789012:MyTopic", 1132 | ], 1133 | }, 1134 | }, 1135 | "Effect": "Allow", 1136 | "Principal": Object { 1137 | "Service": "sns.amazonaws.com", 1138 | }, 1139 | "Resource": Object { 1140 | "Fn::GetAtt": Array [ 1141 | "some-nameQueue", 1142 | "Arn", 1143 | ], 1144 | }, 1145 | "Sid": "test-service-dev-test-Test-functionsome-nameSid", 1146 | }, 1147 | ], 1148 | "Version": "2012-10-17", 1149 | }, 1150 | "Queues": Array [ 1151 | Object { 1152 | "Ref": "some-nameQueue", 1153 | }, 1154 | ], 1155 | }, 1156 | "Type": "AWS::SQS::QueuePolicy", 1157 | }, 1158 | }, 1159 | } 1160 | `; 1161 | 1162 | exports[`Test Serverless SNS SQS Lambda when the provider is specified via a command line option when encryption parameters are not provided should produce valid SQS CF template items 1`] = ` 1163 | Object { 1164 | "AWSTemplateFormatVersion": "2010-09-09", 1165 | "Description": "The AWS CloudFormation template for this Serverless application", 1166 | "Outputs": Object { 1167 | "ServerlessDeploymentBucketName": Object { 1168 | "Export": Object { 1169 | "Name": "sls-test-service-dev-test-ServerlessDeploymentBucketName", 1170 | }, 1171 | "Value": Object { 1172 | "Ref": "ServerlessDeploymentBucket", 1173 | }, 1174 | }, 1175 | "TestDashfunctionLambdaFunctionQualifiedArn": Object { 1176 | "Description": "Current Lambda function version", 1177 | "Export": Object { 1178 | "Name": "sls-test-service-dev-test-TestDashfunctionLambdaFunctionQualifiedArn", 1179 | }, 1180 | "Value": Object { 1181 | "Ref": "TestDashfunctionLambdaVersionA6M23sE6AN9SgN5IQgI9bd1tqh7YgxtybZ9LOhkLY4", 1182 | }, 1183 | }, 1184 | }, 1185 | "Resources": Object { 1186 | "IamRoleLambdaExecution": Object { 1187 | "Properties": Object { 1188 | "AssumeRolePolicyDocument": Object { 1189 | "Statement": Array [ 1190 | Object { 1191 | "Action": Array [ 1192 | "sts:AssumeRole", 1193 | ], 1194 | "Effect": "Allow", 1195 | "Principal": Object { 1196 | "Service": Array [ 1197 | "lambda.amazonaws.com", 1198 | ], 1199 | }, 1200 | }, 1201 | ], 1202 | "Version": "2012-10-17", 1203 | }, 1204 | "Path": "/", 1205 | "Policies": Array [ 1206 | Object { 1207 | "PolicyDocument": Object { 1208 | "Statement": Array [ 1209 | Object { 1210 | "Action": Array [ 1211 | "logs:CreateLogStream", 1212 | "logs:CreateLogGroup", 1213 | ], 1214 | "Effect": "Allow", 1215 | "Resource": Array [ 1216 | Object { 1217 | "Fn::Sub": "arn:\${AWS::Partition}:logs:\${AWS::Region}:\${AWS::AccountId}:log-group:/aws/lambda/test-service-dev-test*:*", 1218 | }, 1219 | ], 1220 | }, 1221 | Object { 1222 | "Action": Array [ 1223 | "logs:PutLogEvents", 1224 | ], 1225 | "Effect": "Allow", 1226 | "Resource": Array [ 1227 | Object { 1228 | "Fn::Sub": "arn:\${AWS::Partition}:logs:\${AWS::Region}:\${AWS::AccountId}:log-group:/aws/lambda/test-service-dev-test*:*:*", 1229 | }, 1230 | ], 1231 | }, 1232 | Object { 1233 | "Action": Array [ 1234 | "sqs:ReceiveMessage", 1235 | "sqs:DeleteMessage", 1236 | "sqs:GetQueueAttributes", 1237 | ], 1238 | "Effect": "Allow", 1239 | "Resource": Array [ 1240 | Object { 1241 | "Fn::GetAtt": Array [ 1242 | "some-nameQueue", 1243 | "Arn", 1244 | ], 1245 | }, 1246 | Object { 1247 | "Fn::GetAtt": Array [ 1248 | "some-nameDeadLetterQueue", 1249 | "Arn", 1250 | ], 1251 | }, 1252 | ], 1253 | }, 1254 | ], 1255 | "Version": "2012-10-17", 1256 | }, 1257 | "PolicyName": Object { 1258 | "Fn::Join": Array [ 1259 | "-", 1260 | Array [ 1261 | "test-service", 1262 | "dev-test", 1263 | "lambda", 1264 | ], 1265 | ], 1266 | }, 1267 | }, 1268 | ], 1269 | "RoleName": Object { 1270 | "Fn::Join": Array [ 1271 | "-", 1272 | Array [ 1273 | "test-service", 1274 | "dev-test", 1275 | Object { 1276 | "Ref": "AWS::Region", 1277 | }, 1278 | "lambdaRole", 1279 | ], 1280 | ], 1281 | }, 1282 | }, 1283 | "Type": "AWS::IAM::Role", 1284 | }, 1285 | "ServerlessDeploymentBucket": Object { 1286 | "Properties": Object { 1287 | "BucketEncryption": Object { 1288 | "ServerSideEncryptionConfiguration": Array [ 1289 | Object { 1290 | "ServerSideEncryptionByDefault": Object { 1291 | "SSEAlgorithm": "AES256", 1292 | }, 1293 | }, 1294 | ], 1295 | }, 1296 | }, 1297 | "Type": "AWS::S3::Bucket", 1298 | }, 1299 | "ServerlessDeploymentBucketPolicy": Object { 1300 | "Properties": Object { 1301 | "Bucket": Object { 1302 | "Ref": "ServerlessDeploymentBucket", 1303 | }, 1304 | "PolicyDocument": Object { 1305 | "Statement": Array [ 1306 | Object { 1307 | "Action": "s3:*", 1308 | "Condition": Object { 1309 | "Bool": Object { 1310 | "aws:SecureTransport": false, 1311 | }, 1312 | }, 1313 | "Effect": "Deny", 1314 | "Principal": "*", 1315 | "Resource": Array [ 1316 | Object { 1317 | "Fn::Join": Array [ 1318 | "", 1319 | Array [ 1320 | "arn:", 1321 | Object { 1322 | "Ref": "AWS::Partition", 1323 | }, 1324 | ":s3:::", 1325 | Object { 1326 | "Ref": "ServerlessDeploymentBucket", 1327 | }, 1328 | "/*", 1329 | ], 1330 | ], 1331 | }, 1332 | Object { 1333 | "Fn::Join": Array [ 1334 | "", 1335 | Array [ 1336 | "arn:", 1337 | Object { 1338 | "Ref": "AWS::Partition", 1339 | }, 1340 | ":s3:::", 1341 | Object { 1342 | "Ref": "ServerlessDeploymentBucket", 1343 | }, 1344 | ], 1345 | ], 1346 | }, 1347 | ], 1348 | }, 1349 | ], 1350 | }, 1351 | }, 1352 | "Type": "AWS::S3::BucketPolicy", 1353 | }, 1354 | "Subscribesome-nameTopic": Object { 1355 | "Properties": Object { 1356 | "Endpoint": Object { 1357 | "Fn::GetAtt": Array [ 1358 | "some-nameQueue", 1359 | "Arn", 1360 | ], 1361 | }, 1362 | "Protocol": "sqs", 1363 | "RawMessageDelivery": false, 1364 | "TopicArn": "arn:aws:sns:us-east-2:123456789012:MyTopic", 1365 | }, 1366 | "Type": "AWS::SNS::Subscription", 1367 | }, 1368 | "Test-functionEventSourceMappingSQSsome-nameQueue": Object { 1369 | "Properties": Object { 1370 | "BatchSize": 10, 1371 | "Enabled": "True", 1372 | "EventSourceArn": Object { 1373 | "Fn::GetAtt": Array [ 1374 | "some-nameQueue", 1375 | "Arn", 1376 | ], 1377 | }, 1378 | "FunctionName": Object { 1379 | "Fn::GetAtt": Array [ 1380 | "Test-functionLambdaFunction", 1381 | "Arn", 1382 | ], 1383 | }, 1384 | "MaximumBatchingWindowInSeconds": 0, 1385 | }, 1386 | "Type": "AWS::Lambda::EventSourceMapping", 1387 | }, 1388 | "TestDashfunctionLambdaFunction": Object { 1389 | "DependsOn": Array [ 1390 | "TestDashfunctionLogGroup", 1391 | ], 1392 | "Properties": Object { 1393 | "Code": Object { 1394 | "S3Bucket": Object { 1395 | "Ref": "ServerlessDeploymentBucket", 1396 | }, 1397 | "S3Key": Any, 1398 | }, 1399 | "FunctionName": "test-service-dev-test-test-function", 1400 | "Handler": "handler.handler", 1401 | "MemorySize": 1024, 1402 | "Role": Object { 1403 | "Fn::GetAtt": Array [ 1404 | "IamRoleLambdaExecution", 1405 | "Arn", 1406 | ], 1407 | }, 1408 | "Runtime": "nodejs14.x", 1409 | "Timeout": 6, 1410 | }, 1411 | "Type": "AWS::Lambda::Function", 1412 | }, 1413 | "TestDashfunctionLambdaVersionA6M23sE6AN9SgN5IQgI9bd1tqh7YgxtybZ9LOhkLY4": Object { 1414 | "DeletionPolicy": "Retain", 1415 | "Properties": Object { 1416 | "CodeSha256": "gxQ2/ARVAXYSjz4OF5PnsOiOB+yUlXG8z5y5h6bNs7U=", 1417 | "FunctionName": Object { 1418 | "Ref": "TestDashfunctionLambdaFunction", 1419 | }, 1420 | }, 1421 | "Type": "AWS::Lambda::Version", 1422 | }, 1423 | "TestDashfunctionLogGroup": Object { 1424 | "Properties": Object { 1425 | "LogGroupName": "/aws/lambda/test-service-dev-test-test-function", 1426 | }, 1427 | "Type": "AWS::Logs::LogGroup", 1428 | }, 1429 | "some-nameDeadLetterQueue": Object { 1430 | "Properties": Object { 1431 | "QueueName": "some prefixsome-nameDeadLetterQueue", 1432 | }, 1433 | "Type": "AWS::SQS::Queue", 1434 | }, 1435 | "some-nameQueue": Object { 1436 | "Properties": Object { 1437 | "QueueName": "some prefixsome-nameQueue", 1438 | "RedrivePolicy": Object { 1439 | "deadLetterTargetArn": Object { 1440 | "Fn::GetAtt": Array [ 1441 | "some-nameDeadLetterQueue", 1442 | "Arn", 1443 | ], 1444 | }, 1445 | "maxReceiveCount": 4, 1446 | }, 1447 | }, 1448 | "Type": "AWS::SQS::Queue", 1449 | }, 1450 | "some-nameQueuePolicy": Object { 1451 | "Properties": Object { 1452 | "PolicyDocument": Object { 1453 | "Id": "some prefixsome-nameQueue", 1454 | "Statement": Array [ 1455 | Object { 1456 | "Action": "SQS:SendMessage", 1457 | "Condition": Object { 1458 | "ArnEquals": Object { 1459 | "aws:SourceArn": Array [ 1460 | "arn:aws:sns:us-east-2:123456789012:MyTopic", 1461 | ], 1462 | }, 1463 | }, 1464 | "Effect": "Allow", 1465 | "Principal": Object { 1466 | "Service": "sns.amazonaws.com", 1467 | }, 1468 | "Resource": Object { 1469 | "Fn::GetAtt": Array [ 1470 | "some-nameQueue", 1471 | "Arn", 1472 | ], 1473 | }, 1474 | "Sid": "some prefixsome-nameSid", 1475 | }, 1476 | ], 1477 | "Version": "2012-10-17", 1478 | }, 1479 | "Queues": Array [ 1480 | Object { 1481 | "Ref": "some-nameQueue", 1482 | }, 1483 | ], 1484 | }, 1485 | "Type": "AWS::SQS::QueuePolicy", 1486 | }, 1487 | }, 1488 | } 1489 | `; 1490 | 1491 | exports[`Test Serverless SNS SQS Lambda when the provider is specified via a command line option when fifo is true should produce valid fifo queues 1`] = ` 1492 | Object { 1493 | "AWSTemplateFormatVersion": "2010-09-09", 1494 | "Description": "The AWS CloudFormation template for this Serverless application", 1495 | "Outputs": Object { 1496 | "ServerlessDeploymentBucketName": Object { 1497 | "Export": Object { 1498 | "Name": "sls-test-service-dev-test-ServerlessDeploymentBucketName", 1499 | }, 1500 | "Value": Object { 1501 | "Ref": "ServerlessDeploymentBucket", 1502 | }, 1503 | }, 1504 | "TestDashfunctionLambdaFunctionQualifiedArn": Object { 1505 | "Description": "Current Lambda function version", 1506 | "Export": Object { 1507 | "Name": "sls-test-service-dev-test-TestDashfunctionLambdaFunctionQualifiedArn", 1508 | }, 1509 | "Value": Object { 1510 | "Ref": "TestDashfunctionLambdaVersionA6M23sE6AN9SgN5IQgI9bd1tqh7YgxtybZ9LOhkLY4", 1511 | }, 1512 | }, 1513 | }, 1514 | "Resources": Object { 1515 | "IamRoleLambdaExecution": Object { 1516 | "Properties": Object { 1517 | "AssumeRolePolicyDocument": Object { 1518 | "Statement": Array [ 1519 | Object { 1520 | "Action": Array [ 1521 | "sts:AssumeRole", 1522 | ], 1523 | "Effect": "Allow", 1524 | "Principal": Object { 1525 | "Service": Array [ 1526 | "lambda.amazonaws.com", 1527 | ], 1528 | }, 1529 | }, 1530 | ], 1531 | "Version": "2012-10-17", 1532 | }, 1533 | "Path": "/", 1534 | "Policies": Array [ 1535 | Object { 1536 | "PolicyDocument": Object { 1537 | "Statement": Array [ 1538 | Object { 1539 | "Action": Array [ 1540 | "logs:CreateLogStream", 1541 | "logs:CreateLogGroup", 1542 | ], 1543 | "Effect": "Allow", 1544 | "Resource": Array [ 1545 | Object { 1546 | "Fn::Sub": "arn:\${AWS::Partition}:logs:\${AWS::Region}:\${AWS::AccountId}:log-group:/aws/lambda/test-service-dev-test*:*", 1547 | }, 1548 | ], 1549 | }, 1550 | Object { 1551 | "Action": Array [ 1552 | "logs:PutLogEvents", 1553 | ], 1554 | "Effect": "Allow", 1555 | "Resource": Array [ 1556 | Object { 1557 | "Fn::Sub": "arn:\${AWS::Partition}:logs:\${AWS::Region}:\${AWS::AccountId}:log-group:/aws/lambda/test-service-dev-test*:*:*", 1558 | }, 1559 | ], 1560 | }, 1561 | Object { 1562 | "Action": Array [ 1563 | "sqs:ReceiveMessage", 1564 | "sqs:DeleteMessage", 1565 | "sqs:GetQueueAttributes", 1566 | ], 1567 | "Effect": "Allow", 1568 | "Resource": Array [ 1569 | Object { 1570 | "Fn::GetAtt": Array [ 1571 | "some-nameQueue", 1572 | "Arn", 1573 | ], 1574 | }, 1575 | Object { 1576 | "Fn::GetAtt": Array [ 1577 | "some-nameDeadLetterQueue", 1578 | "Arn", 1579 | ], 1580 | }, 1581 | ], 1582 | }, 1583 | ], 1584 | "Version": "2012-10-17", 1585 | }, 1586 | "PolicyName": Object { 1587 | "Fn::Join": Array [ 1588 | "-", 1589 | Array [ 1590 | "test-service", 1591 | "dev-test", 1592 | "lambda", 1593 | ], 1594 | ], 1595 | }, 1596 | }, 1597 | ], 1598 | "RoleName": Object { 1599 | "Fn::Join": Array [ 1600 | "-", 1601 | Array [ 1602 | "test-service", 1603 | "dev-test", 1604 | Object { 1605 | "Ref": "AWS::Region", 1606 | }, 1607 | "lambdaRole", 1608 | ], 1609 | ], 1610 | }, 1611 | }, 1612 | "Type": "AWS::IAM::Role", 1613 | }, 1614 | "ServerlessDeploymentBucket": Object { 1615 | "Properties": Object { 1616 | "BucketEncryption": Object { 1617 | "ServerSideEncryptionConfiguration": Array [ 1618 | Object { 1619 | "ServerSideEncryptionByDefault": Object { 1620 | "SSEAlgorithm": "AES256", 1621 | }, 1622 | }, 1623 | ], 1624 | }, 1625 | }, 1626 | "Type": "AWS::S3::Bucket", 1627 | }, 1628 | "ServerlessDeploymentBucketPolicy": Object { 1629 | "Properties": Object { 1630 | "Bucket": Object { 1631 | "Ref": "ServerlessDeploymentBucket", 1632 | }, 1633 | "PolicyDocument": Object { 1634 | "Statement": Array [ 1635 | Object { 1636 | "Action": "s3:*", 1637 | "Condition": Object { 1638 | "Bool": Object { 1639 | "aws:SecureTransport": false, 1640 | }, 1641 | }, 1642 | "Effect": "Deny", 1643 | "Principal": "*", 1644 | "Resource": Array [ 1645 | Object { 1646 | "Fn::Join": Array [ 1647 | "", 1648 | Array [ 1649 | "arn:", 1650 | Object { 1651 | "Ref": "AWS::Partition", 1652 | }, 1653 | ":s3:::", 1654 | Object { 1655 | "Ref": "ServerlessDeploymentBucket", 1656 | }, 1657 | "/*", 1658 | ], 1659 | ], 1660 | }, 1661 | Object { 1662 | "Fn::Join": Array [ 1663 | "", 1664 | Array [ 1665 | "arn:", 1666 | Object { 1667 | "Ref": "AWS::Partition", 1668 | }, 1669 | ":s3:::", 1670 | Object { 1671 | "Ref": "ServerlessDeploymentBucket", 1672 | }, 1673 | ], 1674 | ], 1675 | }, 1676 | ], 1677 | }, 1678 | ], 1679 | }, 1680 | }, 1681 | "Type": "AWS::S3::BucketPolicy", 1682 | }, 1683 | "Subscribesome-nameTopic": Object { 1684 | "Properties": Object { 1685 | "Endpoint": Object { 1686 | "Fn::GetAtt": Array [ 1687 | "some-nameQueue", 1688 | "Arn", 1689 | ], 1690 | }, 1691 | "Protocol": "sqs", 1692 | "RawMessageDelivery": false, 1693 | "TopicArn": "arn:aws:sns:us-east-2:123456789012:MyTopic", 1694 | }, 1695 | "Type": "AWS::SNS::Subscription", 1696 | }, 1697 | "Test-functionEventSourceMappingSQSsome-nameQueue": Object { 1698 | "Properties": Object { 1699 | "BatchSize": 10, 1700 | "Enabled": "True", 1701 | "EventSourceArn": Object { 1702 | "Fn::GetAtt": Array [ 1703 | "some-nameQueue", 1704 | "Arn", 1705 | ], 1706 | }, 1707 | "FunctionName": Object { 1708 | "Fn::GetAtt": Array [ 1709 | "Test-functionLambdaFunction", 1710 | "Arn", 1711 | ], 1712 | }, 1713 | "MaximumBatchingWindowInSeconds": 0, 1714 | }, 1715 | "Type": "AWS::Lambda::EventSourceMapping", 1716 | }, 1717 | "TestDashfunctionLambdaFunction": Object { 1718 | "DependsOn": Array [ 1719 | "TestDashfunctionLogGroup", 1720 | ], 1721 | "Properties": Object { 1722 | "Code": Object { 1723 | "S3Bucket": Object { 1724 | "Ref": "ServerlessDeploymentBucket", 1725 | }, 1726 | "S3Key": Any, 1727 | }, 1728 | "FunctionName": "test-service-dev-test-test-function", 1729 | "Handler": "handler.handler", 1730 | "MemorySize": 1024, 1731 | "Role": Object { 1732 | "Fn::GetAtt": Array [ 1733 | "IamRoleLambdaExecution", 1734 | "Arn", 1735 | ], 1736 | }, 1737 | "Runtime": "nodejs14.x", 1738 | "Timeout": 6, 1739 | }, 1740 | "Type": "AWS::Lambda::Function", 1741 | }, 1742 | "TestDashfunctionLambdaVersionA6M23sE6AN9SgN5IQgI9bd1tqh7YgxtybZ9LOhkLY4": Object { 1743 | "DeletionPolicy": "Retain", 1744 | "Properties": Object { 1745 | "CodeSha256": "gxQ2/ARVAXYSjz4OF5PnsOiOB+yUlXG8z5y5h6bNs7U=", 1746 | "FunctionName": Object { 1747 | "Ref": "TestDashfunctionLambdaFunction", 1748 | }, 1749 | }, 1750 | "Type": "AWS::Lambda::Version", 1751 | }, 1752 | "TestDashfunctionLogGroup": Object { 1753 | "Properties": Object { 1754 | "LogGroupName": "/aws/lambda/test-service-dev-test-test-function", 1755 | }, 1756 | "Type": "AWS::Logs::LogGroup", 1757 | }, 1758 | "some-nameDeadLetterQueue": Object { 1759 | "Properties": Object { 1760 | "FifoQueue": true, 1761 | "QueueName": "test-service-dev-test-Test-functionsome-nameDeadLetterQueue.fifo", 1762 | }, 1763 | "Type": "AWS::SQS::Queue", 1764 | }, 1765 | "some-nameQueue": Object { 1766 | "Properties": Object { 1767 | "FifoQueue": true, 1768 | "QueueName": "test-service-dev-test-Test-functionsome-nameQueue.fifo", 1769 | "RedrivePolicy": Object { 1770 | "deadLetterTargetArn": Object { 1771 | "Fn::GetAtt": Array [ 1772 | "some-nameDeadLetterQueue", 1773 | "Arn", 1774 | ], 1775 | }, 1776 | "maxReceiveCount": 5, 1777 | }, 1778 | }, 1779 | "Type": "AWS::SQS::Queue", 1780 | }, 1781 | "some-nameQueuePolicy": Object { 1782 | "Properties": Object { 1783 | "PolicyDocument": Object { 1784 | "Id": "test-service-dev-test-Test-functionsome-nameQueue", 1785 | "Statement": Array [ 1786 | Object { 1787 | "Action": "SQS:SendMessage", 1788 | "Condition": Object { 1789 | "ArnEquals": Object { 1790 | "aws:SourceArn": Array [ 1791 | "arn:aws:sns:us-east-2:123456789012:MyTopic", 1792 | ], 1793 | }, 1794 | }, 1795 | "Effect": "Allow", 1796 | "Principal": Object { 1797 | "Service": "sns.amazonaws.com", 1798 | }, 1799 | "Resource": Object { 1800 | "Fn::GetAtt": Array [ 1801 | "some-nameQueue", 1802 | "Arn", 1803 | ], 1804 | }, 1805 | "Sid": "test-service-dev-test-Test-functionsome-nameSid", 1806 | }, 1807 | ], 1808 | "Version": "2012-10-17", 1809 | }, 1810 | "Queues": Array [ 1811 | Object { 1812 | "Ref": "some-nameQueue", 1813 | }, 1814 | ], 1815 | }, 1816 | "Type": "AWS::SQS::QueuePolicy", 1817 | }, 1818 | }, 1819 | } 1820 | `; 1821 | 1822 | exports[`Test Serverless SNS SQS Lambda when the provider is specified via a command line option when no optional parameters are provided should produce valid SQS CF template items 1`] = ` 1823 | Object { 1824 | "AWSTemplateFormatVersion": "2010-09-09", 1825 | "Description": "The AWS CloudFormation template for this Serverless application", 1826 | "Outputs": Object { 1827 | "ServerlessDeploymentBucketName": Object { 1828 | "Export": Object { 1829 | "Name": "sls-test-service-dev-test-ServerlessDeploymentBucketName", 1830 | }, 1831 | "Value": Object { 1832 | "Ref": "ServerlessDeploymentBucket", 1833 | }, 1834 | }, 1835 | "TestDashfunctionLambdaFunctionQualifiedArn": Object { 1836 | "Description": "Current Lambda function version", 1837 | "Export": Object { 1838 | "Name": "sls-test-service-dev-test-TestDashfunctionLambdaFunctionQualifiedArn", 1839 | }, 1840 | "Value": Object { 1841 | "Ref": "TestDashfunctionLambdaVersionA6M23sE6AN9SgN5IQgI9bd1tqh7YgxtybZ9LOhkLY4", 1842 | }, 1843 | }, 1844 | }, 1845 | "Resources": Object { 1846 | "IamRoleLambdaExecution": Object { 1847 | "Properties": Object { 1848 | "AssumeRolePolicyDocument": Object { 1849 | "Statement": Array [ 1850 | Object { 1851 | "Action": Array [ 1852 | "sts:AssumeRole", 1853 | ], 1854 | "Effect": "Allow", 1855 | "Principal": Object { 1856 | "Service": Array [ 1857 | "lambda.amazonaws.com", 1858 | ], 1859 | }, 1860 | }, 1861 | ], 1862 | "Version": "2012-10-17", 1863 | }, 1864 | "Path": "/", 1865 | "Policies": Array [ 1866 | Object { 1867 | "PolicyDocument": Object { 1868 | "Statement": Array [ 1869 | Object { 1870 | "Action": Array [ 1871 | "logs:CreateLogStream", 1872 | "logs:CreateLogGroup", 1873 | ], 1874 | "Effect": "Allow", 1875 | "Resource": Array [ 1876 | Object { 1877 | "Fn::Sub": "arn:\${AWS::Partition}:logs:\${AWS::Region}:\${AWS::AccountId}:log-group:/aws/lambda/test-service-dev-test*:*", 1878 | }, 1879 | ], 1880 | }, 1881 | Object { 1882 | "Action": Array [ 1883 | "logs:PutLogEvents", 1884 | ], 1885 | "Effect": "Allow", 1886 | "Resource": Array [ 1887 | Object { 1888 | "Fn::Sub": "arn:\${AWS::Partition}:logs:\${AWS::Region}:\${AWS::AccountId}:log-group:/aws/lambda/test-service-dev-test*:*:*", 1889 | }, 1890 | ], 1891 | }, 1892 | Object { 1893 | "Action": Array [ 1894 | "sqs:ReceiveMessage", 1895 | "sqs:DeleteMessage", 1896 | "sqs:GetQueueAttributes", 1897 | ], 1898 | "Effect": "Allow", 1899 | "Resource": Array [ 1900 | Object { 1901 | "Fn::GetAtt": Array [ 1902 | "some-nameQueue", 1903 | "Arn", 1904 | ], 1905 | }, 1906 | Object { 1907 | "Fn::GetAtt": Array [ 1908 | "some-nameDeadLetterQueue", 1909 | "Arn", 1910 | ], 1911 | }, 1912 | ], 1913 | }, 1914 | ], 1915 | "Version": "2012-10-17", 1916 | }, 1917 | "PolicyName": Object { 1918 | "Fn::Join": Array [ 1919 | "-", 1920 | Array [ 1921 | "test-service", 1922 | "dev-test", 1923 | "lambda", 1924 | ], 1925 | ], 1926 | }, 1927 | }, 1928 | ], 1929 | "RoleName": Object { 1930 | "Fn::Join": Array [ 1931 | "-", 1932 | Array [ 1933 | "test-service", 1934 | "dev-test", 1935 | Object { 1936 | "Ref": "AWS::Region", 1937 | }, 1938 | "lambdaRole", 1939 | ], 1940 | ], 1941 | }, 1942 | }, 1943 | "Type": "AWS::IAM::Role", 1944 | }, 1945 | "ServerlessDeploymentBucket": Object { 1946 | "Properties": Object { 1947 | "BucketEncryption": Object { 1948 | "ServerSideEncryptionConfiguration": Array [ 1949 | Object { 1950 | "ServerSideEncryptionByDefault": Object { 1951 | "SSEAlgorithm": "AES256", 1952 | }, 1953 | }, 1954 | ], 1955 | }, 1956 | }, 1957 | "Type": "AWS::S3::Bucket", 1958 | }, 1959 | "ServerlessDeploymentBucketPolicy": Object { 1960 | "Properties": Object { 1961 | "Bucket": Object { 1962 | "Ref": "ServerlessDeploymentBucket", 1963 | }, 1964 | "PolicyDocument": Object { 1965 | "Statement": Array [ 1966 | Object { 1967 | "Action": "s3:*", 1968 | "Condition": Object { 1969 | "Bool": Object { 1970 | "aws:SecureTransport": false, 1971 | }, 1972 | }, 1973 | "Effect": "Deny", 1974 | "Principal": "*", 1975 | "Resource": Array [ 1976 | Object { 1977 | "Fn::Join": Array [ 1978 | "", 1979 | Array [ 1980 | "arn:", 1981 | Object { 1982 | "Ref": "AWS::Partition", 1983 | }, 1984 | ":s3:::", 1985 | Object { 1986 | "Ref": "ServerlessDeploymentBucket", 1987 | }, 1988 | "/*", 1989 | ], 1990 | ], 1991 | }, 1992 | Object { 1993 | "Fn::Join": Array [ 1994 | "", 1995 | Array [ 1996 | "arn:", 1997 | Object { 1998 | "Ref": "AWS::Partition", 1999 | }, 2000 | ":s3:::", 2001 | Object { 2002 | "Ref": "ServerlessDeploymentBucket", 2003 | }, 2004 | ], 2005 | ], 2006 | }, 2007 | ], 2008 | }, 2009 | ], 2010 | }, 2011 | }, 2012 | "Type": "AWS::S3::BucketPolicy", 2013 | }, 2014 | "Subscribesome-nameTopic": Object { 2015 | "Properties": Object { 2016 | "Endpoint": Object { 2017 | "Fn::GetAtt": Array [ 2018 | "some-nameQueue", 2019 | "Arn", 2020 | ], 2021 | }, 2022 | "Protocol": "sqs", 2023 | "RawMessageDelivery": false, 2024 | "TopicArn": "arn:aws:sns:us-east-2:123456789012:MyTopic", 2025 | }, 2026 | "Type": "AWS::SNS::Subscription", 2027 | }, 2028 | "Test-functionEventSourceMappingSQSsome-nameQueue": Object { 2029 | "Properties": Object { 2030 | "BatchSize": 10, 2031 | "Enabled": "True", 2032 | "EventSourceArn": Object { 2033 | "Fn::GetAtt": Array [ 2034 | "some-nameQueue", 2035 | "Arn", 2036 | ], 2037 | }, 2038 | "FunctionName": Object { 2039 | "Fn::GetAtt": Array [ 2040 | "Test-functionLambdaFunction", 2041 | "Arn", 2042 | ], 2043 | }, 2044 | "MaximumBatchingWindowInSeconds": 0, 2045 | }, 2046 | "Type": "AWS::Lambda::EventSourceMapping", 2047 | }, 2048 | "TestDashfunctionLambdaFunction": Object { 2049 | "DependsOn": Array [ 2050 | "TestDashfunctionLogGroup", 2051 | ], 2052 | "Properties": Object { 2053 | "Code": Object { 2054 | "S3Bucket": Object { 2055 | "Ref": "ServerlessDeploymentBucket", 2056 | }, 2057 | "S3Key": Any, 2058 | }, 2059 | "FunctionName": "test-service-dev-test-test-function", 2060 | "Handler": "handler.handler", 2061 | "MemorySize": 1024, 2062 | "Role": Object { 2063 | "Fn::GetAtt": Array [ 2064 | "IamRoleLambdaExecution", 2065 | "Arn", 2066 | ], 2067 | }, 2068 | "Runtime": "nodejs14.x", 2069 | "Timeout": 6, 2070 | }, 2071 | "Type": "AWS::Lambda::Function", 2072 | }, 2073 | "TestDashfunctionLambdaVersionA6M23sE6AN9SgN5IQgI9bd1tqh7YgxtybZ9LOhkLY4": Object { 2074 | "DeletionPolicy": "Retain", 2075 | "Properties": Object { 2076 | "CodeSha256": "gxQ2/ARVAXYSjz4OF5PnsOiOB+yUlXG8z5y5h6bNs7U=", 2077 | "FunctionName": Object { 2078 | "Ref": "TestDashfunctionLambdaFunction", 2079 | }, 2080 | }, 2081 | "Type": "AWS::Lambda::Version", 2082 | }, 2083 | "TestDashfunctionLogGroup": Object { 2084 | "Properties": Object { 2085 | "LogGroupName": "/aws/lambda/test-service-dev-test-test-function", 2086 | }, 2087 | "Type": "AWS::Logs::LogGroup", 2088 | }, 2089 | "some-nameDeadLetterQueue": Object { 2090 | "Properties": Object { 2091 | "QueueName": "test-service-dev-test-Test-functionsome-nameDeadLetterQueue", 2092 | }, 2093 | "Type": "AWS::SQS::Queue", 2094 | }, 2095 | "some-nameQueue": Object { 2096 | "Properties": Object { 2097 | "QueueName": "test-service-dev-test-Test-functionsome-nameQueue", 2098 | "RedrivePolicy": Object { 2099 | "deadLetterTargetArn": Object { 2100 | "Fn::GetAtt": Array [ 2101 | "some-nameDeadLetterQueue", 2102 | "Arn", 2103 | ], 2104 | }, 2105 | "maxReceiveCount": 5, 2106 | }, 2107 | }, 2108 | "Type": "AWS::SQS::Queue", 2109 | }, 2110 | "some-nameQueuePolicy": Object { 2111 | "Properties": Object { 2112 | "PolicyDocument": Object { 2113 | "Id": "test-service-dev-test-Test-functionsome-nameQueue", 2114 | "Statement": Array [ 2115 | Object { 2116 | "Action": "SQS:SendMessage", 2117 | "Condition": Object { 2118 | "ArnEquals": Object { 2119 | "aws:SourceArn": Array [ 2120 | "arn:aws:sns:us-east-2:123456789012:MyTopic", 2121 | ], 2122 | }, 2123 | }, 2124 | "Effect": "Allow", 2125 | "Principal": Object { 2126 | "Service": "sns.amazonaws.com", 2127 | }, 2128 | "Resource": Object { 2129 | "Fn::GetAtt": Array [ 2130 | "some-nameQueue", 2131 | "Arn", 2132 | ], 2133 | }, 2134 | "Sid": "test-service-dev-test-Test-functionsome-nameSid", 2135 | }, 2136 | ], 2137 | "Version": "2012-10-17", 2138 | }, 2139 | "Queues": Array [ 2140 | Object { 2141 | "Ref": "some-nameQueue", 2142 | }, 2143 | ], 2144 | }, 2145 | "Type": "AWS::SQS::QueuePolicy", 2146 | }, 2147 | }, 2148 | } 2149 | `; 2150 | 2151 | exports[`Test Serverless SNS SQS Lambda when the provider is specified via a command line option when overriding the generated CloudFormation template the overrides should take precedence 1`] = ` 2152 | Object { 2153 | "AWSTemplateFormatVersion": "2010-09-09", 2154 | "Description": "The AWS CloudFormation template for this Serverless application", 2155 | "Outputs": Object { 2156 | "ServerlessDeploymentBucketName": Object { 2157 | "Export": Object { 2158 | "Name": "sls-test-service-dev-test-ServerlessDeploymentBucketName", 2159 | }, 2160 | "Value": Object { 2161 | "Ref": "ServerlessDeploymentBucket", 2162 | }, 2163 | }, 2164 | "TestDashfunctionLambdaFunctionQualifiedArn": Object { 2165 | "Description": "Current Lambda function version", 2166 | "Export": Object { 2167 | "Name": "sls-test-service-dev-test-TestDashfunctionLambdaFunctionQualifiedArn", 2168 | }, 2169 | "Value": Object { 2170 | "Ref": "TestDashfunctionLambdaVersionA6M23sE6AN9SgN5IQgI9bd1tqh7YgxtybZ9LOhkLY4", 2171 | }, 2172 | }, 2173 | }, 2174 | "Resources": Object { 2175 | "IamRoleLambdaExecution": Object { 2176 | "Properties": Object { 2177 | "AssumeRolePolicyDocument": Object { 2178 | "Statement": Array [ 2179 | Object { 2180 | "Action": Array [ 2181 | "sts:AssumeRole", 2182 | ], 2183 | "Effect": "Allow", 2184 | "Principal": Object { 2185 | "Service": Array [ 2186 | "lambda.amazonaws.com", 2187 | ], 2188 | }, 2189 | }, 2190 | ], 2191 | "Version": "2012-10-17", 2192 | }, 2193 | "Path": "/", 2194 | "Policies": Array [ 2195 | Object { 2196 | "PolicyDocument": Object { 2197 | "Statement": Array [ 2198 | Object { 2199 | "Action": Array [ 2200 | "logs:CreateLogStream", 2201 | "logs:CreateLogGroup", 2202 | ], 2203 | "Effect": "Allow", 2204 | "Resource": Array [ 2205 | Object { 2206 | "Fn::Sub": "arn:\${AWS::Partition}:logs:\${AWS::Region}:\${AWS::AccountId}:log-group:/aws/lambda/test-service-dev-test*:*", 2207 | }, 2208 | ], 2209 | }, 2210 | Object { 2211 | "Action": Array [ 2212 | "logs:PutLogEvents", 2213 | ], 2214 | "Effect": "Allow", 2215 | "Resource": Array [ 2216 | Object { 2217 | "Fn::Sub": "arn:\${AWS::Partition}:logs:\${AWS::Region}:\${AWS::AccountId}:log-group:/aws/lambda/test-service-dev-test*:*:*", 2218 | }, 2219 | ], 2220 | }, 2221 | Object { 2222 | "Action": Array [ 2223 | "sqs:ReceiveMessage", 2224 | "sqs:DeleteMessage", 2225 | "sqs:GetQueueAttributes", 2226 | ], 2227 | "Effect": "Allow", 2228 | "Resource": Array [ 2229 | Object { 2230 | "Fn::GetAtt": Array [ 2231 | "some-nameQueue", 2232 | "Arn", 2233 | ], 2234 | }, 2235 | Object { 2236 | "Fn::GetAtt": Array [ 2237 | "some-nameDeadLetterQueue", 2238 | "Arn", 2239 | ], 2240 | }, 2241 | ], 2242 | }, 2243 | ], 2244 | "Version": "2012-10-17", 2245 | }, 2246 | "PolicyName": Object { 2247 | "Fn::Join": Array [ 2248 | "-", 2249 | Array [ 2250 | "test-service", 2251 | "dev-test", 2252 | "lambda", 2253 | ], 2254 | ], 2255 | }, 2256 | }, 2257 | ], 2258 | "RoleName": Object { 2259 | "Fn::Join": Array [ 2260 | "-", 2261 | Array [ 2262 | "test-service", 2263 | "dev-test", 2264 | Object { 2265 | "Ref": "AWS::Region", 2266 | }, 2267 | "lambdaRole", 2268 | ], 2269 | ], 2270 | }, 2271 | }, 2272 | "Type": "AWS::IAM::Role", 2273 | }, 2274 | "ServerlessDeploymentBucket": Object { 2275 | "Properties": Object { 2276 | "BucketEncryption": Object { 2277 | "ServerSideEncryptionConfiguration": Array [ 2278 | Object { 2279 | "ServerSideEncryptionByDefault": Object { 2280 | "SSEAlgorithm": "AES256", 2281 | }, 2282 | }, 2283 | ], 2284 | }, 2285 | }, 2286 | "Type": "AWS::S3::Bucket", 2287 | }, 2288 | "ServerlessDeploymentBucketPolicy": Object { 2289 | "Properties": Object { 2290 | "Bucket": Object { 2291 | "Ref": "ServerlessDeploymentBucket", 2292 | }, 2293 | "PolicyDocument": Object { 2294 | "Statement": Array [ 2295 | Object { 2296 | "Action": "s3:*", 2297 | "Condition": Object { 2298 | "Bool": Object { 2299 | "aws:SecureTransport": false, 2300 | }, 2301 | }, 2302 | "Effect": "Deny", 2303 | "Principal": "*", 2304 | "Resource": Array [ 2305 | Object { 2306 | "Fn::Join": Array [ 2307 | "", 2308 | Array [ 2309 | "arn:", 2310 | Object { 2311 | "Ref": "AWS::Partition", 2312 | }, 2313 | ":s3:::", 2314 | Object { 2315 | "Ref": "ServerlessDeploymentBucket", 2316 | }, 2317 | "/*", 2318 | ], 2319 | ], 2320 | }, 2321 | Object { 2322 | "Fn::Join": Array [ 2323 | "", 2324 | Array [ 2325 | "arn:", 2326 | Object { 2327 | "Ref": "AWS::Partition", 2328 | }, 2329 | ":s3:::", 2330 | Object { 2331 | "Ref": "ServerlessDeploymentBucket", 2332 | }, 2333 | ], 2334 | ], 2335 | }, 2336 | ], 2337 | }, 2338 | ], 2339 | }, 2340 | }, 2341 | "Type": "AWS::S3::BucketPolicy", 2342 | }, 2343 | "Subscribesome-nameTopic": Object { 2344 | "Properties": Object { 2345 | "Endpoint": Object { 2346 | "Fn::GetAtt": Array [ 2347 | "some-nameQueue", 2348 | "Arn", 2349 | ], 2350 | }, 2351 | "Protocol": "sqs", 2352 | "RawMessageDelivery": false, 2353 | "TopicArn": "arn:aws:sns:us-east-2:123456789012:MyTopic", 2354 | }, 2355 | "Type": "AWS::SNS::Subscription", 2356 | }, 2357 | "Test-functionEventSourceMappingSQSsome-nameQueue": Object { 2358 | "Properties": Object { 2359 | "BatchSize": 10, 2360 | "Enabled": false, 2361 | "EventSourceArn": Object { 2362 | "Fn::GetAtt": Array [ 2363 | "some-nameQueue", 2364 | "Arn", 2365 | ], 2366 | }, 2367 | "FunctionName": Object { 2368 | "Fn::GetAtt": Array [ 2369 | "Test-functionLambdaFunction", 2370 | "Arn", 2371 | ], 2372 | }, 2373 | "MaximumBatchingWindowInSeconds": 0, 2374 | }, 2375 | "Type": "AWS::Lambda::EventSourceMapping", 2376 | }, 2377 | "TestDashfunctionLambdaFunction": Object { 2378 | "DependsOn": Array [ 2379 | "TestDashfunctionLogGroup", 2380 | ], 2381 | "Properties": Object { 2382 | "Code": Object { 2383 | "S3Bucket": Object { 2384 | "Ref": "ServerlessDeploymentBucket", 2385 | }, 2386 | "S3Key": Any, 2387 | }, 2388 | "FunctionName": "test-service-dev-test-test-function", 2389 | "Handler": "handler.handler", 2390 | "MemorySize": 1024, 2391 | "Role": Object { 2392 | "Fn::GetAtt": Array [ 2393 | "IamRoleLambdaExecution", 2394 | "Arn", 2395 | ], 2396 | }, 2397 | "Runtime": "nodejs14.x", 2398 | "Timeout": 6, 2399 | }, 2400 | "Type": "AWS::Lambda::Function", 2401 | }, 2402 | "TestDashfunctionLambdaVersionA6M23sE6AN9SgN5IQgI9bd1tqh7YgxtybZ9LOhkLY4": Object { 2403 | "DeletionPolicy": "Retain", 2404 | "Properties": Object { 2405 | "CodeSha256": "gxQ2/ARVAXYSjz4OF5PnsOiOB+yUlXG8z5y5h6bNs7U=", 2406 | "FunctionName": Object { 2407 | "Ref": "TestDashfunctionLambdaFunction", 2408 | }, 2409 | }, 2410 | "Type": "AWS::Lambda::Version", 2411 | }, 2412 | "TestDashfunctionLogGroup": Object { 2413 | "Properties": Object { 2414 | "LogGroupName": "/aws/lambda/test-service-dev-test-test-function", 2415 | }, 2416 | "Type": "AWS::Logs::LogGroup", 2417 | }, 2418 | "some-nameDeadLetterQueue": Object { 2419 | "Properties": Object { 2420 | "MessageRetentionPeriod": 1000, 2421 | "QueueName": "some prefixsome-nameDeadLetterQueue", 2422 | }, 2423 | "Type": "AWS::SQS::Queue", 2424 | }, 2425 | "some-nameQueue": Object { 2426 | "Properties": Object { 2427 | "QueueName": "some prefixsome-nameQueue", 2428 | "RedrivePolicy": Object { 2429 | "deadLetterTargetArn": Object { 2430 | "Fn::GetAtt": Array [ 2431 | "some-nameDeadLetterQueue", 2432 | "Arn", 2433 | ], 2434 | }, 2435 | "maxReceiveCount": 4, 2436 | }, 2437 | "VisibilityTimeout": 4321, 2438 | }, 2439 | "Type": "AWS::SQS::Queue", 2440 | }, 2441 | "some-nameQueuePolicy": Object { 2442 | "Properties": Object { 2443 | "PolicyDocument": Object { 2444 | "Id": "some prefixsome-nameQueue", 2445 | "Statement": Array [ 2446 | Object { 2447 | "Action": "SQS:SendMessage", 2448 | "Condition": Object { 2449 | "ArnEquals": Object { 2450 | "aws:SourceArn": Array [ 2451 | "arn:aws:sns:us-east-2:123456789012:MyTopic", 2452 | ], 2453 | }, 2454 | }, 2455 | "Effect": "Allow", 2456 | "Principal": Object { 2457 | "Service": "sns.amazonaws.com", 2458 | }, 2459 | "Resource": Object { 2460 | "Fn::GetAtt": Array [ 2461 | "some-nameQueue", 2462 | "Arn", 2463 | ], 2464 | }, 2465 | "Sid": "some prefixsome-nameSid", 2466 | }, 2467 | ], 2468 | "Version": "2012-10-17", 2469 | }, 2470 | "Queues": Array [ 2471 | Object { 2472 | "Ref": "some-nameQueue", 2473 | }, 2474 | ], 2475 | }, 2476 | "Type": "AWS::SQS::QueuePolicy", 2477 | }, 2478 | }, 2479 | } 2480 | `; 2481 | 2482 | exports[`Test Serverless SNS SQS Lambda when the provider is specified via a config option in serverless.yml when fifo is true should produce valid fifo queues 1`] = ` 2483 | Object { 2484 | "Resources": Object { 2485 | "IamRoleLambdaExecution": Object { 2486 | "Properties": Object { 2487 | "AssumeRolePolicyDocument": Object { 2488 | "Statement": Array [ 2489 | Object { 2490 | "Action": Array [ 2491 | "sts:AssumeRole", 2492 | ], 2493 | "Effect": "Allow", 2494 | "Principal": Object { 2495 | "Service": Array [ 2496 | "lambda.amazonaws.com", 2497 | ], 2498 | }, 2499 | }, 2500 | ], 2501 | "Version": "2012-10-17", 2502 | }, 2503 | "Path": "/", 2504 | "Policies": Array [ 2505 | Object { 2506 | "PolicyDocument": Object { 2507 | "Statement": Array [ 2508 | Object { 2509 | "Action": Array [ 2510 | "sqs:ReceiveMessage", 2511 | "sqs:DeleteMessage", 2512 | "sqs:GetQueueAttributes", 2513 | ], 2514 | "Effect": "Allow", 2515 | "Resource": Array [ 2516 | Object { 2517 | "Fn::GetAtt": Array [ 2518 | "some-nameQueue", 2519 | "Arn", 2520 | ], 2521 | }, 2522 | Object { 2523 | "Fn::GetAtt": Array [ 2524 | "some-nameDeadLetterQueue", 2525 | "Arn", 2526 | ], 2527 | }, 2528 | ], 2529 | }, 2530 | ], 2531 | "Version": "2012-10-17", 2532 | }, 2533 | "PolicyName": Object { 2534 | "Fn::Join": Array [ 2535 | "-", 2536 | Array [ 2537 | "sns-sqs-service", 2538 | "dev-sd", 2539 | "lambda", 2540 | ], 2541 | ], 2542 | }, 2543 | }, 2544 | ], 2545 | "RoleName": Object { 2546 | "Fn::Join": Array [ 2547 | "-", 2548 | Array [ 2549 | "sns-sqs-service", 2550 | "dev-sd", 2551 | Object { 2552 | "Ref": "AWS::Region", 2553 | }, 2554 | "lambdaRole", 2555 | ], 2556 | ], 2557 | }, 2558 | }, 2559 | "Type": "AWS::IAM::Role", 2560 | }, 2561 | "Subscribesome-nameTopic": Object { 2562 | "Properties": Object { 2563 | "Endpoint": Object { 2564 | "Fn::GetAtt": Array [ 2565 | "some-nameQueue", 2566 | "Arn", 2567 | ], 2568 | }, 2569 | "Protocol": "sqs", 2570 | "RawMessageDelivery": false, 2571 | "TopicArn": "arn:aws:sns:us-east-2:123456789012:MyTopic", 2572 | }, 2573 | "Type": "AWS::SNS::Subscription", 2574 | }, 2575 | "Test-functionEventSourceMappingSQSsome-nameQueue": Object { 2576 | "Properties": Object { 2577 | "BatchSize": 10, 2578 | "Enabled": "True", 2579 | "EventSourceArn": Object { 2580 | "Fn::GetAtt": Array [ 2581 | "some-nameQueue", 2582 | "Arn", 2583 | ], 2584 | }, 2585 | "FunctionName": Object { 2586 | "Fn::GetAtt": Array [ 2587 | "Test-functionLambdaFunction", 2588 | "Arn", 2589 | ], 2590 | }, 2591 | "MaximumBatchingWindowInSeconds": 0, 2592 | }, 2593 | "Type": "AWS::Lambda::EventSourceMapping", 2594 | }, 2595 | "some-nameDeadLetterQueue": Object { 2596 | "Properties": Object { 2597 | "FifoQueue": true, 2598 | "QueueName": "test-service-dev-test-config-Test-functionsome-nameDeadLetterQueue.fifo", 2599 | }, 2600 | "Type": "AWS::SQS::Queue", 2601 | }, 2602 | "some-nameQueue": Object { 2603 | "Properties": Object { 2604 | "FifoQueue": true, 2605 | "QueueName": "test-service-dev-test-config-Test-functionsome-nameQueue.fifo", 2606 | "RedrivePolicy": Object { 2607 | "deadLetterTargetArn": Object { 2608 | "Fn::GetAtt": Array [ 2609 | "some-nameDeadLetterQueue", 2610 | "Arn", 2611 | ], 2612 | }, 2613 | "maxReceiveCount": 5, 2614 | }, 2615 | }, 2616 | "Type": "AWS::SQS::Queue", 2617 | }, 2618 | }, 2619 | } 2620 | `; 2621 | 2622 | exports[`Test Serverless SNS SQS Lambda when the provider is specified via a config option in serverless.yml when no optional parameters are provided should produce valid SQS CF template items 1`] = ` 2623 | Object { 2624 | "Resources": Object { 2625 | "IamRoleLambdaExecution": Object { 2626 | "Properties": Object { 2627 | "AssumeRolePolicyDocument": Object { 2628 | "Statement": Array [ 2629 | Object { 2630 | "Action": Array [ 2631 | "sts:AssumeRole", 2632 | ], 2633 | "Effect": "Allow", 2634 | "Principal": Object { 2635 | "Service": Array [ 2636 | "lambda.amazonaws.com", 2637 | ], 2638 | }, 2639 | }, 2640 | ], 2641 | "Version": "2012-10-17", 2642 | }, 2643 | "Path": "/", 2644 | "Policies": Array [ 2645 | Object { 2646 | "PolicyDocument": Object { 2647 | "Statement": Array [ 2648 | Object { 2649 | "Action": Array [ 2650 | "sqs:ReceiveMessage", 2651 | "sqs:DeleteMessage", 2652 | "sqs:GetQueueAttributes", 2653 | ], 2654 | "Effect": "Allow", 2655 | "Resource": Array [ 2656 | Object { 2657 | "Fn::GetAtt": Array [ 2658 | "some-nameQueue", 2659 | "Arn", 2660 | ], 2661 | }, 2662 | Object { 2663 | "Fn::GetAtt": Array [ 2664 | "some-nameDeadLetterQueue", 2665 | "Arn", 2666 | ], 2667 | }, 2668 | ], 2669 | }, 2670 | ], 2671 | "Version": "2012-10-17", 2672 | }, 2673 | "PolicyName": Object { 2674 | "Fn::Join": Array [ 2675 | "-", 2676 | Array [ 2677 | "sns-sqs-service", 2678 | "dev-sd", 2679 | "lambda", 2680 | ], 2681 | ], 2682 | }, 2683 | }, 2684 | ], 2685 | "RoleName": Object { 2686 | "Fn::Join": Array [ 2687 | "-", 2688 | Array [ 2689 | "sns-sqs-service", 2690 | "dev-sd", 2691 | Object { 2692 | "Ref": "AWS::Region", 2693 | }, 2694 | "lambdaRole", 2695 | ], 2696 | ], 2697 | }, 2698 | }, 2699 | "Type": "AWS::IAM::Role", 2700 | }, 2701 | "Subscribesome-nameTopic": Object { 2702 | "Properties": Object { 2703 | "Endpoint": Object { 2704 | "Fn::GetAtt": Array [ 2705 | "some-nameQueue", 2706 | "Arn", 2707 | ], 2708 | }, 2709 | "Protocol": "sqs", 2710 | "RawMessageDelivery": false, 2711 | "TopicArn": "arn:aws:sns:us-east-2:123456789012:MyTopic", 2712 | }, 2713 | "Type": "AWS::SNS::Subscription", 2714 | }, 2715 | "Test-functionEventSourceMappingSQSsome-nameQueue": Object { 2716 | "Properties": Object { 2717 | "BatchSize": 10, 2718 | "Enabled": "True", 2719 | "EventSourceArn": Object { 2720 | "Fn::GetAtt": Array [ 2721 | "some-nameQueue", 2722 | "Arn", 2723 | ], 2724 | }, 2725 | "FunctionName": Object { 2726 | "Fn::GetAtt": Array [ 2727 | "Test-functionLambdaFunction", 2728 | "Arn", 2729 | ], 2730 | }, 2731 | "MaximumBatchingWindowInSeconds": 0, 2732 | }, 2733 | "Type": "AWS::Lambda::EventSourceMapping", 2734 | }, 2735 | "some-nameDeadLetterQueue": Object { 2736 | "Properties": Object { 2737 | "QueueName": "test-service-dev-test-config-Test-functionsome-nameDeadLetterQueue", 2738 | }, 2739 | "Type": "AWS::SQS::Queue", 2740 | }, 2741 | "some-nameQueue": Object { 2742 | "Properties": Object { 2743 | "QueueName": "test-service-dev-test-config-Test-functionsome-nameQueue", 2744 | "RedrivePolicy": Object { 2745 | "deadLetterTargetArn": Object { 2746 | "Fn::GetAtt": Array [ 2747 | "some-nameDeadLetterQueue", 2748 | "Arn", 2749 | ], 2750 | }, 2751 | "maxReceiveCount": 5, 2752 | }, 2753 | }, 2754 | "Type": "AWS::SQS::Queue", 2755 | }, 2756 | }, 2757 | } 2758 | `; 2759 | 2760 | exports[`Test Serverless SNS SQS Lambda when the provider is specified via a provider option in serverless.yml when fifo is true should produce valid fifo queues 1`] = ` 2761 | Object { 2762 | "Resources": Object { 2763 | "IamRoleLambdaExecution": Object { 2764 | "Properties": Object { 2765 | "AssumeRolePolicyDocument": Object { 2766 | "Statement": Array [ 2767 | Object { 2768 | "Action": Array [ 2769 | "sts:AssumeRole", 2770 | ], 2771 | "Effect": "Allow", 2772 | "Principal": Object { 2773 | "Service": Array [ 2774 | "lambda.amazonaws.com", 2775 | ], 2776 | }, 2777 | }, 2778 | ], 2779 | "Version": "2012-10-17", 2780 | }, 2781 | "Path": "/", 2782 | "Policies": Array [ 2783 | Object { 2784 | "PolicyDocument": Object { 2785 | "Statement": Array [ 2786 | Object { 2787 | "Action": Array [ 2788 | "sqs:ReceiveMessage", 2789 | "sqs:DeleteMessage", 2790 | "sqs:GetQueueAttributes", 2791 | ], 2792 | "Effect": "Allow", 2793 | "Resource": Array [ 2794 | Object { 2795 | "Fn::GetAtt": Array [ 2796 | "some-nameQueue", 2797 | "Arn", 2798 | ], 2799 | }, 2800 | Object { 2801 | "Fn::GetAtt": Array [ 2802 | "some-nameDeadLetterQueue", 2803 | "Arn", 2804 | ], 2805 | }, 2806 | ], 2807 | }, 2808 | ], 2809 | "Version": "2012-10-17", 2810 | }, 2811 | "PolicyName": Object { 2812 | "Fn::Join": Array [ 2813 | "-", 2814 | Array [ 2815 | "sns-sqs-service", 2816 | "dev-sd", 2817 | "lambda", 2818 | ], 2819 | ], 2820 | }, 2821 | }, 2822 | ], 2823 | "RoleName": Object { 2824 | "Fn::Join": Array [ 2825 | "-", 2826 | Array [ 2827 | "sns-sqs-service", 2828 | "dev-sd", 2829 | Object { 2830 | "Ref": "AWS::Region", 2831 | }, 2832 | "lambdaRole", 2833 | ], 2834 | ], 2835 | }, 2836 | }, 2837 | "Type": "AWS::IAM::Role", 2838 | }, 2839 | "Subscribesome-nameTopic": Object { 2840 | "Properties": Object { 2841 | "Endpoint": Object { 2842 | "Fn::GetAtt": Array [ 2843 | "some-nameQueue", 2844 | "Arn", 2845 | ], 2846 | }, 2847 | "Protocol": "sqs", 2848 | "RawMessageDelivery": false, 2849 | "TopicArn": "arn:aws:sns:us-east-2:123456789012:MyTopic", 2850 | }, 2851 | "Type": "AWS::SNS::Subscription", 2852 | }, 2853 | "Test-functionEventSourceMappingSQSsome-nameQueue": Object { 2854 | "Properties": Object { 2855 | "BatchSize": 10, 2856 | "Enabled": "True", 2857 | "EventSourceArn": Object { 2858 | "Fn::GetAtt": Array [ 2859 | "some-nameQueue", 2860 | "Arn", 2861 | ], 2862 | }, 2863 | "FunctionName": Object { 2864 | "Fn::GetAtt": Array [ 2865 | "Test-functionLambdaFunction", 2866 | "Arn", 2867 | ], 2868 | }, 2869 | "MaximumBatchingWindowInSeconds": 0, 2870 | }, 2871 | "Type": "AWS::Lambda::EventSourceMapping", 2872 | }, 2873 | "some-nameDeadLetterQueue": Object { 2874 | "Properties": Object { 2875 | "FifoQueue": true, 2876 | "QueueName": "test-service-dev-test-provider-Test-functionsome-nameDeadLetterQueue.fifo", 2877 | }, 2878 | "Type": "AWS::SQS::Queue", 2879 | }, 2880 | "some-nameQueue": Object { 2881 | "Properties": Object { 2882 | "FifoQueue": true, 2883 | "QueueName": "test-service-dev-test-provider-Test-functionsome-nameQueue.fifo", 2884 | "RedrivePolicy": Object { 2885 | "deadLetterTargetArn": Object { 2886 | "Fn::GetAtt": Array [ 2887 | "some-nameDeadLetterQueue", 2888 | "Arn", 2889 | ], 2890 | }, 2891 | "maxReceiveCount": 5, 2892 | }, 2893 | }, 2894 | "Type": "AWS::SQS::Queue", 2895 | }, 2896 | }, 2897 | } 2898 | `; 2899 | 2900 | exports[`Test Serverless SNS SQS Lambda when the provider is specified via a provider option in serverless.yml when no optional parameters are provided should produce valid SQS CF template items 1`] = ` 2901 | Object { 2902 | "Resources": Object { 2903 | "IamRoleLambdaExecution": Object { 2904 | "Properties": Object { 2905 | "AssumeRolePolicyDocument": Object { 2906 | "Statement": Array [ 2907 | Object { 2908 | "Action": Array [ 2909 | "sts:AssumeRole", 2910 | ], 2911 | "Effect": "Allow", 2912 | "Principal": Object { 2913 | "Service": Array [ 2914 | "lambda.amazonaws.com", 2915 | ], 2916 | }, 2917 | }, 2918 | ], 2919 | "Version": "2012-10-17", 2920 | }, 2921 | "Path": "/", 2922 | "Policies": Array [ 2923 | Object { 2924 | "PolicyDocument": Object { 2925 | "Statement": Array [ 2926 | Object { 2927 | "Action": Array [ 2928 | "sqs:ReceiveMessage", 2929 | "sqs:DeleteMessage", 2930 | "sqs:GetQueueAttributes", 2931 | ], 2932 | "Effect": "Allow", 2933 | "Resource": Array [ 2934 | Object { 2935 | "Fn::GetAtt": Array [ 2936 | "some-nameQueue", 2937 | "Arn", 2938 | ], 2939 | }, 2940 | Object { 2941 | "Fn::GetAtt": Array [ 2942 | "some-nameDeadLetterQueue", 2943 | "Arn", 2944 | ], 2945 | }, 2946 | ], 2947 | }, 2948 | ], 2949 | "Version": "2012-10-17", 2950 | }, 2951 | "PolicyName": Object { 2952 | "Fn::Join": Array [ 2953 | "-", 2954 | Array [ 2955 | "sns-sqs-service", 2956 | "dev-sd", 2957 | "lambda", 2958 | ], 2959 | ], 2960 | }, 2961 | }, 2962 | ], 2963 | "RoleName": Object { 2964 | "Fn::Join": Array [ 2965 | "-", 2966 | Array [ 2967 | "sns-sqs-service", 2968 | "dev-sd", 2969 | Object { 2970 | "Ref": "AWS::Region", 2971 | }, 2972 | "lambdaRole", 2973 | ], 2974 | ], 2975 | }, 2976 | }, 2977 | "Type": "AWS::IAM::Role", 2978 | }, 2979 | "Subscribesome-nameTopic": Object { 2980 | "Properties": Object { 2981 | "Endpoint": Object { 2982 | "Fn::GetAtt": Array [ 2983 | "some-nameQueue", 2984 | "Arn", 2985 | ], 2986 | }, 2987 | "Protocol": "sqs", 2988 | "RawMessageDelivery": false, 2989 | "TopicArn": "arn:aws:sns:us-east-2:123456789012:MyTopic", 2990 | }, 2991 | "Type": "AWS::SNS::Subscription", 2992 | }, 2993 | "Test-functionEventSourceMappingSQSsome-nameQueue": Object { 2994 | "Properties": Object { 2995 | "BatchSize": 10, 2996 | "Enabled": "True", 2997 | "EventSourceArn": Object { 2998 | "Fn::GetAtt": Array [ 2999 | "some-nameQueue", 3000 | "Arn", 3001 | ], 3002 | }, 3003 | "FunctionName": Object { 3004 | "Fn::GetAtt": Array [ 3005 | "Test-functionLambdaFunction", 3006 | "Arn", 3007 | ], 3008 | }, 3009 | "MaximumBatchingWindowInSeconds": 0, 3010 | }, 3011 | "Type": "AWS::Lambda::EventSourceMapping", 3012 | }, 3013 | "some-nameDeadLetterQueue": Object { 3014 | "Properties": Object { 3015 | "QueueName": "test-service-dev-test-provider-Test-functionsome-nameDeadLetterQueue", 3016 | }, 3017 | "Type": "AWS::SQS::Queue", 3018 | }, 3019 | "some-nameQueue": Object { 3020 | "Properties": Object { 3021 | "QueueName": "test-service-dev-test-provider-Test-functionsome-nameQueue", 3022 | "RedrivePolicy": Object { 3023 | "deadLetterTargetArn": Object { 3024 | "Fn::GetAtt": Array [ 3025 | "some-nameDeadLetterQueue", 3026 | "Arn", 3027 | ], 3028 | }, 3029 | "maxReceiveCount": 5, 3030 | }, 3031 | }, 3032 | "Type": "AWS::SQS::Queue", 3033 | }, 3034 | }, 3035 | } 3036 | `; 3037 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | import ServerlessSnsSqsLambda from "./serverless-sns-sqs-lambda"; 2 | 3 | module.exports = ServerlessSnsSqsLambda; 4 | -------------------------------------------------------------------------------- /lib/modules.d.ts: -------------------------------------------------------------------------------- 1 | // I don't think that serverless export these types (although I only had a cursory look) 2 | // We can have a better look as part of https://github.com/agiledigital/serverless-sns-sqs-lambda/issues/313 3 | // Declaring these modules keeps tsc happy (they are implicitly any) 4 | declare module "serverless/lib/classes/cli"; 5 | declare module "serverless/lib/plugins/aws/provider"; 6 | -------------------------------------------------------------------------------- /lib/serverless-sns-sqs-lambda.test.ts: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import CLI from "serverless/lib/classes/cli"; 4 | import Serverless from "serverless/lib/serverless"; 5 | import AwsProvider from "serverless/lib/plugins/aws/provider"; 6 | import ServerlessSnsSqsLambda from "./serverless-sns-sqs-lambda"; 7 | // See https://github.com/serverless/test/blob/71746cd0e0c897de50e19bc96a3968e5f26bee4f/docs/run-serverless.md for more info on run-serverless 8 | import runServerless from "@serverless/test/run-serverless"; 9 | import { join } from "path"; 10 | 11 | const serverlessPath = join(__dirname, "../node_modules/serverless"); 12 | 13 | const slsOpt = { 14 | region: "ap-southeast-2" 15 | }; 16 | 17 | /** 18 | * Returns a resource that looks like what Serverless generates when not using 19 | * a custom execution role ARN. 20 | * 21 | * It would be better to get Serverless to generate this for us but we don't 22 | * run in a serverless context at the moment so this is the best we have. 23 | */ 24 | const generateIamLambdaExecutionRole = () => ({ 25 | IamRoleLambdaExecution: { 26 | Type: "AWS::IAM::Role", 27 | Properties: { 28 | AssumeRolePolicyDocument: { 29 | Version: "2012-10-17", 30 | Statement: [ 31 | { 32 | Effect: "Allow", 33 | Principal: { 34 | Service: ["lambda.amazonaws.com"] 35 | }, 36 | Action: ["sts:AssumeRole"] 37 | } 38 | ] 39 | }, 40 | Policies: [ 41 | { 42 | PolicyName: { 43 | "Fn::Join": ["-", ["sns-sqs-service", "dev-sd", "lambda"]] 44 | }, 45 | PolicyDocument: { 46 | Version: "2012-10-17", 47 | Statement: [] 48 | } 49 | } 50 | ], 51 | Path: "/", 52 | RoleName: { 53 | "Fn::Join": [ 54 | "-", 55 | [ 56 | "sns-sqs-service", 57 | "dev-sd", 58 | { 59 | Ref: "AWS::Region" 60 | }, 61 | "lambdaRole" 62 | ] 63 | ] 64 | } 65 | } 66 | } 67 | }); 68 | 69 | describe("Test Serverless SNS SQS Lambda", () => { 70 | let serverless; 71 | let serverlessSnsSqsLambda; 72 | 73 | afterEach(() => { 74 | jest.resetModules(); // reset modules after each test 75 | }); 76 | 77 | describe("when the provider is specified via a command line option", () => { 78 | const baseConfig = { 79 | service: "test-service", 80 | configValidationMode: "error", 81 | frameworkVersion: "*", 82 | provider: { 83 | ...slsOpt, 84 | name: "aws", 85 | runtime: "nodejs14.x", 86 | stage: "dev-test" 87 | }, 88 | package: { 89 | // This is simply here to prevent serverless from trying to package 90 | // any files. Since the config is generated in unique temp directories 91 | // for each test, there are no files to resolve for packaging 92 | // so providing a "pre-built" artefact with an absolute path 93 | // keeps serverless happy 94 | artifact: require.resolve("./__fixtures__/handler.js") 95 | }, 96 | plugins: [require.resolve("../dist")] 97 | }; 98 | 99 | it("should fail if name is not passed", async () => { 100 | expect.assertions(1); 101 | 102 | await expect(() => 103 | runServerless(serverlessPath, { 104 | command: "package", 105 | config: { 106 | ...baseConfig, 107 | functions: { 108 | processEvent: { 109 | handler: "handler.handler", 110 | events: [ 111 | { 112 | snsSqs: { 113 | topicArn: "arn:aws:sns:us-east-2:123456789012:MyTopic", 114 | name: undefined 115 | } 116 | } 117 | ] 118 | } 119 | } 120 | } 121 | }) 122 | ).rejects.toMatchInlineSnapshot(` 123 | [ServerlessError: Configuration error at 'functions.processEvent.events.0.snsSqs': must have required property 'name' 124 | 125 | Learn more about configuration validation here: http://slss.io/configuration-validation] 126 | `); 127 | }); 128 | 129 | it("should fail if topicArn is not passed", async () => { 130 | expect.assertions(1); 131 | 132 | await expect(() => 133 | runServerless(serverlessPath, { 134 | command: "package", 135 | config: { 136 | ...baseConfig, 137 | functions: { 138 | processEvent: { 139 | handler: "handler.handler", 140 | events: [ 141 | { 142 | snsSqs: { 143 | topicArn: undefined, 144 | name: "some name" 145 | } 146 | } 147 | ] 148 | } 149 | } 150 | } 151 | }) 152 | ).rejects.toMatchInlineSnapshot(` 153 | [ServerlessError: Configuration error at 'functions.processEvent.events.0.snsSqs': must have required property 'topicArn' 154 | 155 | Learn more about configuration validation here: http://slss.io/configuration-validation] 156 | `); 157 | }); 158 | 159 | it("should fail if topicArn is invalid", async () => { 160 | expect.assertions(1); 161 | 162 | await expect(() => 163 | runServerless(serverlessPath, { 164 | command: "package", 165 | config: { 166 | ...baseConfig, 167 | functions: { 168 | processEvent: { 169 | handler: "handler.handler", 170 | events: [ 171 | { 172 | snsSqs: { 173 | topicArn: "not_an_arn", 174 | name: "some name" 175 | } 176 | } 177 | ] 178 | } 179 | } 180 | } 181 | }) 182 | ).rejects.toMatchInlineSnapshot(` 183 | [ServerlessError: Configuration error at 'functions.processEvent.events.0.snsSqs.topicArn': unsupported string format 184 | 185 | Learn more about configuration validation here: http://slss.io/configuration-validation] 186 | `); 187 | }); 188 | 189 | describe("when no optional parameters are provided", () => { 190 | it("should produce valid SQS CF template items", async () => { 191 | const { cfTemplate } = await runServerless(serverlessPath, { 192 | command: "package", 193 | config: { 194 | ...baseConfig, 195 | functions: { 196 | ["test-function"]: { 197 | handler: "handler.handler", 198 | events: [ 199 | { 200 | snsSqs: { 201 | name: "some-name", 202 | topicArn: "arn:aws:sns:us-east-2:123456789012:MyTopic" 203 | } 204 | } 205 | ] 206 | } 207 | } 208 | } 209 | }); 210 | 211 | expect(cfTemplate).toMatchSnapshot({ 212 | Resources: { 213 | TestDashfunctionLambdaFunction: { 214 | Properties: { 215 | Code: { S3Key: expect.any(String) } 216 | } 217 | } 218 | } 219 | }); 220 | }); 221 | }); 222 | 223 | describe("when all parameters are provided", () => { 224 | it("should produce valid SQS CF template items", async () => { 225 | const { cfTemplate } = await runServerless(serverlessPath, { 226 | command: "package", 227 | config: { 228 | ...baseConfig, 229 | functions: { 230 | ["test-function"]: { 231 | handler: "handler.handler", 232 | events: [ 233 | { 234 | snsSqs: { 235 | name: "some-name", 236 | topicArn: "arn:aws:sns:us-east-2:123456789012:MyTopic", 237 | batchSize: 7, 238 | maximumBatchingWindowInSeconds: 99, 239 | prefix: "some prefix", 240 | maxRetryCount: 4, 241 | kmsMasterKeyId: "some key", 242 | kmsDataKeyReusePeriodSeconds: 200, 243 | deadLetterMessageRetentionPeriodSeconds: 1209600, 244 | deadLetterQueueEnabled: true, 245 | enabled: false, 246 | visibilityTimeout: 999, 247 | rawMessageDelivery: true, 248 | filterPolicy: { pet: ["dog", "cat"] } 249 | } 250 | } 251 | ] 252 | } 253 | } 254 | } 255 | }); 256 | 257 | expect(cfTemplate).toMatchSnapshot({ 258 | Resources: { 259 | TestDashfunctionLambdaFunction: { 260 | Properties: { 261 | Code: { S3Key: expect.any(String) } 262 | } 263 | } 264 | } 265 | }); 266 | }); 267 | }); 268 | 269 | describe("when dead letter queue is disabled", () => { 270 | it("should not produce SQS dead letter queue and related IAM policies in CF template", async () => { 271 | const { cfTemplate } = await runServerless(serverlessPath, { 272 | command: "package", 273 | config: { 274 | ...baseConfig, 275 | functions: { 276 | ["test-function"]: { 277 | handler: "handler.handler", 278 | events: [ 279 | { 280 | snsSqs: { 281 | name: "some-name", 282 | topicArn: "arn:aws:sns:us-east-2:123456789012:MyTopic", 283 | deadLetterQueueEnabled: false 284 | } 285 | } 286 | ] 287 | } 288 | } 289 | } 290 | }); 291 | 292 | expect(cfTemplate).toMatchSnapshot({ 293 | Resources: { 294 | TestDashfunctionLambdaFunction: { 295 | Properties: { 296 | Code: { S3Key: expect.any(String) } 297 | } 298 | } 299 | } 300 | }); 301 | }); 302 | }); 303 | 304 | describe("when encryption parameters are not provided", () => { 305 | it("should produce valid SQS CF template items", async () => { 306 | const { cfTemplate } = await runServerless(serverlessPath, { 307 | command: "package", 308 | config: { 309 | ...baseConfig, 310 | functions: { 311 | ["test-function"]: { 312 | handler: "handler.handler", 313 | events: [ 314 | { 315 | snsSqs: { 316 | name: "some-name", 317 | topicArn: "arn:aws:sns:us-east-2:123456789012:MyTopic", 318 | prefix: "some prefix", 319 | maxRetryCount: 4 320 | } 321 | } 322 | ] 323 | } 324 | } 325 | } 326 | }); 327 | 328 | expect(cfTemplate).toMatchSnapshot({ 329 | Resources: { 330 | TestDashfunctionLambdaFunction: { 331 | Properties: { 332 | Code: { S3Key: expect.any(String) } 333 | } 334 | } 335 | } 336 | }); 337 | }); 338 | }); 339 | 340 | describe("when overriding the generated CloudFormation template", () => { 341 | it("the overrides should take precedence", async () => { 342 | const { cfTemplate } = await runServerless(serverlessPath, { 343 | command: "package", 344 | config: { 345 | ...baseConfig, 346 | functions: { 347 | ["test-function"]: { 348 | handler: "handler.handler", 349 | events: [ 350 | { 351 | snsSqs: { 352 | name: "some-name", 353 | topicArn: "arn:aws:sns:us-east-2:123456789012:MyTopic", 354 | prefix: "some prefix", 355 | maxRetryCount: 4, 356 | enabled: true, 357 | visibilityTimeout: 1234, 358 | deadLetterMessageRetentionPeriodSeconds: 120, 359 | rawMessageDelivery: true, 360 | mainQueueOverride: { 361 | visibilityTimeout: 4321 362 | }, 363 | deadLetterQueueOverride: { 364 | MessageRetentionPeriod: 1000 365 | }, 366 | eventSourceMappingOverride: { 367 | Enabled: false 368 | }, 369 | subscriptionOverride: { 370 | rawMessageDelivery: false 371 | } 372 | } 373 | } 374 | ] 375 | } 376 | } 377 | } 378 | }); 379 | 380 | expect(cfTemplate).toMatchSnapshot({ 381 | Resources: { 382 | TestDashfunctionLambdaFunction: { 383 | Properties: { 384 | Code: { S3Key: expect.any(String) } 385 | } 386 | } 387 | } 388 | }); 389 | }); 390 | }); 391 | 392 | describe("when fifo is true", () => { 393 | it("should produce valid fifo queues", async () => { 394 | const { cfTemplate } = await runServerless(serverlessPath, { 395 | command: "package", 396 | config: { 397 | ...baseConfig, 398 | functions: { 399 | ["test-function"]: { 400 | handler: "handler.handler", 401 | events: [ 402 | { 403 | snsSqs: { 404 | name: "some-name", 405 | topicArn: "arn:aws:sns:us-east-2:123456789012:MyTopic", 406 | fifo: true 407 | } 408 | } 409 | ] 410 | } 411 | } 412 | } 413 | }); 414 | 415 | expect(cfTemplate).toMatchSnapshot({ 416 | Resources: { 417 | TestDashfunctionLambdaFunction: { 418 | Properties: { 419 | Code: { S3Key: expect.any(String) } 420 | } 421 | } 422 | } 423 | }); 424 | }); 425 | }); 426 | 427 | describe("when a custom role ARN is specified", () => { 428 | it("it should not crash and just skip creating the policies", async () => { 429 | const { cfTemplate } = await runServerless(serverlessPath, { 430 | command: "package", 431 | config: { 432 | ...baseConfig, 433 | provider: { 434 | ...baseConfig.provider, 435 | iam: { 436 | role: "arn:aws:iam::123456789012:role/execution-role", 437 | deploymentRole: "arn:aws:iam::123456789012:role/deploy-role" 438 | } 439 | }, 440 | functions: { 441 | ["test-function"]: { 442 | handler: "handler.handler", 443 | events: [ 444 | { 445 | snsSqs: { 446 | name: "some-name", 447 | topicArn: "arn:aws:sns:us-east-2:123456789012:MyTopic" 448 | } 449 | } 450 | ] 451 | } 452 | } 453 | } 454 | }); 455 | 456 | expect(cfTemplate).toMatchSnapshot({ 457 | Resources: { 458 | TestDashfunctionLambdaFunction: { 459 | Properties: { 460 | Code: { S3Key: expect.any(String) } 461 | } 462 | } 463 | } 464 | }); 465 | }); 466 | }); 467 | }); 468 | 469 | describe("when the provider is specified via a config option in serverless.yml", () => { 470 | beforeEach(() => { 471 | serverless = new Serverless({ commands: [], options: {} }); 472 | serverless.service.service = "test-service"; 473 | // This should really be a proper instance of the Config class. See also: https://github.com/agiledigital/serverless-sns-sqs-lambda/issues/313 474 | serverless.config = { stage: "dev-test-config" }; 475 | 476 | const options = { 477 | ...slsOpt 478 | }; 479 | 480 | const provider = new AwsProvider(serverless); 481 | serverless.setProvider("aws", provider); 482 | 483 | serverless.cli = new CLI(serverless); 484 | serverlessSnsSqsLambda = new ServerlessSnsSqsLambda(serverless, options); 485 | }); 486 | 487 | describe("when no optional parameters are provided", () => { 488 | it("should produce valid SQS CF template items", () => { 489 | const template = { 490 | Resources: { 491 | ...generateIamLambdaExecutionRole() 492 | } 493 | }; 494 | const testConfig = { 495 | name: "some-name", 496 | topicArn: "arn:aws:sns:us-east-2:123456789012:MyTopic" 497 | }; 498 | const validatedConfig = serverlessSnsSqsLambda.validateConfig( 499 | "test-function", 500 | serverlessSnsSqsLambda.stage, 501 | testConfig 502 | ); 503 | serverlessSnsSqsLambda.addEventQueue(template, validatedConfig); 504 | serverlessSnsSqsLambda.addEventDeadLetterQueue( 505 | template, 506 | validatedConfig 507 | ); 508 | serverlessSnsSqsLambda.addEventSourceMapping(template, validatedConfig); 509 | serverlessSnsSqsLambda.addTopicSubscription(template, validatedConfig); 510 | serverlessSnsSqsLambda.addLambdaSqsPermissions( 511 | template, 512 | validatedConfig 513 | ); 514 | 515 | expect(template).toMatchSnapshot(); 516 | }); 517 | }); 518 | 519 | describe("when fifo is true", () => { 520 | it("should produce valid fifo queues", () => { 521 | const template = { 522 | Resources: { 523 | ...generateIamLambdaExecutionRole() 524 | } 525 | }; 526 | const testConfig = { 527 | name: "some-name", 528 | topicArn: "arn:aws:sns:us-east-2:123456789012:MyTopic", 529 | fifo: true 530 | }; 531 | const validatedConfig = serverlessSnsSqsLambda.validateConfig( 532 | "test-function", 533 | serverlessSnsSqsLambda.stage, 534 | testConfig 535 | ); 536 | serverlessSnsSqsLambda.addEventQueue(template, validatedConfig); 537 | serverlessSnsSqsLambda.addEventDeadLetterQueue( 538 | template, 539 | validatedConfig 540 | ); 541 | serverlessSnsSqsLambda.addEventSourceMapping(template, validatedConfig); 542 | serverlessSnsSqsLambda.addTopicSubscription(template, validatedConfig); 543 | serverlessSnsSqsLambda.addLambdaSqsPermissions( 544 | template, 545 | validatedConfig 546 | ); 547 | 548 | expect(template).toMatchSnapshot(); 549 | }); 550 | }); 551 | }); 552 | 553 | describe("when the provider is specified via a provider option in serverless.yml", () => { 554 | beforeEach(() => { 555 | serverless = new Serverless({ commands: [], options: {} }); 556 | serverless.service.service = "test-service"; 557 | 558 | const options = { 559 | ...slsOpt 560 | }; 561 | 562 | const provider = new AwsProvider(serverless); 563 | serverless.setProvider("aws", provider); 564 | serverless.service.provider.stage = "dev-test-provider"; 565 | 566 | serverless.cli = new CLI(serverless); 567 | serverlessSnsSqsLambda = new ServerlessSnsSqsLambda(serverless, options); 568 | }); 569 | 570 | describe("when no optional parameters are provided", () => { 571 | it("should produce valid SQS CF template items", () => { 572 | const template = { 573 | Resources: { 574 | ...generateIamLambdaExecutionRole() 575 | } 576 | }; 577 | const testConfig = { 578 | name: "some-name", 579 | topicArn: "arn:aws:sns:us-east-2:123456789012:MyTopic" 580 | }; 581 | const validatedConfig = serverlessSnsSqsLambda.validateConfig( 582 | "test-function", 583 | serverlessSnsSqsLambda.stage, 584 | testConfig 585 | ); 586 | serverlessSnsSqsLambda.addEventQueue(template, validatedConfig); 587 | serverlessSnsSqsLambda.addEventDeadLetterQueue( 588 | template, 589 | validatedConfig 590 | ); 591 | serverlessSnsSqsLambda.addEventSourceMapping(template, validatedConfig); 592 | serverlessSnsSqsLambda.addTopicSubscription(template, validatedConfig); 593 | serverlessSnsSqsLambda.addLambdaSqsPermissions( 594 | template, 595 | validatedConfig 596 | ); 597 | 598 | expect(template).toMatchSnapshot(); 599 | }); 600 | }); 601 | 602 | describe("when fifo is true", () => { 603 | it("should produce valid fifo queues", () => { 604 | const template = { 605 | Resources: { 606 | ...generateIamLambdaExecutionRole() 607 | } 608 | }; 609 | const testConfig = { 610 | name: "some-name", 611 | topicArn: "arn:aws:sns:us-east-2:123456789012:MyTopic", 612 | fifo: true 613 | }; 614 | const validatedConfig = serverlessSnsSqsLambda.validateConfig( 615 | "test-function", 616 | serverlessSnsSqsLambda.stage, 617 | testConfig 618 | ); 619 | serverlessSnsSqsLambda.addEventQueue(template, validatedConfig); 620 | serverlessSnsSqsLambda.addEventDeadLetterQueue( 621 | template, 622 | validatedConfig 623 | ); 624 | serverlessSnsSqsLambda.addEventSourceMapping(template, validatedConfig); 625 | serverlessSnsSqsLambda.addTopicSubscription(template, validatedConfig); 626 | serverlessSnsSqsLambda.addLambdaSqsPermissions( 627 | template, 628 | validatedConfig 629 | ); 630 | 631 | expect(template).toMatchSnapshot(); 632 | }); 633 | }); 634 | }); 635 | 636 | describe("when no provider is specified", () => { 637 | beforeEach(() => { 638 | serverless = new Serverless({ commands: [], options: {} }); 639 | serverless.service.service = "test-service"; 640 | 641 | const options = { 642 | ...slsOpt 643 | }; 644 | const provider = new AwsProvider(serverless); 645 | serverless.setProvider("aws", provider); 646 | serverless.cli = new CLI(serverless); 647 | serverlessSnsSqsLambda = new ServerlessSnsSqsLambda(serverless, options); 648 | }); 649 | 650 | describe("when no optional parameters are provided", () => { 651 | it("stage should default to 'dev'", () => { 652 | const template = { 653 | Resources: { 654 | ...generateIamLambdaExecutionRole() 655 | } 656 | }; 657 | const testConfig = { 658 | name: "some-name", 659 | topicArn: "arn:aws:sns:us-east-2:123456789012:MyTopic" 660 | }; 661 | const validatedConfig = serverlessSnsSqsLambda.validateConfig( 662 | "test-function", 663 | serverlessSnsSqsLambda.stage, 664 | testConfig 665 | ); 666 | serverlessSnsSqsLambda.addEventQueue(template, validatedConfig); 667 | serverlessSnsSqsLambda.addEventDeadLetterQueue( 668 | template, 669 | validatedConfig 670 | ); 671 | serverlessSnsSqsLambda.addEventSourceMapping(template, validatedConfig); 672 | serverlessSnsSqsLambda.addTopicSubscription(template, validatedConfig); 673 | serverlessSnsSqsLambda.addLambdaSqsPermissions( 674 | template, 675 | validatedConfig 676 | ); 677 | 678 | expect(template).toMatchSnapshot(); 679 | }); 680 | }); 681 | 682 | describe("when fifo is true", () => { 683 | it("stage should default to 'dev'", () => { 684 | const template = { 685 | Resources: { 686 | ...generateIamLambdaExecutionRole() 687 | } 688 | }; 689 | const testConfig = { 690 | name: "some-name", 691 | topicArn: "arn:aws:sns:us-east-2:123456789012:MyTopic", 692 | fifo: true 693 | }; 694 | const validatedConfig = serverlessSnsSqsLambda.validateConfig( 695 | "test-function", 696 | serverlessSnsSqsLambda.stage, 697 | testConfig 698 | ); 699 | serverlessSnsSqsLambda.addEventQueue(template, validatedConfig); 700 | serverlessSnsSqsLambda.addEventDeadLetterQueue( 701 | template, 702 | validatedConfig 703 | ); 704 | serverlessSnsSqsLambda.addEventSourceMapping(template, validatedConfig); 705 | serverlessSnsSqsLambda.addTopicSubscription(template, validatedConfig); 706 | serverlessSnsSqsLambda.addLambdaSqsPermissions( 707 | template, 708 | validatedConfig 709 | ); 710 | 711 | expect(template).toMatchSnapshot(); 712 | }); 713 | }); 714 | 715 | describe("when there are duplicate names", () => { 716 | it("should throw", () => { 717 | const template = { 718 | Resources: { 719 | ...generateIamLambdaExecutionRole() 720 | } 721 | }; 722 | const testCase = { 723 | functions: { 724 | Fn1: { 725 | events: [ 726 | { 727 | snsSqs: { 728 | name: "Event1", 729 | topicArn: "arn:aws:sns:us-east-2:123456789012:MyTopic" 730 | } 731 | } 732 | ] 733 | }, 734 | Fn2: { 735 | events: [ 736 | { 737 | snsSqs: { 738 | name: "Event1", 739 | topicArn: "arn:aws:sns:us-east-2:123456789012:MyTopic" 740 | } 741 | } 742 | ] 743 | } 744 | } 745 | } as const; 746 | 747 | const thunk = () => { 748 | serverlessSnsSqsLambda.addSnsSqsResources( 749 | template, 750 | "Fn1", 751 | "unit-test", 752 | testCase.functions.Fn1.events[0].snsSqs 753 | ); 754 | serverlessSnsSqsLambda.addSnsSqsResources( 755 | template, 756 | "Fn2", 757 | "unit-test", 758 | testCase.functions.Fn2.events[0].snsSqs 759 | ); 760 | }; 761 | 762 | expect(thunk).toThrowErrorMatchingInlineSnapshot( 763 | `"Generated logical ID [Event1DeadLetterQueue] already exists in resources definition. Ensure that the snsSqs event definition has a unique name property."` 764 | ); 765 | }); 766 | }); 767 | 768 | describe("when the generated queue names are too long (over 80 characters)", () => { 769 | describe("when omitPhysicalId is false", () => { 770 | it("should throw", () => { 771 | const template = { 772 | Resources: { 773 | ...generateIamLambdaExecutionRole() 774 | } 775 | }; 776 | const testCase = { 777 | functions: { 778 | Fn1: { 779 | events: [ 780 | { 781 | snsSqs: { 782 | prefix: "something-really-long-that-puts-it-", 783 | name: "over-80-characters-which-is-no-good", 784 | topicArn: "arn:aws:sns:us-east-2:123456789012:MyTopic" 785 | } 786 | } 787 | ] 788 | } 789 | } 790 | } as const; 791 | 792 | const thunk = () => { 793 | serverlessSnsSqsLambda.addSnsSqsResources( 794 | template, 795 | "Fn1", 796 | "unit-test", 797 | testCase.functions.Fn1.events[0].snsSqs 798 | ); 799 | }; 800 | 801 | expect(thunk).toThrowErrorMatchingInlineSnapshot( 802 | `"Generated queue name [something-really-long-that-puts-it-over-80-characters-which-is-no-goodDeadLetterQueue] is longer than 80 characters long and may be truncated by AWS, causing naming collisions. Try a shorter prefix or name, or try the hashQueueName config option."` 803 | ); 804 | }); 805 | }); 806 | }); 807 | describe("when omitPhysicalId is true", () => { 808 | it("should omit the queue name so that AWS can generate a unique one which is 80 chars or less", () => { 809 | const template = { 810 | Resources: { 811 | ...generateIamLambdaExecutionRole() 812 | } 813 | }; 814 | const testCase = { 815 | functions: { 816 | Fn1: { 817 | events: [ 818 | { 819 | snsSqs: { 820 | prefix: "something-really-long-that-puts-it-", 821 | name: "over-80-characters-which-is-no-good", 822 | topicArn: "arn:aws:sns:us-east-2:123456789012:MyTopic", 823 | omitPhysicalId: true 824 | } 825 | } 826 | ] 827 | } 828 | } 829 | } as const; 830 | 831 | serverlessSnsSqsLambda.addSnsSqsResources( 832 | template, 833 | "Fn1", 834 | "unit-test", 835 | testCase.functions.Fn1.events[0].snsSqs 836 | ); 837 | 838 | const regularQueueName = 839 | template.Resources["over-80-characters-which-is-no-goodQueue"] 840 | .Properties.QueueName; 841 | const deadLetterQueueName = 842 | template.Resources[ 843 | "over-80-characters-which-is-no-goodDeadLetterQueue" 844 | ].Properties.QueueName; 845 | 846 | // AWS will do this for us 847 | expect(regularQueueName).toBeUndefined(); 848 | expect(deadLetterQueueName).toBeUndefined(); 849 | }); 850 | }); 851 | }); 852 | }); 853 | -------------------------------------------------------------------------------- /lib/serverless-sns-sqs-lambda.ts: -------------------------------------------------------------------------------- 1 | import { JsonObject } from "type-fest"; 2 | 3 | // Future work: Properly type the file 4 | /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ 5 | /* eslint-disable @typescript-eslint/no-explicit-any */ 6 | 7 | /** 8 | * A regular expression that matches AWS KMS arns 9 | */ 10 | const kmsArnRegex = /^arn:aws:kms:.*:.*:key\/.+$/; 11 | 12 | /** 13 | * Defines the structure of the config object 14 | * that is passed to the main functions to 15 | * generate the Serverless template. 16 | */ 17 | type Config = { 18 | name: string; 19 | topicArn: string; 20 | funcName: string; 21 | prefix: string; 22 | batchSize: number; 23 | maximumBatchingWindowInSeconds: number; 24 | maxRetryCount: number; 25 | kmsMasterKeyId: string; 26 | kmsDataKeyReusePeriodSeconds: number; 27 | deadLetterMessageRetentionPeriodSeconds: number; 28 | deadLetterQueueEnabled: boolean; 29 | enabled: boolean; 30 | fifo: boolean; 31 | visibilityTimeout: number; 32 | rawMessageDelivery: boolean; 33 | filterPolicy: any; 34 | readonly omitPhysicalId: boolean; 35 | 36 | mainQueueOverride: JsonObject; 37 | deadLetterQueueOverride: JsonObject; 38 | eventSourceMappingOverride: JsonObject; 39 | subscriptionOverride: JsonObject; 40 | }; 41 | 42 | /** 43 | * Parse a value into a number or set it to a default value. 44 | * 45 | * @param {string|number|null|undefined} intString value possibly in string 46 | * @param {*} defaultInt the default value if `intString` can't be parsed 47 | */ 48 | const parseIntOr = (intString, defaultInt) => { 49 | if (intString === null || intString === undefined) { 50 | return defaultInt; 51 | } 52 | try { 53 | return parseInt(intString.toString(), 10); 54 | } catch { 55 | return defaultInt; 56 | } 57 | }; 58 | 59 | /** 60 | * Converts a string from camelCase to PascalCase. Basically, it just 61 | * capitalises the first letter. 62 | * 63 | * @param {string} camelCase camelCase string 64 | */ 65 | const pascalCase = (camelCase: string): string => 66 | camelCase.slice(0, 1).toUpperCase() + camelCase.slice(1); 67 | 68 | const pascalCaseAllKeys = (jsonObject: JsonObject): JsonObject => 69 | Object.keys(jsonObject).reduce( 70 | (acc, key) => ({ 71 | ...acc, 72 | [pascalCase(key)]: jsonObject[key] 73 | }), 74 | {} 75 | ); 76 | 77 | const validateQueueName = (queueName: string): string => { 78 | if (queueName.length > 80) { 79 | throw new Error( 80 | `Generated queue name [${queueName}] is longer than 80 characters long and may be truncated by AWS, causing naming collisions. Try a shorter prefix or name, or try the hashQueueName config option.` 81 | ); 82 | } 83 | return queueName; 84 | }; 85 | 86 | /** 87 | * Returns true if the provided string looks like an KMS ARN, otherwise false 88 | * @param possibleArn the candidate string 89 | * @returns true if the provided string looks like a KMS ARN, otherwise false 90 | */ 91 | const isKmsArn = (possibleArn: string): boolean => 92 | kmsArnRegex.test(possibleArn); 93 | 94 | /** 95 | * Adds a resource block to a template, ensuring uniqueness. 96 | * @param template the serverless template 97 | * @param logicalId the logical ID (resource key) for the resource 98 | * @param resourceDefinition the definition of the resource 99 | */ 100 | const addResource = ( 101 | template: any, 102 | logicalId: string, 103 | resourceDefinition: Record 104 | ) => { 105 | if (logicalId in template.Resources) { 106 | throw new Error( 107 | `Generated logical ID [${logicalId}] already exists in resources definition. Ensure that the snsSqs event definition has a unique name property.` 108 | ); 109 | } 110 | template.Resources[logicalId] = resourceDefinition; 111 | }; 112 | 113 | /** 114 | * The ServerlessSnsSqsLambda plugin looks for functions that contain an 115 | * `snsSqs` event and adds the necessary resources for the Lambda to subscribe 116 | * to the SNS topics with error handling and retry functionality built in. 117 | * 118 | * An example configuration might look like: 119 | * 120 | * functions: 121 | * processEvent: 122 | * handler: handler.handler 123 | * events: 124 | * - snsSqs: 125 | * name: ResourcePrefix 126 | * topicArn: ${self:custom.topicArn} 127 | * batchSize: 2 128 | * maximumBatchingWindowInSeconds: 30 129 | * maxRetryCount: 2 130 | * kmsMasterKeyId: alias/aws/sqs 131 | * kmsDataKeyReusePeriodSeconds: 600 132 | * deadLetterMessageRetentionPeriodSeconds: 1209600 133 | * deadLetterQueueEnabled: true 134 | * visibilityTimeout: 120 135 | * rawMessageDelivery: true 136 | * enabled: false 137 | * fifo: false 138 | * filterPolicy: 139 | * pet: 140 | * - dog 141 | * - cat 142 | */ 143 | export default class ServerlessSnsSqsLambda { 144 | serverless: any; 145 | options: any; 146 | provider: any; 147 | custom: any; 148 | serviceName: string; 149 | stage: string; 150 | hooks: any; 151 | 152 | /** 153 | * @param {*} serverless 154 | * @param {*} options 155 | */ 156 | constructor(serverless, options) { 157 | this.serverless = serverless; 158 | this.options = options; 159 | this.provider = serverless ? serverless.getProvider("aws") : null; 160 | this.custom = serverless.service ? serverless.service.custom : null; 161 | this.serviceName = serverless.service.service; 162 | 163 | // Aligns with AWS provider order of precedence: https://github.com/serverless/serverless/blob/46d090a302b9f7f4a3cf479695489b7ffc46b75b/lib/plugins/aws/provider.js#L1728 164 | // Serverless will set one of these to "dev" if it is not provided so we don't need an explicit fallback 165 | this.stage = 166 | this.options.stage || 167 | this.serverless.config.stage || 168 | this.serverless.service.provider.stage; 169 | 170 | serverless.configSchemaHandler.defineFunctionEvent("aws", "snsSqs", { 171 | type: "object", 172 | properties: { 173 | name: { type: "string" }, 174 | topicArn: { $ref: "#/definitions/awsArn" }, 175 | prefix: { type: "string" }, 176 | omitPhysicalId: { type: "boolean" }, 177 | batchSize: { type: "number", minimum: 1, maximum: 10000 }, 178 | maximumBatchingWindowInSeconds: { 179 | type: "number", 180 | minimum: 0, 181 | maximum: 300 182 | }, 183 | maxRetryCount: { type: "number" }, 184 | kmsMasterKeyId: { 185 | anyOf: [{ type: "string" }, { $ref: "#/definitions/awsArn" }] 186 | }, 187 | kmsDataKeyReusePeriodSeconds: { 188 | type: "number", 189 | minimum: 60, 190 | maximum: 86400 191 | }, 192 | visibilityTimeout: { 193 | type: "number", 194 | minimum: 0, 195 | maximum: 43200 196 | }, 197 | deadLetterMessageRetentionPeriodSeconds: { 198 | type: "number", 199 | minimum: 60, 200 | maximum: 1209600 201 | }, 202 | deadLetterQueueEnabled: { type: "boolean" }, 203 | rawMessageDelivery: { type: "boolean" }, 204 | enabled: { type: "boolean" }, 205 | fifo: { type: "boolean" }, 206 | filterPolicy: { type: "object" }, 207 | mainQueueOverride: { type: "object" }, 208 | deadLetterQueueOverride: { type: "object" }, 209 | eventSourceMappingOverride: { type: "object" }, 210 | subscriptionOverride: { type: "object" } 211 | }, 212 | required: ["name", "topicArn"], 213 | additionalProperties: false 214 | }); 215 | 216 | if (!this.provider) { 217 | throw new Error("This plugin must be used with AWS"); 218 | } 219 | 220 | this.hooks = { 221 | "aws:package:finalize:mergeCustomProviderResources": 222 | this.modifyTemplate.bind(this) 223 | }; 224 | } 225 | 226 | /** 227 | * Mutate the CloudFormation template, adding the necessary resources for 228 | * the Lambda to subscribe to the SNS topics with error handling and retry 229 | * functionality built in. 230 | */ 231 | modifyTemplate() { 232 | const functions = this.serverless.service.functions; 233 | const template = 234 | this.serverless.service.provider.compiledCloudFormationTemplate; 235 | 236 | Object.keys(functions).forEach(funcKey => { 237 | const func = functions[funcKey]; 238 | if (func.events) { 239 | func.events.forEach(event => { 240 | if (event.snsSqs) { 241 | if (this.options.verbose) { 242 | console.info( 243 | `Adding snsSqs event handler [${JSON.stringify(event.snsSqs)}]` 244 | ); 245 | } 246 | this.addSnsSqsResources( 247 | template, 248 | funcKey, 249 | this.stage, 250 | event.snsSqs 251 | ); 252 | } 253 | }); 254 | } 255 | }); 256 | } 257 | 258 | /** 259 | * 260 | * @param {object} template the template which gets mutated 261 | * @param {string} funcName the name of the function from serverless config 262 | * @param {string} stage the stage name from the serverless config 263 | * @param {object} snsSqsConfig the configuration values from the snsSqs 264 | * event portion of the serverless function config 265 | */ 266 | addSnsSqsResources(template, funcName, stage, snsSqsConfig) { 267 | const config = this.validateConfig(funcName, stage, snsSqsConfig); 268 | 269 | [ 270 | this.addEventSourceMapping, 271 | this.addEventDeadLetterQueue, 272 | this.addEventQueue, 273 | this.addEventQueuePolicy, 274 | this.addTopicSubscription, 275 | this.addLambdaSqsPermissions 276 | ].reduce((template, func) => { 277 | func(template, config); 278 | return template; 279 | }, template); 280 | } 281 | 282 | /** 283 | * Validate the configuration values from the serverless config file, 284 | * returning a config object that can be passed to the resource setup 285 | * functions. 286 | * 287 | * @param {string} funcName the name of the function from serverless config 288 | * @param {string} stage the stage name from the serverless config 289 | * @param {object} config the configuration values from the snsSqs event 290 | * portion of the serverless function config 291 | */ 292 | validateConfig(funcName, stage, config): Config { 293 | if (!config.topicArn || !config.name) { 294 | throw new Error(`Error: 295 | When creating an snsSqs handler, you must define the name and topicArn. 296 | In function [${funcName}]: 297 | - name was [${config.name}] 298 | - topicArn was [${config.topicArn}]. 299 | 300 | Usage 301 | ----- 302 | 303 | functions: 304 | processEvent: 305 | handler: handler.handler 306 | events: 307 | - snsSqs: 308 | name: Event # required 309 | topicArn: !Ref TopicArn # required 310 | prefix: some-prefix # optional - default is \`\${this.serviceName}-\${stage}-\${funcNamePascalCase}\` 311 | maxRetryCount: 2 # optional - default is 5 312 | batchSize: 1 # optional - default is 10 313 | batchWindow: 10 # optional - default is 0 (no batch window) 314 | kmsMasterKeyId: alias/aws/sqs # optional - default is none (no encryption) 315 | kmsDataKeyReusePeriodSeconds: 600 # optional - AWS default is 300 seconds 316 | deadLetterMessageRetentionPeriodSeconds: 1209600 # optional - AWS default is 345600 secs (4 days) 317 | deadLetterQueueEnabled: true # optional - default is enabled 318 | enabled: true # optional - AWS default is true 319 | fifo: false # optional - AWS default is false 320 | visibilityTimeout: 30 # optional - AWS default is 30 seconds 321 | rawMessageDelivery: false # optional - default is false 322 | filterPolicy: 323 | pet: 324 | - dog 325 | - cat 326 | 327 | # Overrides for generated CloudFormation templates 328 | # Mirrors the CloudFormation docs but uses camel case instead of title case 329 | # 330 | # 331 | # https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-sqs-queues.html 332 | mainQueueOverride: 333 | maximumMessageSize: 1024 334 | ... 335 | deadLetterQueueOverride: 336 | maximumMessageSize: 1024 337 | ... 338 | # https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-eventsourcemapping.html 339 | eventSourceMappingOverride: 340 | bisectBatchOnFunctionError: true 341 | # https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-sns-subscription.html 342 | subscriptionOverride: 343 | rawMessageDelivery: true 344 | 345 | `); 346 | } 347 | 348 | const funcNamePascalCase = pascalCase(funcName); 349 | return { 350 | ...config, 351 | name: config.name, 352 | funcName: funcNamePascalCase, 353 | prefix: 354 | config.prefix || `${this.serviceName}-${stage}-${funcNamePascalCase}`, 355 | batchSize: parseIntOr(config.batchSize, 10), 356 | maxRetryCount: parseIntOr(config.maxRetryCount, 5), 357 | kmsMasterKeyId: config.kmsMasterKeyId, 358 | kmsDataKeyReusePeriodSeconds: config.kmsDataKeyReusePeriodSeconds, 359 | deadLetterMessageRetentionPeriodSeconds: 360 | config.deadLetterMessageRetentionPeriodSeconds, 361 | deadLetterQueueEnabled: 362 | config.deadLetterQueueEnabled !== undefined 363 | ? config.deadLetterQueueEnabled 364 | : true, 365 | enabled: config.enabled, 366 | fifo: config.fifo !== undefined ? config.fifo : false, 367 | visibilityTimeout: config.visibilityTimeout, 368 | rawMessageDelivery: 369 | config.rawMessageDelivery !== undefined 370 | ? config.rawMessageDelivery 371 | : false, 372 | mainQueueOverride: config.mainQueueOverride ?? {}, 373 | deadLetterQueueOverride: config.deadLetterQueueOverride ?? {}, 374 | eventSourceMappingOverride: config.eventSourceMappingOverride ?? {}, 375 | subscriptionOverride: config.subscriptionOverride ?? {} 376 | }; 377 | } 378 | 379 | /** 380 | * Add the Event Source Mapping which sets up the message handler to pull 381 | * events of the Event Queue and handle them. 382 | * 383 | * @param {object} template the template which gets mutated 384 | * @param {{funcName, name, prefix, batchSize, enabled}} config including name of the queue 385 | * and the resource prefix 386 | */ 387 | addEventSourceMapping( 388 | template, 389 | { 390 | funcName, 391 | name, 392 | batchSize, 393 | maximumBatchingWindowInSeconds, 394 | enabled, 395 | eventSourceMappingOverride 396 | }: Config 397 | ) { 398 | const enabledWithDefault = enabled !== undefined ? enabled : true; 399 | addResource(template, `${funcName}EventSourceMappingSQS${name}Queue`, { 400 | Type: "AWS::Lambda::EventSourceMapping", 401 | Properties: { 402 | BatchSize: batchSize, 403 | MaximumBatchingWindowInSeconds: 404 | maximumBatchingWindowInSeconds !== undefined 405 | ? maximumBatchingWindowInSeconds 406 | : 0, 407 | EventSourceArn: { "Fn::GetAtt": [`${name}Queue`, "Arn"] }, 408 | FunctionName: { "Fn::GetAtt": [`${funcName}LambdaFunction`, "Arn"] }, 409 | Enabled: enabledWithDefault ? "True" : "False", 410 | ...pascalCaseAllKeys(eventSourceMappingOverride) 411 | } 412 | }); 413 | } 414 | 415 | /** 416 | * Add the Dead Letter Queue which will collect failed messages for later 417 | * inspection and handling. 418 | * 419 | * @param {object} template the template which gets mutated 420 | * @param {{name, prefix, kmsMasterKeyId, kmsDataKeyReusePeriodSeconds, deadLetterMessageRetentionPeriodSeconds }} config including name of the queue 421 | * and the resource prefix 422 | */ 423 | addEventDeadLetterQueue( 424 | template, 425 | { 426 | name, 427 | prefix, 428 | fifo, 429 | kmsMasterKeyId, 430 | kmsDataKeyReusePeriodSeconds, 431 | deadLetterMessageRetentionPeriodSeconds, 432 | deadLetterQueueOverride, 433 | deadLetterQueueEnabled, 434 | omitPhysicalId 435 | } 436 | ) { 437 | if (!deadLetterQueueEnabled) { 438 | return; 439 | } 440 | const candidateQueueName = `${prefix}${name}DeadLetterQueue${ 441 | fifo ? ".fifo" : "" 442 | }`; 443 | addResource(template, `${name}DeadLetterQueue`, { 444 | Type: "AWS::SQS::Queue", 445 | Properties: { 446 | ...(omitPhysicalId 447 | ? {} 448 | : { QueueName: validateQueueName(candidateQueueName) }), 449 | ...(fifo ? { FifoQueue: true } : {}), 450 | ...(kmsMasterKeyId !== undefined 451 | ? { 452 | KmsMasterKeyId: kmsMasterKeyId 453 | } 454 | : {}), 455 | ...(kmsDataKeyReusePeriodSeconds !== undefined 456 | ? { 457 | KmsDataKeyReusePeriodSeconds: kmsDataKeyReusePeriodSeconds 458 | } 459 | : {}), 460 | ...(deadLetterMessageRetentionPeriodSeconds !== undefined 461 | ? { 462 | MessageRetentionPeriod: deadLetterMessageRetentionPeriodSeconds 463 | } 464 | : {}), 465 | ...pascalCaseAllKeys(deadLetterQueueOverride) 466 | } 467 | }); 468 | } 469 | 470 | /** 471 | * Add the event queue that will subscribe to the topic and collect the events 472 | * from SNS as they arrive, holding them for processing. 473 | * 474 | * @param {object} template the template which gets mutated 475 | * @param {{name, prefix, maxRetryCount, kmsMasterKeyId, kmsDataKeyReusePeriodSeconds, visibilityTimeout}} config including name of the queue, 476 | * the resource prefix and the max retry count for message handler failures. 477 | */ 478 | addEventQueue( 479 | template, 480 | { 481 | name, 482 | prefix, 483 | fifo, 484 | maxRetryCount, 485 | kmsMasterKeyId, 486 | kmsDataKeyReusePeriodSeconds, 487 | visibilityTimeout, 488 | mainQueueOverride, 489 | omitPhysicalId, 490 | deadLetterQueueEnabled 491 | }: Config 492 | ) { 493 | const candidateQueueName = `${prefix}${name}Queue${fifo ? ".fifo" : ""}`; 494 | addResource(template, `${name}Queue`, { 495 | Type: "AWS::SQS::Queue", 496 | Properties: { 497 | ...(omitPhysicalId 498 | ? {} 499 | : { QueueName: validateQueueName(candidateQueueName) }), 500 | ...(fifo ? { FifoQueue: true } : {}), 501 | ...(deadLetterQueueEnabled 502 | ? { 503 | RedrivePolicy: { 504 | deadLetterTargetArn: { 505 | "Fn::GetAtt": [`${name}DeadLetterQueue`, "Arn"] 506 | }, 507 | maxReceiveCount: maxRetryCount 508 | } 509 | } 510 | : {}), 511 | ...(kmsMasterKeyId !== undefined 512 | ? { 513 | KmsMasterKeyId: kmsMasterKeyId 514 | } 515 | : {}), 516 | ...(kmsDataKeyReusePeriodSeconds !== undefined 517 | ? { 518 | KmsDataKeyReusePeriodSeconds: kmsDataKeyReusePeriodSeconds 519 | } 520 | : {}), 521 | ...(visibilityTimeout !== undefined 522 | ? { 523 | VisibilityTimeout: visibilityTimeout 524 | } 525 | : {}), 526 | ...pascalCaseAllKeys(mainQueueOverride) 527 | } 528 | }); 529 | } 530 | 531 | /** 532 | * Add a policy allowing the queue to subscribe to the SNS topic. 533 | * 534 | * @param {object} template the template which gets mutated 535 | * @param {{name, prefix, topicArn}} config including name of the queue, the 536 | * resource prefix and the arn of the topic 537 | */ 538 | addEventQueuePolicy(template, { name, prefix, topicArn }: Config) { 539 | addResource(template, `${name}QueuePolicy`, { 540 | Type: "AWS::SQS::QueuePolicy", 541 | Properties: { 542 | PolicyDocument: { 543 | Version: "2012-10-17", 544 | Id: `${prefix}${name}Queue`, 545 | Statement: [ 546 | { 547 | Sid: `${prefix}${name}Sid`, 548 | Effect: "Allow", 549 | Principal: { Service: "sns.amazonaws.com" }, 550 | Action: "SQS:SendMessage", 551 | Resource: { "Fn::GetAtt": [`${name}Queue`, "Arn"] }, 552 | Condition: { ArnEquals: { "aws:SourceArn": [topicArn] } } 553 | } 554 | ] 555 | }, 556 | Queues: [{ Ref: `${name}Queue` }] 557 | } 558 | }); 559 | } 560 | 561 | /** 562 | * Subscribe the newly created queue to the desired topic. 563 | * 564 | * @param {object} template the template which gets mutated 565 | * @param {{name, topicArn, filterPolicy}} config including name of the queue, 566 | * the arn of the topic and the filter policy for the subscription 567 | */ 568 | addTopicSubscription( 569 | template, 570 | { 571 | name, 572 | topicArn, 573 | filterPolicy, 574 | rawMessageDelivery, 575 | subscriptionOverride 576 | }: Config 577 | ) { 578 | addResource(template, `Subscribe${name}Topic`, { 579 | Type: "AWS::SNS::Subscription", 580 | Properties: { 581 | Endpoint: { "Fn::GetAtt": [`${name}Queue`, "Arn"] }, 582 | Protocol: "sqs", 583 | TopicArn: topicArn, 584 | ...(filterPolicy ? { FilterPolicy: filterPolicy } : {}), 585 | ...(rawMessageDelivery !== undefined 586 | ? { 587 | RawMessageDelivery: rawMessageDelivery 588 | } 589 | : {}), 590 | ...pascalCaseAllKeys(subscriptionOverride) 591 | } 592 | }); 593 | } 594 | 595 | /** 596 | * Add permissions so that the SQS handler can access the queue. 597 | * 598 | * @param {object} template the template which gets mutated 599 | * @param {{name, prefix}} config the name of the queue the lambda is subscribed to 600 | */ 601 | addLambdaSqsPermissions( 602 | template, 603 | { name, kmsMasterKeyId, deadLetterQueueEnabled } 604 | ) { 605 | if (template.Resources.IamRoleLambdaExecution === undefined) { 606 | // The user has set their own custom role ARN so the Serverless generated role is not generated 607 | // We can safely skip this step because the owner of the custom role ARN is responsible for setting 608 | // this the relevant policy to allow the lambda to access the queue. 609 | return; 610 | } 611 | const queues = [{ "Fn::GetAtt": [`${name}Queue`, "Arn"] }]; 612 | if (deadLetterQueueEnabled) { 613 | queues.push({ "Fn::GetAtt": [`${name}DeadLetterQueue`, "Arn"] }); 614 | } 615 | template.Resources.IamRoleLambdaExecution.Properties.Policies[0].PolicyDocument.Statement.push( 616 | { 617 | Effect: "Allow", 618 | Action: [ 619 | "sqs:ReceiveMessage", 620 | "sqs:DeleteMessage", 621 | "sqs:GetQueueAttributes" 622 | ], 623 | Resource: queues 624 | } 625 | ); 626 | 627 | if (kmsMasterKeyId !== undefined && kmsMasterKeyId !== null) { 628 | // TODO: Should we rename kmsMasterKeyId to make it clearer that it can accept an ARN? 629 | const resource = 630 | // If the key ID is an object, it is most likely a "Ref" or "GetAtt" so we should pass it straight through so it gets resolved by CloudFormation 631 | // If an ARN is provided, pass it straight through too, because no processing is needed 632 | // Otherwise if it isn't either of those things, it is probably an ID, so we need to 633 | // transform it to an ARN to make the policy valid 634 | typeof kmsMasterKeyId === "object" || isKmsArn(kmsMasterKeyId) 635 | ? kmsMasterKeyId 636 | : `arn:aws:kms:::key/${kmsMasterKeyId}`; 637 | template.Resources.IamRoleLambdaExecution.Properties.Policies[0].PolicyDocument.Statement.push( 638 | { 639 | Effect: "Allow", 640 | Action: ["kms:Decrypt"], 641 | Resource: resource 642 | } 643 | ); 644 | } 645 | } 646 | } 647 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@agiledigital/serverless-sns-sqs-lambda", 3 | "version": "2.1.0", 4 | "description": "serverless plugin to make serverless-sns-sqs-lambda events", 5 | "main": "dist/index.js", 6 | "scripts": { 7 | "test": "yarn build && eslint lib/**/*.ts && jest --clearCache lib && jest --runInBand lib integration-tests", 8 | "format": "prettier --ignore-path .gitignore --write . !**.md", 9 | "format-check": "prettier --ignore-path .gitignore --check . !**.md", 10 | "build": "tsc", 11 | "commit": "git-cz", 12 | "check-branch-commits": "cz check --rev-range master..HEAD", 13 | "prepack": "tsc" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "git+https://github.com/agiledigital/serverless-sns-sqs-lambda.git" 18 | }, 19 | "keywords": [ 20 | "serverless", 21 | "sls", 22 | "sns", 23 | "sqs", 24 | "aws", 25 | "lambda" 26 | ], 27 | "author": "Agile Digital", 28 | "license": "MIT", 29 | "bugs": { 30 | "url": "https://github.com/agiledigital/serverless-sns-sqs-lambda/issues" 31 | }, 32 | "homepage": "https://github.com/agiledigital/serverless-sns-sqs-lambda#readme", 33 | "devDependencies": { 34 | "@commitlint/cli": "^17.0.1", 35 | "@commitlint/config-conventional": "^17.0.0", 36 | "@jedmao/semantic-release-npm-github-config": "^1.0.9", 37 | "@serverless/test": "^11.0.0", 38 | "@types/jest": "^27.0.1", 39 | "@typescript-eslint/eslint-plugin": "^4.6.0", 40 | "@typescript-eslint/parser": "^4.6.0", 41 | "ajv": "^8.6.0", 42 | "commitizen": "^4.1.2", 43 | "cz-conventional-changelog": "^3.2.0", 44 | "eslint": "^7.12.1", 45 | "jest": "^26.3.0", 46 | "prettier": "^2.1.2", 47 | "semantic-release": "^19.0.2", 48 | "serverless": "^3.16.0", 49 | "ts-jest": "^26.4.3", 50 | "type-coverage": "^2.14.0", 51 | "type-fest": "^2.9.0", 52 | "typescript": "^4.0.5" 53 | }, 54 | "peerDependencies": { 55 | "serverless": "3.x" 56 | }, 57 | "publishConfig": { 58 | "access": "public" 59 | }, 60 | "config": { 61 | "commitizen": { 62 | "path": "./node_modules/cz-conventional-changelog" 63 | } 64 | }, 65 | "files": [ 66 | "dist" 67 | ], 68 | "typeCoverage": { 69 | "atLeast": 56, 70 | "strict": true, 71 | "detail": true 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /scripts/plant.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | for D in `ls ./plant-uml-files/**.plant` 3 | do 4 | echo 'generating' $D 5 | plantuml $D 6 | done 7 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */, 4 | "module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */, 5 | "allowJs": true /* Allow javascript files to be compiled. */, 6 | "checkJs": true /* Report errors in .js files. */, 7 | "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */, 8 | "skipLibCheck": true /* Skip type checking of declaration files. */, 9 | "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */, 10 | "outDir": "dist", 11 | "sourceMap": true 12 | }, 13 | "include": ["lib/*"] 14 | } 15 | --------------------------------------------------------------------------------