diff --git a/.eslintrc b/.eslintrc index 1101f09..961180e 100644 --- a/.eslintrc +++ b/.eslintrc @@ -12,6 +12,7 @@ }, "rules": { "no-unused-vars": "warn", - "@typescript-eslint/no-unused-vars": "warn" + "@typescript-eslint/no-unused-vars": "warn", + "@typescript-eslint/no-explicit-any": "error" } } diff --git a/README.md b/README.md index 32aa6ce..67bbd19 100644 --- a/README.md +++ b/README.md @@ -6,10 +6,11 @@ [![npm version](https://badge.fury.io/js/az-queue-consumer.svg)](https://badge.fury.io/js/az-queue-consumer) ![NPM License](https://img.shields.io/npm/l/az-queue-consumer) [![CodeFactor](https://www.codefactor.io/repository/github/aarontravass/az-queue-consumer/badge)](https://www.codefactor.io/repository/github/aarontravass/az-queue-consumer) +![TypeScript types](https://badgen.net/npm/types/az-queue-consumer)
-Azure Queue Consumer is a simple consumer that allows you to handle queue messages without having to worry about setting up the azure framework. +Azure Queue Consumer is a simple, typescript first consumer that allows you to handle queue messages without having to worry about setting up the azure framework. ## Installation diff --git a/package.json b/package.json index 2dce583..f873f3a 100644 --- a/package.json +++ b/package.json @@ -19,8 +19,8 @@ }, "scripts": { "build": "parcel build", - "format:check": "prettier --check src/**", - "format:fix": "prettier --write src/**", + "format:check": "prettier --check src/** tests/**", + "format:fix": "prettier --write src/** tests/**", "lint:check": "eslint src/** --ext=ts", "lint:fix": "eslint src/** --fix --ext=ts", "prepare": "husky install", @@ -43,6 +43,7 @@ "@azure/storage-queue": "^12.14.0" }, "devDependencies": { + "@azure/identity": "^4.0.0", "@commitlint/cli": "^17.6.7", "@commitlint/config-conventional": "^17.6.7", "@parcel/packager-ts": "^2.10.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0f666d1..b44ecc1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -10,6 +10,9 @@ dependencies: version: 12.14.0 devDependencies: + '@azure/identity': + specifier: ^4.0.0 + version: 4.0.0 '@commitlint/cli': specifier: ^17.6.7 version: 17.6.7 @@ -82,7 +85,6 @@ packages: engines: {node: '>=12.0.0'} dependencies: tslib: 2.6.0 - dev: false /@azure/core-auth@1.4.0: resolution: {integrity: sha512-HFrcTgmuSuukRf/EdPmqBrc5l6Q5Uu+2TbuhaKbgaCpP2TfAeiNaQPAadxO+CYBRHGUzIDteMAjFspFLDLnKVQ==} @@ -92,6 +94,30 @@ packages: tslib: 2.6.0 dev: false + /@azure/core-auth@1.5.0: + resolution: {integrity: sha512-udzoBuYG1VBoHVohDTrvKjyzel34zt77Bhp7dQntVGGD0ehVq48owENbBG8fIgkHRNUBQH5k1r0hpoMu5L8+kw==} + engines: {node: '>=14.0.0'} + dependencies: + '@azure/abort-controller': 1.1.0 + '@azure/core-util': 1.3.2 + tslib: 2.6.0 + dev: true + + /@azure/core-client@1.7.3: + resolution: {integrity: sha512-kleJ1iUTxcO32Y06dH9Pfi9K4U+Tlb111WXEnbt7R/ne+NLRwppZiTGJuTD5VVoxTMK5NTbEtm5t2vcdNCFe2g==} + engines: {node: '>=14.0.0'} + dependencies: + '@azure/abort-controller': 1.1.0 + '@azure/core-auth': 1.5.0 + '@azure/core-rest-pipeline': 1.13.0 + '@azure/core-tracing': 1.0.1 + '@azure/core-util': 1.3.2 + '@azure/logger': 1.0.4 + tslib: 2.6.0 + transitivePeerDependencies: + - supports-color + dev: true + /@azure/core-http@3.0.2: resolution: {integrity: sha512-o1wR9JrmoM0xEAa0Ue7Sp8j+uJvmqYaGoHOCT5qaVYmvgmnZDC0OvQimPA/JR3u77Sz6D1y3Xmk1y69cDU9q9A==} engines: {node: '>=14.0.0'} @@ -121,6 +147,22 @@ packages: tslib: 2.6.0 dev: false + /@azure/core-rest-pipeline@1.13.0: + resolution: {integrity: sha512-a62aP/wppgmnfIkJLfcB4ssPBcH94WzrzPVJ3tlJt050zX4lfmtnvy95D3igDo3f31StO+9BgPrzvkj4aOxnoA==} + engines: {node: '>=18.0.0'} + dependencies: + '@azure/abort-controller': 1.1.0 + '@azure/core-auth': 1.5.0 + '@azure/core-tracing': 1.0.1 + '@azure/core-util': 1.3.2 + '@azure/logger': 1.0.4 + http-proxy-agent: 5.0.0 + https-proxy-agent: 5.0.1 + tslib: 2.6.0 + transitivePeerDependencies: + - supports-color + dev: true + /@azure/core-tracing@1.0.0-preview.13: resolution: {integrity: sha512-KxDlhXyMlh2Jhj2ykX6vNEU0Vou4nHr025KoSEiz7cS3BNiHNaZcdECk/DmLkEB0as5T7b/TpRcehJ5yV6NeXQ==} engines: {node: '>=12.0.0'} @@ -129,20 +171,68 @@ packages: tslib: 2.6.0 dev: false + /@azure/core-tracing@1.0.1: + resolution: {integrity: sha512-I5CGMoLtX+pI17ZdiFJZgxMJApsK6jjfm85hpgp3oazCdq5Wxgh4wMr7ge/TTWW1B5WBuvIOI1fMU/FrOAMKrw==} + engines: {node: '>=12.0.0'} + dependencies: + tslib: 2.6.0 + dev: true + /@azure/core-util@1.3.2: resolution: {integrity: sha512-2bECOUh88RvL1pMZTcc6OzfobBeWDBf5oBbhjIhT1MV9otMVWCzpOJkkiKtrnO88y5GGBelgY8At73KGAdbkeQ==} engines: {node: '>=14.0.0'} dependencies: '@azure/abort-controller': 1.1.0 tslib: 2.6.0 - dev: false + + /@azure/identity@4.0.0: + resolution: {integrity: sha512-gtPYxIL0kI39Dw4t3HvlbfhOdXqKD2MqDgynlklF0j728j51dcKgRo6FLX0QzpBw/1gGfLxjMXqq3nKOSQ2lmA==} + engines: {node: '>=18.0.0'} + dependencies: + '@azure/abort-controller': 1.1.0 + '@azure/core-auth': 1.5.0 + '@azure/core-client': 1.7.3 + '@azure/core-rest-pipeline': 1.13.0 + '@azure/core-tracing': 1.0.1 + '@azure/core-util': 1.3.2 + '@azure/logger': 1.0.4 + '@azure/msal-browser': 3.6.0 + '@azure/msal-node': 2.6.0 + events: 3.3.0 + jws: 4.0.0 + open: 8.4.2 + stoppable: 1.1.0 + tslib: 2.6.0 + transitivePeerDependencies: + - supports-color + dev: true /@azure/logger@1.0.4: resolution: {integrity: sha512-ustrPY8MryhloQj7OWGe+HrYx+aoiOxzbXTtgblbV3xwCqpzUK36phH3XNHQKj3EPonyFUuDTfR3qFhTEAuZEg==} engines: {node: '>=14.0.0'} dependencies: tslib: 2.6.0 - dev: false + + /@azure/msal-browser@3.6.0: + resolution: {integrity: sha512-FrFBJXRJMyWXjAjg4cUNZwEKktzfzD/YD9+S1kj2ors67hKoveam4aL0bZuCZU/jTiHTn0xDQGQh2ksCMXTXtA==} + engines: {node: '>=0.8.0'} + dependencies: + '@azure/msal-common': 14.5.0 + dev: true + + /@azure/msal-common@14.5.0: + resolution: {integrity: sha512-Gx5rZbiZV/HiZ2nEKfjfAF/qDdZ4/QWxMvMo2jhIFVz528dVKtaZyFAOtsX2Ak8+TQvRsGCaEfuwJFuXB6tu1A==} + engines: {node: '>=0.8.0'} + dev: true + + /@azure/msal-node@2.6.0: + resolution: {integrity: sha512-RWAWCYYrSldIYC47oWtofIun41e6SB9TBYgGYsezq6ednagwo9ZRFyRsvl1NabmdTkdDDXRAABIdveeN2Gtd8w==} + engines: {node: 16|| 18 || 20} + dependencies: + '@azure/msal-common': 14.5.0 + jsonwebtoken: 9.0.2 + uuid: 8.3.2 + dev: true /@azure/storage-queue@12.14.0: resolution: {integrity: sha512-1j6uxhzCcbEDVPOTNWIJ5CsLzOAU5U/bXgGZeT25fy6IghFTC1JlPGALez2CWJ9fBVj6AmSnsiBXL/77iXhSpg==} @@ -2006,6 +2096,11 @@ packages: resolution: {integrity: sha512-myfUej5naTBWnqOCc/MdVOLVjXUXtIA+NpDrDBKJtLLg2shUjBu3cZmB/85RyitKc55+lUUyl7oRfLOvkr2hsw==} dev: true + /@tootallnate/once@2.0.0: + resolution: {integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==} + engines: {node: '>= 10'} + dev: true + /@trysound/sax@0.2.0: resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==} engines: {node: '>=10.13.0'} @@ -2295,6 +2390,15 @@ packages: hasBin: true dev: true + /agent-base@6.0.2: + resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} + engines: {node: '>= 6.0.0'} + dependencies: + debug: 4.3.4 + transitivePeerDependencies: + - supports-color + dev: true + /ajv@6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} dependencies: @@ -2418,6 +2522,10 @@ packages: update-browserslist-db: 1.0.11(browserslist@4.21.9) dev: true + /buffer-equal-constant-time@1.0.1: + resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} + dev: true + /buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} dev: true @@ -2715,6 +2823,11 @@ packages: titleize: 3.0.0 dev: true + /define-lazy-prop@2.0.0: + resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==} + engines: {node: '>=8'} + dev: true + /define-lazy-prop@3.0.0: resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==} engines: {node: '>=12'} @@ -2803,6 +2916,12 @@ packages: engines: {node: '>=6'} dev: true + /ecdsa-sig-formatter@1.0.11: + resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} + dependencies: + safe-buffer: 5.2.1 + dev: true + /electron-to-chromium@1.4.468: resolution: {integrity: sha512-6M1qyhaJOt7rQtNti1lBA0GwclPH+oKCmsra/hkcWs5INLxfXXD/dtdnaKUYQu/pjOBP/8Osoe4mAcNvvzoFag==} dev: true @@ -3023,6 +3142,11 @@ packages: engines: {node: '>=0.10.0'} dev: true + /events@3.3.0: + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} + dev: true + /execa@5.1.1: resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} engines: {node: '>=10'} @@ -3348,6 +3472,27 @@ packages: entities: 3.0.1 dev: true + /http-proxy-agent@5.0.0: + resolution: {integrity: sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==} + engines: {node: '>= 6'} + dependencies: + '@tootallnate/once': 2.0.0 + agent-base: 6.0.2 + debug: 4.3.4 + transitivePeerDependencies: + - supports-color + dev: true + + /https-proxy-agent@5.0.1: + resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} + engines: {node: '>= 6'} + dependencies: + agent-base: 6.0.2 + debug: 4.3.4 + transitivePeerDependencies: + - supports-color + dev: true + /human-signals@2.1.0: resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} engines: {node: '>=10.17.0'} @@ -3584,6 +3729,52 @@ packages: engines: {'0': node >= 0.2.0} dev: true + /jsonwebtoken@9.0.2: + resolution: {integrity: sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==} + engines: {node: '>=12', npm: '>=6'} + dependencies: + jws: 3.2.2 + lodash.includes: 4.3.0 + lodash.isboolean: 3.0.3 + lodash.isinteger: 4.0.4 + lodash.isnumber: 3.0.3 + lodash.isplainobject: 4.0.6 + lodash.isstring: 4.0.1 + lodash.once: 4.1.1 + ms: 2.1.2 + semver: 7.5.4 + dev: true + + /jwa@1.4.1: + resolution: {integrity: sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==} + dependencies: + buffer-equal-constant-time: 1.0.1 + ecdsa-sig-formatter: 1.0.11 + safe-buffer: 5.2.1 + dev: true + + /jwa@2.0.0: + resolution: {integrity: sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==} + dependencies: + buffer-equal-constant-time: 1.0.1 + ecdsa-sig-formatter: 1.0.11 + safe-buffer: 5.2.1 + dev: true + + /jws@3.2.2: + resolution: {integrity: sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==} + dependencies: + jwa: 1.4.1 + safe-buffer: 5.2.1 + dev: true + + /jws@4.0.0: + resolution: {integrity: sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==} + dependencies: + jwa: 2.0.0 + safe-buffer: 5.2.1 + dev: true + /kind-of@6.0.3: resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} engines: {node: '>=0.10.0'} @@ -3741,14 +3932,34 @@ packages: resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} dev: true + /lodash.includes@4.3.0: + resolution: {integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==} + dev: true + + /lodash.isboolean@3.0.3: + resolution: {integrity: sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==} + dev: true + /lodash.isfunction@3.0.9: resolution: {integrity: sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==} dev: true + /lodash.isinteger@4.0.4: + resolution: {integrity: sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==} + dev: true + + /lodash.isnumber@3.0.3: + resolution: {integrity: sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==} + dev: true + /lodash.isplainobject@4.0.6: resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} dev: true + /lodash.isstring@4.0.1: + resolution: {integrity: sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==} + dev: true + /lodash.kebabcase@4.1.1: resolution: {integrity: sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==} dev: true @@ -3761,6 +3972,10 @@ packages: resolution: {integrity: sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==} dev: true + /lodash.once@4.1.1: + resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==} + dev: true + /lodash.snakecase@4.1.1: resolution: {integrity: sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==} dev: true @@ -4056,6 +4271,15 @@ packages: mimic-fn: 4.0.0 dev: true + /open@8.4.2: + resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==} + engines: {node: '>=12'} + dependencies: + define-lazy-prop: 2.0.0 + is-docker: 2.2.1 + is-wsl: 2.2.0 + dev: true + /open@9.1.0: resolution: {integrity: sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg==} engines: {node: '>=14.16'} @@ -4562,6 +4786,11 @@ packages: resolution: {integrity: sha512-Rz6yejtVyWnVjC1RFvNmYL10kgjC49EOghxWn0RFqlCHGFpQx+Xe7yW3I4ceK1SGrWIGMjD5Kbue8W/udkbMJg==} dev: true + /stoppable@1.1.0: + resolution: {integrity: sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw==} + engines: {node: '>=4', npm: '>=6'} + dev: true + /string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} @@ -4865,7 +5094,6 @@ packages: /uuid@8.3.2: resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} hasBin: true - dev: false /v8-compile-cache-lib@3.0.1: resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} diff --git a/src/errors.ts b/src/errors.ts new file mode 100644 index 0000000..8c83f2c --- /dev/null +++ b/src/errors.ts @@ -0,0 +1,19 @@ +export class QueueError extends Error { + code: string + message: string + constructor(code: string, message: string) { + super(`${code}:${message}`) + this.code = code + this.message = message + } +} + +export class QueueConnectionError extends Error { + code: string + message: string + constructor(code: string, message: string) { + super(`${code}:${message}`) + this.code = code + this.message = message + } +} diff --git a/src/index.ts b/src/index.ts index 4abf25f..9a595f2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,12 +1,17 @@ import { DequeuedMessageItem, QueueClient, QueueServiceClient } from '@azure/storage-queue' -import { HandlerFunction, QueueError, QueueOptions } from './utils' +import { QueueConnectionError, QueueError } from './errors' import { QueueEventEmitter } from './events' - -type QueueConnection = string | QueueServiceClient +import { HandlerFunction, QueueOptions, QueueConnection } from './types' /** * The Main Queue Consumer class * @class + * @example + * import { AzureQueueConsumer } from 'az-queue-consumer'; + * const queueName = // queue name; + * const connectionString = // storage account connection string; + * const listener = new AzureQueueConsumer(queueName, connectionString, (message) => { // do something with the message }); + * listener.listen(); */ export class AzureQueueConsumer extends QueueEventEmitter { #options: QueueOptions @@ -39,9 +44,7 @@ export class AzureQueueConsumer extends QueueEventEmitter { #createQueueAsync = async () => { await this.#queueClient .createIfNotExists() - .then((res) => { - this.emit('queue::ready', res) - }) + .then((res) => this.emit('queue::ready', res)) .catch((er) => { throw new QueueError(er.code, er.message) }) @@ -73,7 +76,7 @@ export class AzureQueueConsumer extends QueueEventEmitter { if (error.code === 'REQUEST_SEND_ERROR') this.#pollingTime += 5 return }) - .then((_) => { + .then(() => { if (!this.#shouldShutdown) setTimeout(this.listen.bind(this), this.#pollingTime * 1000) else this.removeAllListeners() }) @@ -86,9 +89,9 @@ export class AzureQueueConsumer extends QueueEventEmitter { #deleteMessages = async (messages: DequeuedMessageItem[]) => { for (const message of messages) { this.emit('message::preDelete', message.messageId, message.popReceipt) - await this.#queueClient.deleteMessage(message.messageId, message.popReceipt).then((res) => { - this.emit('message::afterDelete', res) - }) + await this.#queueClient + .deleteMessage(message.messageId, message.popReceipt) + .then((res) => this.emit('message::afterDelete', res)) } } @@ -108,8 +111,12 @@ export class AzureQueueConsumer extends QueueEventEmitter { queueServiceClient = QueueServiceClient.fromConnectionString(connection, { retryOptions: { maxTries: this.#options.maxTries } }) - } else { + } else if ('connectionString' in connection && 'credential' in connection) { + queueServiceClient = new QueueServiceClient(connection.queueUrl, connection.credential) + } else if (connection instanceof QueueServiceClient) { queueServiceClient = connection + } else { + throw new QueueConnectionError('INVALID_CONNECTION', 'Queue Connection provided was invalid') } return queueServiceClient.getQueueClient(queueName) } diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 0000000..03d4d7e --- /dev/null +++ b/src/types.ts @@ -0,0 +1,62 @@ +import { + AnonymousCredential, + DequeuedMessageItem, + QueueServiceClient, + StorageSharedKeyCredential +} from '@azure/storage-queue' + +/** + * QueueOptions interface + * @interface + */ +export interface QueueOptions { + /** + * Polling time in seconds + * @default 10 + */ + pollingTime: number + /** + * Maximum number of times to try + * @default 4 + */ + maxTries?: number + /** + * Number of messages to receieve + * @default 1 + */ + numberOfMessages?: number +} + +/** + * @type {Function} + * @param {DequeuedMessageItem[]} messages + */ +export type HandlerFunction = (messages: DequeuedMessageItem[]) => void | Promise + +/** + * Azure Credential Type which is used to initialize `QueueServiceClient` + * @example + * new QueueServiceClient(AzureCredential.connectionString, AzureCredential.credential) + * @type {object} + * @property {string} connectionString - Account Connection String + * @property {object} credential - instance of Azure Credentials. Can be `DefaultAzureCredential`, `StorageSharedKeyCredential`, etc. + */ + +export type AzureCredential = { + /** + * URL of your queue + * @example + * `https://{accountName}.blob.core.windows.net` + */ + queueUrl: string + /** + * Instance of Credential class which holds the authorization needed + * @example + * new DefaultAzureCredential() or + * new AnonymousCredential() or + * new StorageSharedKeyCredential(account, accountKey) + */ + credential: StorageSharedKeyCredential | AnonymousCredential +} + +export type QueueConnection = string | QueueServiceClient | AzureCredential diff --git a/src/utils.ts b/src/utils.ts deleted file mode 100644 index e83fc9e..0000000 --- a/src/utils.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { DequeuedMessageItem } from '@azure/storage-queue' - -/** - * QueueOptions interface - * @interface - */ -export interface QueueOptions { - /** - * Polling time in seconds - * @default 10 - */ - pollingTime: number - /** - * Maximum number of times to try - * @default 4 - */ - maxTries?: number - /** - * Number of messages to receieve - * @default 1 - */ - numberOfMessages?: number -} - -export class QueueError extends Error { - code: string - message: string - constructor(code: string, message: string) { - super(`${code}:${message}`) - this.code = code - this.message = message - } -} - -/** - * @type {Function} - * @param {DequeuedMessageItem[]} messages - */ -export type HandlerFunction = (messages: DequeuedMessageItem[]) => void diff --git a/tests/consumer.test.ts b/tests/consumer.test.ts index ec3acef..bc3abca 100644 --- a/tests/consumer.test.ts +++ b/tests/consumer.test.ts @@ -50,7 +50,7 @@ describe('azure queue tests', () => { const errorToThrow = { code: 'code', message: 'message' } try { - vi.spyOn(QueueServiceClient, 'fromConnectionString').mockImplementation((..._args: string[]) => { + vi.spyOn(QueueServiceClient, 'fromConnectionString').mockImplementation((...args: unknown) => { const getQueueClient = vi.fn(() => { const createIfNotExists = vi.fn(() => new Promise((resolve, reject) => reject(errorToThrow))) @@ -59,12 +59,20 @@ describe('azure queue tests', () => { return { getQueueClient } }) - const k = new AzureQueueConsumer('test', 'https://test.com', (...args) => {}) + new AzureQueueConsumer('test', 'https://test.com', (...args) => {}) await flushPromises() } catch (error) { console.error(error) } }) + describe('Queue Connection tests', () => { + it('should throw an error if connection is invalid', () => { + expect.assertions(1) + expect(() => new AzureQueueConsumer('test', ['hello'], (arg) => {})).toThrowError( + 'Queue Connection provided was invalid' + ) + }) + }) }) describe('listener tests', () => { it('should test for queue message', async () => {