From 1e3ca4580131f7af3b6ae28bf7017409e42338ca Mon Sep 17 00:00:00 2001 From: Azure SDK Bot <53356347+azure-sdk@users.noreply.github.com> Date: Tue, 10 Mar 2020 19:21:34 -0700 Subject: [PATCH 01/28] Increment version for identity releases (#7753) update dependency to be pinned to preview1 Co-authored-by: Karishma Ghiya --- common/config/rush/pnpm-lock.yaml | 54 ++++++++++++++----- .../app-configuration/package.json | 2 +- sdk/core/core-amqp/package.json | 2 +- sdk/core/core-lro/package.json | 2 +- sdk/eventhub/event-hubs/package.json | 2 +- sdk/identity/identity/CHANGELOG.md | 3 ++ sdk/identity/identity/package.json | 2 +- .../manual-integration/AzureVM/package.json | 2 +- .../Cloudshell/package.json | 2 +- .../Kubernetes/package.json | 2 +- .../keyvault-certificates/package.json | 2 +- sdk/keyvault/keyvault-keys/package.json | 2 +- sdk/keyvault/keyvault-secrets/package.json | 2 +- sdk/servicebus/service-bus/package.json | 2 +- sdk/storage/storage-blob/package.json | 2 +- .../storage-file-datalake/package.json | 2 +- .../samples/package.json | 2 +- sdk/storage/storage-queue/package.json | 2 +- .../ai-text-analytics/package.json | 2 +- 19 files changed, 61 insertions(+), 30 deletions(-) diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index f0f44003bf65..3ef9835801a3 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -132,6 +132,21 @@ packages: dev: false resolution: integrity: sha512-CxaMaEjwtsmIhWtjHyGimKO7RmES0YxPqGQ9+jKqGygNlhG5NYHktDaiQu6w7k3g+I51VaLXtVSt+BVFd6VWfQ== + /@azure/identity/1.1.0-preview1: + dependencies: + '@azure/core-http': 1.0.4 + '@azure/core-tracing': 1.0.0-preview.7 + '@azure/logger': 1.0.0 + '@opentelemetry/types': 0.2.0 + events: 3.1.0 + jws: 3.2.2 + msal: 1.2.1 + qs: 6.9.1 + tslib: 1.11.1 + uuid: 3.4.0 + dev: false + resolution: + integrity: sha512-8IYH4kWHXCqn1t5VIQmg5FugVoUoTK4rKI7/liJFpNRWdcuzLjNQe+qy9UinQxfXsjwx8H0zjW5yC/l2QNDb1Q== /@azure/logger-js/1.3.2: dependencies: tslib: 1.11.1 @@ -7416,6 +7431,7 @@ packages: 'file:projects/ai-text-analytics.tgz': dependencies: '@azure/core-tracing': 1.0.0-preview.7 + '@azure/identity': 1.1.0-preview1 '@microsoft/api-extractor': 7.7.8 '@opentelemetry/types': 0.2.0 '@rollup/plugin-commonjs': 11.0.2_rollup@1.32.1 @@ -7470,12 +7486,13 @@ packages: dev: false name: '@rush-temp/ai-text-analytics' resolution: - integrity: sha512-Um+NUwa7KPM15say2ER1p9p+ZIHhpN46nzmO/9ojpC9djUpaugd3wHjgXDnwCnvVSj+SvLAMo4SGV33yRjcNjQ== + integrity: sha512-HXQ7sEeyDnFz7wr8i1YCn/Oq/eUTHNrRxSYBhtCm/4fBw280nhCz4C6hS2x8xp2doCeJAgzouKfQ2tQ6p40S9A== tarball: 'file:projects/ai-text-analytics.tgz' version: 0.0.0 'file:projects/app-configuration.tgz': dependencies: '@azure/core-tracing': 1.0.0-preview.7 + '@azure/identity': 1.1.0-preview1 '@microsoft/api-extractor': 7.7.8 '@opentelemetry/types': 0.2.0 '@rollup/plugin-commonjs': 11.0.2_rollup@1.32.1 @@ -7512,12 +7529,13 @@ packages: dev: false name: '@rush-temp/app-configuration' resolution: - integrity: sha512-sv3ZUV00GjqH2PAD1ws/viTUB7y3EzJ+gcgDaxiFgZznhA50Lv5pyZA1LYe38amO73GCGNoqVZEyd2k6zxFAzA== + integrity: sha512-TtffWKbKueVi1E1QiOQaX8DdMjh+Dy1ON0AzlQu2VwqHBq/P6NHLOX4+fzO1P8SQUZ60OSmodQv7Szo7xB4vKQ== tarball: 'file:projects/app-configuration.tgz' version: 0.0.0 'file:projects/core-amqp.tgz': dependencies: '@azure/eslint-plugin-azure-sdk': 2.0.1_ee45056a3a53530214d828c032d67185 + '@azure/identity': 1.1.0-preview1 '@rollup/plugin-commonjs': 11.0.2_rollup@1.32.1 '@rollup/plugin-inject': 4.0.1_rollup@1.32.1 '@rollup/plugin-json': 4.0.2_rollup@1.32.1 @@ -7579,7 +7597,7 @@ packages: dev: false name: '@rush-temp/core-amqp' resolution: - integrity: sha512-sxMktzz6n0rXPjcxfqa8Xp9fERATm7HwezEVZCWMMjhX/BdHGhrAccmhgDsgJHR/ZWNTtr4afH121EHPFjsofw== + integrity: sha512-+I8NcHJFPvaMTI9zXx5pIrRYdlVUv4gSYrVvwp8RsBd/lt92S6wfpdQH0W3tXqtLLcDRI7GUQdFyVeYbdsHZPQ== tarball: 'file:projects/core-amqp.tgz' version: 0.0.0 'file:projects/core-arm.tgz': @@ -7756,6 +7774,7 @@ packages: 'file:projects/core-lro.tgz': dependencies: '@azure/core-arm': 1.0.0-preview.7 + '@azure/identity': 1.1.0-preview1 '@microsoft/api-extractor': 7.7.8 '@opentelemetry/types': 0.2.0 '@rollup/plugin-commonjs': 11.0.2_rollup@1.32.1 @@ -7806,7 +7825,7 @@ packages: dev: false name: '@rush-temp/core-lro' resolution: - integrity: sha512-2IHfMCvOaTuZMdgFWVtKAWcWOxJntLxn+SaDCpojnMRei6pjOoE4C3uTO2Rm8aCtgZqjM4r+suoEegMQ6KMOLg== + integrity: sha512-Q2ycvTiywEt2t//BciGEsxAoAkoUznSrG/mwOj1ssneeb1Yln9zrgPTBr3OxsZpRRoZuIdqJOmKKXAR2ajVw6w== tarball: 'file:projects/core-lro.tgz' version: 0.0.0 'file:projects/core-paging.tgz': @@ -7962,13 +7981,14 @@ packages: dev: false name: '@rush-temp/eslint-plugin-azure-sdk' resolution: - integrity: sha512-6a3mK8Z0237JBYz9RpmEKLuLGDNYdJ+jANYzUZa7VEDU5UGqSi+RpgKKe4oP5BTb6Td7FlH9JALSWDMP1HeO4w== + integrity: sha512-aMdxopTddXUy77fzX6jtFk9DIX9Hx8A77E+39V72mYUTU4YCv2Um4vKccChPLOMWbECP/XsEzfe9zT8kaR58/g== tarball: 'file:projects/eslint-plugin-azure-sdk.tgz' version: 0.0.0 'file:projects/event-hubs.tgz': dependencies: '@azure/core-tracing': 1.0.0-preview.7 '@azure/eslint-plugin-azure-sdk': 2.0.1_ee45056a3a53530214d828c032d67185 + '@azure/identity': 1.1.0-preview1 '@microsoft/api-extractor': 7.7.8 '@opentelemetry/types': 0.2.0 '@rollup/plugin-commonjs': 11.0.2_rollup@1.32.1 @@ -8042,7 +8062,7 @@ packages: dev: false name: '@rush-temp/event-hubs' resolution: - integrity: sha512-eWg8gnLGWqup9/iExkXLtDe6SC2u0JkOYx4/qNm1klvNToNf8tWYnmDSWY8V5z82E9chx/2m88hyDxW5plkDFA== + integrity: sha512-i6rPyYLlb/THBjrZMAcWFweSyM4g3rK7Bqpwzlo4RFGQvNil4ea1Ma0TSDYvUPXIWXybSLPKtelORdMLqCb0Hg== tarball: 'file:projects/event-hubs.tgz' version: 0.0.0 'file:projects/event-processor-host.tgz': @@ -8228,6 +8248,7 @@ packages: dependencies: '@azure/core-tracing': 1.0.0-preview.7 '@azure/eslint-plugin-azure-sdk': 2.0.1_ee45056a3a53530214d828c032d67185 + '@azure/identity': 1.1.0-preview1 '@microsoft/api-extractor': 7.7.8 '@opentelemetry/types': 0.2.0 '@rollup/plugin-commonjs': 11.0.2_rollup@1.32.1 @@ -8287,13 +8308,14 @@ packages: dev: false name: '@rush-temp/keyvault-certificates' resolution: - integrity: sha512-+DODawVUSxEp6FJdhS2j7JZoWiQzIOpWZdqG3zBpDJMDs9w2ilYZJiLKScKYFQxZUB/rpsdQ7RuQ2Y1BH7RZ3w== + integrity: sha512-kcF1QeALb78k1ebetS9RMvQ+ERE64bZ+6VbIljX9kFK8eCbWchkiM0VvY3z+4S/L5HAnQ0o7rqdRjgwBqP4w7A== tarball: 'file:projects/keyvault-certificates.tgz' version: 0.0.0 'file:projects/keyvault-keys.tgz': dependencies: '@azure/core-tracing': 1.0.0-preview.7 '@azure/eslint-plugin-azure-sdk': 2.0.1_ee45056a3a53530214d828c032d67185 + '@azure/identity': 1.1.0-preview1 '@microsoft/api-extractor': 7.7.8 '@opentelemetry/types': 0.2.0 '@rollup/plugin-commonjs': 11.0.2_rollup@1.32.1 @@ -8353,13 +8375,14 @@ packages: dev: false name: '@rush-temp/keyvault-keys' resolution: - integrity: sha512-0vygGHI4s5a60mcWBdrm3muCdhu4Crh32//S2FePTIMOhplNh5vEPOnSKgC4FVrOul/mUPHECM/Om+vSxYr88A== + integrity: sha512-WRzB1mONnSK1/qZdgaW2rO6QRIZLju/WDPqMn0gj+GOznT01XkpB8VMPQ5B0zRwk/N4g/rxGFCULHZ3YK+2/mQ== tarball: 'file:projects/keyvault-keys.tgz' version: 0.0.0 'file:projects/keyvault-secrets.tgz': dependencies: '@azure/core-tracing': 1.0.0-preview.7 '@azure/eslint-plugin-azure-sdk': 2.0.1_ee45056a3a53530214d828c032d67185 + '@azure/identity': 1.1.0-preview1 '@microsoft/api-extractor': 7.7.8 '@opentelemetry/types': 0.2.0 '@rollup/plugin-commonjs': 11.0.2_rollup@1.32.1 @@ -8419,7 +8442,7 @@ packages: dev: false name: '@rush-temp/keyvault-secrets' resolution: - integrity: sha512-qWGD1JT4V4V8UDYlMzYstLo21UXvYDKYEB3LjIrNUlTSOlH6VciaPleN4b3kqTw4KENIBI8KED8g6P26fyJ0Jw== + integrity: sha512-MTwfUVkm4XKNPiROswoPlGtuaY8HX+79Lz3JGhWNCxAnSpVQyMirlis1GwmnTWckVU5xV6bY1vahUaqgw4N0hQ== tarball: 'file:projects/keyvault-secrets.tgz' version: 0.0.0 'file:projects/logger.tgz': @@ -8535,6 +8558,7 @@ packages: 'file:projects/service-bus.tgz': dependencies: '@azure/eslint-plugin-azure-sdk': 2.0.1_ee45056a3a53530214d828c032d67185 + '@azure/identity': 1.1.0-preview1 '@microsoft/api-extractor': 7.7.8 '@opentelemetry/types': 0.2.0 '@rollup/plugin-commonjs': 11.0.2_rollup@1.32.1 @@ -8607,12 +8631,13 @@ packages: dev: false name: '@rush-temp/service-bus' resolution: - integrity: sha512-whN73iXQ3X95mRAKe7BrddlqHTXfLTwcRMg1TJDY29V73HVmTAc0Enk55fSlvfe4eAKQu0gZvgdhoSzwCqjUgQ== + integrity: sha512-o01tMu6B1o7ABcN8vMFmRH8NMBWva5VkUaxvEVItRm65o5FLyhLvmg39KkmEz6JdgD16WulE+uEbIUdl0Yd+iA== tarball: 'file:projects/service-bus.tgz' version: 0.0.0 'file:projects/storage-blob.tgz': dependencies: '@azure/core-tracing': 1.0.0-preview.7 + '@azure/identity': 1.1.0-preview1 '@microsoft/api-extractor': 7.7.8 '@opentelemetry/types': 0.2.0 '@rollup/plugin-commonjs': 11.0.2_rollup@1.32.1 @@ -8669,12 +8694,13 @@ packages: dev: false name: '@rush-temp/storage-blob' resolution: - integrity: sha512-8jw0JAmF2HShfNEwFrzSRtRjaEuOGTVj03dySjZih5S/1n7lr+pqIzfbERLB3G2mc9TxYMU0wCfYDJ+TxAGUFQ== + integrity: sha512-JjNNxByexMxaQqbBHffzgp2DnG6gb2nRs/IILCB1d9r3SZ3/t1axzwzHbxQU3fmXN6hOO0brGqYe45IN9SsBPg== tarball: 'file:projects/storage-blob.tgz' version: 0.0.0 'file:projects/storage-file-datalake.tgz': dependencies: '@azure/core-tracing': 1.0.0-preview.7 + '@azure/identity': 1.1.0-preview1 '@microsoft/api-extractor': 7.7.8 '@opentelemetry/types': 0.2.0 '@rollup/plugin-commonjs': 11.0.2_rollup@1.32.1 @@ -8739,7 +8765,7 @@ packages: dev: false name: '@rush-temp/storage-file-datalake' resolution: - integrity: sha512-h6lfNb5K0pIYcBeT0TOGVTbjVSpin1iw653zou6CNsQY+a772HkN3QWQF1EvyZ1XrxOiJTdQhIuY1zhIjWOiHg== + integrity: sha512-YJACP135onaEKbupR2QZYFSf2be3vfYYimWmKDuE4pocBhObK1ylt/AnJA2QrGwItvok2z3xj0jLGE/F6JTCag== tarball: 'file:projects/storage-file-datalake.tgz' version: 0.0.0 'file:projects/storage-file-share.tgz': @@ -8807,6 +8833,7 @@ packages: 'file:projects/storage-queue.tgz': dependencies: '@azure/core-tracing': 1.0.0-preview.7 + '@azure/identity': 1.1.0-preview1 '@microsoft/api-extractor': 7.7.8 '@opentelemetry/types': 0.2.0 '@rollup/plugin-commonjs': 11.0.2_rollup@1.32.1 @@ -8862,7 +8889,7 @@ packages: dev: false name: '@rush-temp/storage-queue' resolution: - integrity: sha512-3gI2TYigIBQ3vFaW5jNc4IPXHkM0eo7IzQcRPuqKWDsrXH4XkSPtwOYvG1Phn7lWpyDtpZp9PyOPg9BiCXYmQg== + integrity: sha512-TYoGzVK8a27ch4yuWj4d/UkDPJNYdQH/d+0ToM7+10G/9384QHPcNRcniMqyVkrDZL8D/4rgfHMqudpxmUA+Lg== tarball: 'file:projects/storage-queue.tgz' version: 0.0.0 'file:projects/template.tgz': @@ -8998,6 +9025,7 @@ packages: integrity: sha512-guDU8PdEdKCVnGxNd1JEkmqukDoc1wodkEqQCWpY1+bX4ZT+ZY520gfVcMeMHYCEO8TAAhScGNke/y7p9qBArA== tarball: 'file:projects/testhub.tgz' version: 0.0.0 +registry: '' specifiers: '@rush-temp/abort-controller': 'file:./projects/abort-controller.tgz' '@rush-temp/ai-text-analytics': 'file:./projects/ai-text-analytics.tgz' diff --git a/sdk/appconfiguration/app-configuration/package.json b/sdk/appconfiguration/app-configuration/package.json index 0ab5ab1694ab..17f60c4d5457 100644 --- a/sdk/appconfiguration/app-configuration/package.json +++ b/sdk/appconfiguration/app-configuration/package.json @@ -81,7 +81,7 @@ "tslib": "^1.10.0" }, "devDependencies": { - "@azure/identity": "^1.1.0-preview1", + "@azure/identity": "1.1.0-preview1", "@microsoft/api-extractor": "^7.5.4", "@rollup/plugin-commonjs": "^11.0.1", "@rollup/plugin-multi-entry": "^3.0.0", diff --git a/sdk/core/core-amqp/package.json b/sdk/core/core-amqp/package.json index f3eaef83d8cc..3978bc23823b 100644 --- a/sdk/core/core-amqp/package.json +++ b/sdk/core/core-amqp/package.json @@ -76,7 +76,7 @@ }, "devDependencies": { "@azure/eslint-plugin-azure-sdk": "^2.0.1", - "@azure/identity": "^1.1.0-preview1", + "@azure/identity": "1.1.0-preview1", "@rollup/plugin-commonjs": "^11.0.1", "@rollup/plugin-inject": "^4.0.0", "@rollup/plugin-json": "^4.0.0", diff --git a/sdk/core/core-lro/package.json b/sdk/core/core-lro/package.json index c3abf2e91f65..09d5e3cbbc6f 100644 --- a/sdk/core/core-lro/package.json +++ b/sdk/core/core-lro/package.json @@ -103,7 +103,7 @@ }, "devDependencies": { "@azure/core-arm": "1.0.0-preview.7", - "@azure/identity": "^1.1.0-preview1", + "@azure/identity": "1.1.0-preview1", "@microsoft/api-extractor": "^7.5.4", "@rollup/plugin-commonjs": "^11.0.1", "@rollup/plugin-multi-entry": "^3.0.0", diff --git a/sdk/eventhub/event-hubs/package.json b/sdk/eventhub/event-hubs/package.json index fd14f2fba5be..b74c62913324 100644 --- a/sdk/eventhub/event-hubs/package.json +++ b/sdk/eventhub/event-hubs/package.json @@ -88,7 +88,7 @@ }, "devDependencies": { "@azure/eslint-plugin-azure-sdk": "^2.0.1", - "@azure/identity": "^1.1.0-preview1", + "@azure/identity": "1.1.0-preview1", "@microsoft/api-extractor": "^7.5.4", "@rollup/plugin-commonjs": "^11.0.1", "@rollup/plugin-inject": "^4.0.0", diff --git a/sdk/identity/identity/CHANGELOG.md b/sdk/identity/identity/CHANGELOG.md index e051eb83feab..78fe483c7801 100644 --- a/sdk/identity/identity/CHANGELOG.md +++ b/sdk/identity/identity/CHANGELOG.md @@ -1,5 +1,8 @@ # Release History +## 1.1.0-preview.2 (Unreleased) + + ## 1.1.0-preview1 (2020-03-10) - Extended DefaultAzureCredential with an experimental credential that uses the login credential from Azure CLI diff --git a/sdk/identity/identity/package.json b/sdk/identity/identity/package.json index 61af2368134c..e836c8f34305 100644 --- a/sdk/identity/identity/package.json +++ b/sdk/identity/identity/package.json @@ -1,7 +1,7 @@ { "name": "@azure/identity", "sdk-type": "client", - "version": "1.1.0-preview1", + "version": "1.1.0-preview.2", "description": "Provides credential implementations for Azure SDK libraries that can authenticate with Azure Active Directory", "main": "dist/index.js", "module": "dist-esm/src/index.js", diff --git a/sdk/identity/identity/test/manual-integration/AzureVM/package.json b/sdk/identity/identity/test/manual-integration/AzureVM/package.json index de4f5d830b26..bf3a2a5b8f84 100644 --- a/sdk/identity/identity/test/manual-integration/AzureVM/package.json +++ b/sdk/identity/identity/test/manual-integration/AzureVM/package.json @@ -9,7 +9,7 @@ "author": "", "license": "ISC", "dependencies": { - "@azure/identity": "^1.1.0-preview1", + "@azure/identity": "1.1.0-preview1", "@azure/keyvault-secrets": "^4.0.2" } } diff --git a/sdk/identity/identity/test/manual-integration/Cloudshell/package.json b/sdk/identity/identity/test/manual-integration/Cloudshell/package.json index c8f8c8817f96..bb1aead780f1 100644 --- a/sdk/identity/identity/test/manual-integration/Cloudshell/package.json +++ b/sdk/identity/identity/test/manual-integration/Cloudshell/package.json @@ -9,7 +9,7 @@ "author": "", "license": "ISC", "dependencies": { - "@azure/identity": "^1.1.0-preview1", + "@azure/identity": "1.1.0-preview1", "@azure/keyvault-secrets": "^4.0.2" } } diff --git a/sdk/identity/identity/test/manual-integration/Kubernetes/package.json b/sdk/identity/identity/test/manual-integration/Kubernetes/package.json index 4d6cba2c5d7b..ed86521ab704 100644 --- a/sdk/identity/identity/test/manual-integration/Kubernetes/package.json +++ b/sdk/identity/identity/test/manual-integration/Kubernetes/package.json @@ -11,7 +11,7 @@ "dependencies": { "yargs": "15.1.0", "@types/yargs": "15.0.3", - "@azure/identity": "^1.1.0-preview1", + "@azure/identity": "1.1.0-preview1", "@azure/keyvault-secrets": "^4.0.2" } } diff --git a/sdk/keyvault/keyvault-certificates/package.json b/sdk/keyvault/keyvault-certificates/package.json index 22a7a8f8c25b..c66169d86dd8 100644 --- a/sdk/keyvault/keyvault-certificates/package.json +++ b/sdk/keyvault/keyvault-certificates/package.json @@ -97,7 +97,7 @@ }, "devDependencies": { "@azure/eslint-plugin-azure-sdk": "^2.0.1", - "@azure/identity": "^1.1.0-preview1", + "@azure/identity": "1.1.0-preview1", "@azure/keyvault-keys": "^4.1.0-preview.1", "@azure/keyvault-secrets": "^4.1.0-preview.1", "@azure/test-utils-recorder": "^1.0.0", diff --git a/sdk/keyvault/keyvault-keys/package.json b/sdk/keyvault/keyvault-keys/package.json index 50d8aacc9e66..cedad7988807 100644 --- a/sdk/keyvault/keyvault-keys/package.json +++ b/sdk/keyvault/keyvault-keys/package.json @@ -95,7 +95,7 @@ "devDependencies": { "@azure/abort-controller": "^1.0.0", "@azure/eslint-plugin-azure-sdk": "^2.0.1", - "@azure/identity": "^1.1.0-preview1", + "@azure/identity": "1.1.0-preview1", "@azure/test-utils-recorder": "^1.0.0", "@microsoft/api-extractor": "^7.5.4", "@rollup/plugin-commonjs": "^11.0.1", diff --git a/sdk/keyvault/keyvault-secrets/package.json b/sdk/keyvault/keyvault-secrets/package.json index 650412770757..74289e9f4211 100644 --- a/sdk/keyvault/keyvault-secrets/package.json +++ b/sdk/keyvault/keyvault-secrets/package.json @@ -95,7 +95,7 @@ }, "devDependencies": { "@azure/eslint-plugin-azure-sdk": "^2.0.1", - "@azure/identity": "^1.1.0-preview1", + "@azure/identity": "1.1.0-preview1", "@azure/test-utils-recorder": "^1.0.0", "@microsoft/api-extractor": "^7.5.4", "@rollup/plugin-commonjs": "^11.0.1", diff --git a/sdk/servicebus/service-bus/package.json b/sdk/servicebus/service-bus/package.json index e4ca9661d5dc..ec6474d43c6a 100644 --- a/sdk/servicebus/service-bus/package.json +++ b/sdk/servicebus/service-bus/package.json @@ -92,7 +92,7 @@ }, "devDependencies": { "@azure/eslint-plugin-azure-sdk": "^2.0.1", - "@azure/identity": "^1.1.0-preview1", + "@azure/identity": "1.1.0-preview1", "@microsoft/api-extractor": "^7.5.4", "@rollup/plugin-commonjs": "^11.0.1", "@rollup/plugin-inject": "^4.0.0", diff --git a/sdk/storage/storage-blob/package.json b/sdk/storage/storage-blob/package.json index f72a767811d7..9115fb7c87cc 100644 --- a/sdk/storage/storage-blob/package.json +++ b/sdk/storage/storage-blob/package.json @@ -110,7 +110,7 @@ "tslib": "^1.10.0" }, "devDependencies": { - "@azure/identity": "^1.1.0-preview1", + "@azure/identity": "1.1.0-preview1", "@azure/test-utils-recorder": "^1.0.0", "@microsoft/api-extractor": "^7.5.4", "@rollup/plugin-multi-entry": "^3.0.0", diff --git a/sdk/storage/storage-file-datalake/package.json b/sdk/storage/storage-file-datalake/package.json index 17421268beda..332c5703c364 100644 --- a/sdk/storage/storage-file-datalake/package.json +++ b/sdk/storage/storage-file-datalake/package.json @@ -105,7 +105,7 @@ "tslib": "^1.10.0" }, "devDependencies": { - "@azure/identity": "^1.1.0-preview1", + "@azure/identity": "1.1.0-preview1", "@azure/test-utils-recorder": "^1.0.0", "@microsoft/api-extractor": "^7.5.4", "@opentelemetry/types": "^0.2.0", diff --git a/sdk/storage/storage-file-datalake/samples/package.json b/sdk/storage/storage-file-datalake/samples/package.json index 2a372a26f8b2..aac0f788647c 100644 --- a/sdk/storage/storage-file-datalake/samples/package.json +++ b/sdk/storage/storage-file-datalake/samples/package.json @@ -10,7 +10,7 @@ "author": "", "license": "MIT", "dependencies": { - "@azure/identity": "^1.1.0-preview1", + "@azure/identity": "1.1.0-preview1", "@azure/storage-file-datalake": "^12.0.0", "execa": "^3.2.0" } diff --git a/sdk/storage/storage-queue/package.json b/sdk/storage/storage-queue/package.json index e33def3c1a48..a793c7009131 100644 --- a/sdk/storage/storage-queue/package.json +++ b/sdk/storage/storage-queue/package.json @@ -108,7 +108,7 @@ "tslib": "^1.10.0" }, "devDependencies": { - "@azure/identity": "^1.1.0-preview1", + "@azure/identity": "1.1.0-preview1", "@azure/test-utils-recorder": "^1.0.0", "@microsoft/api-extractor": "^7.5.4", "@rollup/plugin-commonjs": "^11.0.1", diff --git a/sdk/textanalytics/ai-text-analytics/package.json b/sdk/textanalytics/ai-text-analytics/package.json index 00a57fc7233b..c0da0e242024 100644 --- a/sdk/textanalytics/ai-text-analytics/package.json +++ b/sdk/textanalytics/ai-text-analytics/package.json @@ -80,7 +80,7 @@ "tslib": "^1.10.0" }, "devDependencies": { - "@azure/identity": "^1.1.0-preview1", + "@azure/identity": "1.1.0-preview1", "@azure/test-utils-recorder": "^1.0.0", "@microsoft/api-extractor": "^7.5.4", "@rollup/plugin-commonjs": "^11.0.1", From 91183197536a263916b9277d99250159f84bee80 Mon Sep 17 00:00:00 2001 From: Liang Wang Date: Wed, 11 Mar 2020 11:27:17 +0800 Subject: [PATCH 02/28] Fix Readme File for Subscription Service (#7740) --- sdk/subscription/arm-subscriptions/README.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/sdk/subscription/arm-subscriptions/README.md b/sdk/subscription/arm-subscriptions/README.md index 9d11b8444df1..a4549f0fae61 100644 --- a/sdk/subscription/arm-subscriptions/README.md +++ b/sdk/subscription/arm-subscriptions/README.md @@ -31,12 +31,10 @@ import * as msRest from "@azure/ms-rest-js"; import * as msRestAzure from "@azure/ms-rest-azure-js"; import * as msRestNodeAuth from "@azure/ms-rest-nodeauth"; import { SubscriptionClient, SubscriptionModels, SubscriptionMappers } from "@azure/arm-subscriptions"; -const subscriptionId = process.env["AZURE_SUBSCRIPTION_ID"]; - + msRestNodeAuth.interactiveLogin().then((creds) => { - const client = new SubscriptionClient(creds, subscriptionId); - const subscriptionId = "testsubscriptionId"; - client.subscriptions.listLocations(subscriptionId).then((result) => { + const client = new SubscriptionClient(creds); + client.subscriptions.list().then((result) => { console.log("The result is:"); console.log(result); }); From 0884197f56986552951d9dbbe6f6bec89cc4e540 Mon Sep 17 00:00:00 2001 From: Azure SDK Bot <53356347+azure-sdk@users.noreply.github.com> Date: Wed, 11 Mar 2020 06:31:28 -0700 Subject: [PATCH 03/28] Increment version for keyvault releases (#7761) * Increment package version after release of azure-keyvault-secrets * Increment package version after release of azure-keyvault-keys * Increment package version after release of azure-keyvault-certificates --- sdk/keyvault/keyvault-certificates/CHANGELOG.md | 3 +++ sdk/keyvault/keyvault-certificates/package.json | 2 +- .../keyvault-certificates/src/core/keyVaultClientContext.ts | 2 +- sdk/keyvault/keyvault-certificates/src/core/utils/constants.ts | 2 +- sdk/keyvault/keyvault-keys/CHANGELOG.md | 3 +++ sdk/keyvault/keyvault-keys/package.json | 2 +- sdk/keyvault/keyvault-keys/src/core/keyVaultClientContext.ts | 2 +- sdk/keyvault/keyvault-keys/src/core/utils/constants.ts | 2 +- sdk/keyvault/keyvault-secrets/CHANGELOG.md | 3 +++ sdk/keyvault/keyvault-secrets/package.json | 2 +- .../keyvault-secrets/src/core/keyVaultClientContext.ts | 2 +- sdk/keyvault/keyvault-secrets/src/core/utils/constants.ts | 2 +- 12 files changed, 18 insertions(+), 9 deletions(-) diff --git a/sdk/keyvault/keyvault-certificates/CHANGELOG.md b/sdk/keyvault/keyvault-certificates/CHANGELOG.md index ad37755f6a44..4de34575ea4e 100644 --- a/sdk/keyvault/keyvault-certificates/CHANGELOG.md +++ b/sdk/keyvault/keyvault-certificates/CHANGELOG.md @@ -1,5 +1,8 @@ # Release History +## 4.1.0-preview.2 (Unreleased) + + ## 4.1.0-preview.1 (2020-03-10) - Added the optional `apiVersion` property to the `CertificateClient` optional parameters. diff --git a/sdk/keyvault/keyvault-certificates/package.json b/sdk/keyvault/keyvault-certificates/package.json index c66169d86dd8..98fe169fe9a6 100644 --- a/sdk/keyvault/keyvault-certificates/package.json +++ b/sdk/keyvault/keyvault-certificates/package.json @@ -2,7 +2,7 @@ "name": "@azure/keyvault-certificates", "sdk-type": "client", "author": "Microsoft Corporation", - "version": "4.1.0-preview.1", + "version": "4.1.0-preview.2", "license": "MIT", "description": "Isomorphic client library for Azure KeyVault's certificates.", "homepage": "https://github.com/Azure/azure-sdk-for-js/blob/master/sdk/keyvault/keyvault-certificates/README.md", diff --git a/sdk/keyvault/keyvault-certificates/src/core/keyVaultClientContext.ts b/sdk/keyvault/keyvault-certificates/src/core/keyVaultClientContext.ts index 614b0e739bc4..93723747b9b5 100644 --- a/sdk/keyvault/keyvault-certificates/src/core/keyVaultClientContext.ts +++ b/sdk/keyvault/keyvault-certificates/src/core/keyVaultClientContext.ts @@ -11,7 +11,7 @@ import * as coreHttp from "@azure/core-http"; const packageName = "@azure/keyvault-certificates"; -export const packageVersion = "4.1.0-preview.1"; +export const packageVersion = "4.1.0-preview.2"; export class KeyVaultClientContext extends coreHttp.ServiceClient { apiVersion: string; diff --git a/sdk/keyvault/keyvault-certificates/src/core/utils/constants.ts b/sdk/keyvault/keyvault-certificates/src/core/utils/constants.ts index f788f8e5355f..409e169dba81 100644 --- a/sdk/keyvault/keyvault-certificates/src/core/utils/constants.ts +++ b/sdk/keyvault/keyvault-certificates/src/core/utils/constants.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. -export const SDK_VERSION: string = "4.1.0-preview.1"; +export const SDK_VERSION: string = "4.1.0-preview.2"; export const RetryConstants = { MIN_RETRY_INTERVAL_MS: 3000 diff --git a/sdk/keyvault/keyvault-keys/CHANGELOG.md b/sdk/keyvault/keyvault-keys/CHANGELOG.md index 5f9412095862..a907f6a6ab8f 100644 --- a/sdk/keyvault/keyvault-keys/CHANGELOG.md +++ b/sdk/keyvault/keyvault-keys/CHANGELOG.md @@ -1,5 +1,8 @@ # Release History +## 4.1.0-preview.2 (Unreleased) + + ## 4.1.0-preview.1 (2020-03-10) - Added the optional `apiVersion` property to the `KeyClient` and `CryptographyClient` optional parameters. diff --git a/sdk/keyvault/keyvault-keys/package.json b/sdk/keyvault/keyvault-keys/package.json index cedad7988807..3d91c05f61a2 100644 --- a/sdk/keyvault/keyvault-keys/package.json +++ b/sdk/keyvault/keyvault-keys/package.json @@ -2,7 +2,7 @@ "name": "@azure/keyvault-keys", "sdk-type": "client", "author": "Microsoft Corporation", - "version": "4.1.0-preview.1", + "version": "4.1.0-preview.2", "license": "MIT", "description": "Isomorphic client library for Azure KeyVault's keys.", "homepage": "https://github.com/Azure/azure-sdk-for-js/blob/master/sdk/keyvault/keyvault-keys/README.md", diff --git a/sdk/keyvault/keyvault-keys/src/core/keyVaultClientContext.ts b/sdk/keyvault/keyvault-keys/src/core/keyVaultClientContext.ts index 5f4770e8b842..75881d8ea626 100644 --- a/sdk/keyvault/keyvault-keys/src/core/keyVaultClientContext.ts +++ b/sdk/keyvault/keyvault-keys/src/core/keyVaultClientContext.ts @@ -11,7 +11,7 @@ import * as coreHttp from "@azure/core-http"; const packageName = "@azure/keyvault-keys"; -export const packageVersion = "4.1.0-preview.1"; +export const packageVersion = "4.1.0-preview.2"; export class KeyVaultClientContext extends coreHttp.ServiceClient { apiVersion: string; diff --git a/sdk/keyvault/keyvault-keys/src/core/utils/constants.ts b/sdk/keyvault/keyvault-keys/src/core/utils/constants.ts index f788f8e5355f..409e169dba81 100644 --- a/sdk/keyvault/keyvault-keys/src/core/utils/constants.ts +++ b/sdk/keyvault/keyvault-keys/src/core/utils/constants.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. -export const SDK_VERSION: string = "4.1.0-preview.1"; +export const SDK_VERSION: string = "4.1.0-preview.2"; export const RetryConstants = { MIN_RETRY_INTERVAL_MS: 3000 diff --git a/sdk/keyvault/keyvault-secrets/CHANGELOG.md b/sdk/keyvault/keyvault-secrets/CHANGELOG.md index 3fd2c8dcbe0f..812f823f6991 100644 --- a/sdk/keyvault/keyvault-secrets/CHANGELOG.md +++ b/sdk/keyvault/keyvault-secrets/CHANGELOG.md @@ -1,5 +1,8 @@ # Release History +## 4.1.0-preview.2 (Unreleased) + + ## 4.1.0-preview.1 (2020-03-10) - Added the optional `apiVersion` property to the `SecretClient` optional parameters. diff --git a/sdk/keyvault/keyvault-secrets/package.json b/sdk/keyvault/keyvault-secrets/package.json index 74289e9f4211..7e65574b98f9 100644 --- a/sdk/keyvault/keyvault-secrets/package.json +++ b/sdk/keyvault/keyvault-secrets/package.json @@ -2,7 +2,7 @@ "name": "@azure/keyvault-secrets", "sdk-type": "client", "author": "Microsoft Corporation", - "version": "4.1.0-preview.1", + "version": "4.1.0-preview.2", "license": "MIT", "description": "Isomorphic client library for Azure KeyVault's secrets.", "homepage": "https://github.com/Azure/azure-sdk-for-js/blob/master/sdk/keyvault/keyvault-secrets/README.md", diff --git a/sdk/keyvault/keyvault-secrets/src/core/keyVaultClientContext.ts b/sdk/keyvault/keyvault-secrets/src/core/keyVaultClientContext.ts index 53c80245b449..4a5ee4b92fd4 100644 --- a/sdk/keyvault/keyvault-secrets/src/core/keyVaultClientContext.ts +++ b/sdk/keyvault/keyvault-secrets/src/core/keyVaultClientContext.ts @@ -11,7 +11,7 @@ import * as coreHttp from "@azure/core-http"; const packageName = "@azure/keyvault-secrets"; -export const packageVersion = "4.1.0-preview.1"; +export const packageVersion = "4.1.0-preview.2"; export class KeyVaultClientContext extends coreHttp.ServiceClient { apiVersion: string; diff --git a/sdk/keyvault/keyvault-secrets/src/core/utils/constants.ts b/sdk/keyvault/keyvault-secrets/src/core/utils/constants.ts index f788f8e5355f..409e169dba81 100644 --- a/sdk/keyvault/keyvault-secrets/src/core/utils/constants.ts +++ b/sdk/keyvault/keyvault-secrets/src/core/utils/constants.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. -export const SDK_VERSION: string = "4.1.0-preview.1"; +export const SDK_VERSION: string = "4.1.0-preview.2"; export const RetryConstants = { MIN_RETRY_INTERVAL_MS: 3000 From 0343a0d91a50d8b9813aea297dbacded43d4a6c8 Mon Sep 17 00:00:00 2001 From: Jeff Fisher Date: Wed, 11 Mar 2020 10:34:37 -0700 Subject: [PATCH 04/28] Use inlineSources to bundle source file contents inside map files (#7615) * Enable inlineSources and stop packaging TS files * Make all license texts consistent and include README.md Fixes #7706 --- .../rules/ts-package-json-files-required.ts | 17 --- .../rules/ts-package-json-files-required.ts | 103 ++----------- .../{LICENSE.txt => LICENSE} | 2 +- .../app-configuration/package.json | 4 +- .../app-configuration/tsconfig.json | 1 + sdk/core/abort-controller/LICENSE | 34 ++--- sdk/core/abort-controller/package.json | 4 +- sdk/core/core-amqp/LICENSE | 34 ++--- sdk/core/core-amqp/package.json | 6 +- .../LICENSE.txt => core/core-arm/LICENSE} | 2 +- sdk/core/core-arm/package.json | 1 - sdk/core/core-asynciterator-polyfill/LICENSE | 34 ++--- .../core-asynciterator-polyfill/package.json | 3 +- .../LICENSE.md => core/core-auth/LICENSE} | 4 +- sdk/core/core-auth/package.json | 5 +- sdk/core/core-http/LICENSE | 34 ++--- sdk/core/core-http/ThirdPartyNotices.txt | 55 ------- sdk/core/core-http/package.json | 5 +- .../LICENSE.TXT => core/core-lro/LICENSE} | 6 +- sdk/core/core-lro/package.json | 5 +- sdk/core/core-paging/LICENSE | 34 ++--- sdk/core/core-paging/tsconfig.json | 1 + sdk/core/core-tracing/LICENSE | 21 +++ sdk/core/core-tracing/LICENSE.txt | 21 --- sdk/core/core-tracing/package.json | 4 +- sdk/core/logger/LICENSE | 34 ++--- sdk/core/logger/package.json | 4 +- sdk/cosmosdb/cosmos/LICENSE | 34 ++--- sdk/cosmosdb/cosmos/package.json | 5 +- sdk/cosmosdb/cosmos/src/tsconfig.json | 1 + sdk/eventhub/event-hubs/License | 35 +++-- sdk/eventhub/event-hubs/package.json | 4 +- sdk/eventhub/event-hubs/tsconfig.json | 1 + sdk/eventhub/event-processor-host/License | 34 ++--- .../event-processor-host/package.json | 4 +- .../event-processor-host/tsconfig.json | 1 + .../eventhubs-checkpointstore-blob/License | 35 +++-- .../package.json | 4 +- .../tsconfig.json | 1 + sdk/eventhub/testhub/LICENSE | 21 +++ sdk/eventhub/testhub/tsconfig.json | 20 ++- sdk/identity/identity/LICENSE | 21 +++ sdk/identity/identity/package.json | 5 +- sdk/identity/identity/tsconfig.json | 2 +- .../keyvault-certificates/.eslintrc.json | 3 +- sdk/keyvault/keyvault-certificates/LICENSE | 21 +++ .../ThirdPartyNotices.txt | 135 ------------------ .../keyvault-certificates/package.json | 7 +- .../keyvault-certificates/tsconfig.json | 1 + sdk/keyvault/keyvault-keys/LICENSE | 21 +++ sdk/keyvault/keyvault-keys/LICENSE.TXT | 21 --- .../keyvault-keys/ThirdPartyNotices.txt | 135 ------------------ sdk/keyvault/keyvault-keys/package.json | 6 +- sdk/keyvault/keyvault-keys/tsconfig.json | 1 + sdk/keyvault/keyvault-secrets/LICENSE | 21 +++ sdk/keyvault/keyvault-secrets/package.json | 6 +- sdk/keyvault/keyvault-secrets/tsconfig.json | 1 + sdk/search/search/LICENSE | 21 +++ sdk/search/search/package.json | 5 +- sdk/servicebus/service-bus/License | 6 +- sdk/servicebus/service-bus/package.json | 4 +- sdk/servicebus/service-bus/tsconfig.json | 1 + sdk/storage/storage-blob/LICENSE | 34 ++--- sdk/storage/storage-blob/package.json | 5 +- sdk/storage/storage-blob/tsconfig.json | 1 + sdk/storage/storage-file-datalake/LICENSE | 34 ++--- .../storage-file-datalake/package.json | 5 +- .../storage-file-datalake/tsconfig.json | 1 + sdk/storage/storage-file-share/LICENSE | 34 ++--- sdk/storage/storage-file-share/package.json | 5 +- sdk/storage/storage-file-share/tsconfig.json | 1 + sdk/storage/storage-queue/LICENSE | 34 ++--- sdk/storage/storage-queue/package.json | 5 +- sdk/storage/storage-queue/tsconfig.json | 1 + sdk/template/template/LICENSE | 21 +++ sdk/template/template/package.json | 5 +- sdk/test-utils/recorder/LICENSE | 34 ++--- sdk/test-utils/recorder/package.json | 7 +- sdk/textanalytics/ai-text-analytics/LICENSE | 21 +++ .../ai-text-analytics/package.json | 4 +- tsconfig.json | 1 + 81 files changed, 546 insertions(+), 829 deletions(-) rename sdk/appconfiguration/app-configuration/{LICENSE.txt => LICENSE} (97%) rename sdk/{textanalytics/ai-text-analytics/LICENSE.txt => core/core-arm/LICENSE} (97%) rename sdk/{keyvault/keyvault-certificates/LICENSE.md => core/core-auth/LICENSE} (94%) delete mode 100644 sdk/core/core-http/ThirdPartyNotices.txt rename sdk/{keyvault/keyvault-secrets/LICENSE.TXT => core/core-lro/LICENSE} (87%) create mode 100644 sdk/core/core-tracing/LICENSE delete mode 100644 sdk/core/core-tracing/LICENSE.txt create mode 100644 sdk/eventhub/testhub/LICENSE create mode 100644 sdk/identity/identity/LICENSE create mode 100644 sdk/keyvault/keyvault-certificates/LICENSE delete mode 100644 sdk/keyvault/keyvault-certificates/ThirdPartyNotices.txt create mode 100644 sdk/keyvault/keyvault-keys/LICENSE delete mode 100644 sdk/keyvault/keyvault-keys/LICENSE.TXT delete mode 100644 sdk/keyvault/keyvault-keys/ThirdPartyNotices.txt create mode 100644 sdk/keyvault/keyvault-secrets/LICENSE create mode 100644 sdk/search/search/LICENSE create mode 100644 sdk/template/template/LICENSE create mode 100644 sdk/textanalytics/ai-text-analytics/LICENSE diff --git a/common/tools/eslint-plugin-azure-sdk/src/rules/ts-package-json-files-required.ts b/common/tools/eslint-plugin-azure-sdk/src/rules/ts-package-json-files-required.ts index 3d97f3c0e10f..f6e92ed265d4 100644 --- a/common/tools/eslint-plugin-azure-sdk/src/rules/ts-package-json-files-required.ts +++ b/common/tools/eslint-plugin-azure-sdk/src/rules/ts-package-json-files-required.ts @@ -81,23 +81,6 @@ export = { } }); } - - // looks for 'src' with optional leading './' and optional trailing '/ ' - if ( - elements.every( - (element: Literal): boolean => - !/^(.\/)?((src\/)|(src$))/.test(element.value as string) - ) - ) { - context.report({ - node: nodeValue, - message: "src is not included in files", - fix: (fixer: Rule.RuleFixer): Rule.Fix => { - elementValues.push("src"); - return fixer.replaceText(nodeValue, arrayToString(elementValues)); - } - }); - } } } as Rule.RuleListener) : {}; diff --git a/common/tools/eslint-plugin-azure-sdk/tests/rules/ts-package-json-files-required.ts b/common/tools/eslint-plugin-azure-sdk/tests/rules/ts-package-json-files-required.ts index 161044bf69aa..9d272f537b78 100644 --- a/common/tools/eslint-plugin-azure-sdk/tests/rules/ts-package-json-files-required.ts +++ b/common/tools/eslint-plugin-azure-sdk/tests/rules/ts-package-json-files-required.ts @@ -120,8 +120,7 @@ const examplePackageGood = `{ "typings/service-bus.d.ts", "tsconfig.json", "dist", - "dist-esm/src", - "src" + "dist-esm/src" ], "sideEffects": false }`; @@ -252,25 +251,25 @@ ruleTester.run("ts-package-json-files-required", rule, { valid: [ { // only the fields we care about - code: '{"files": ["src", "dist", "dist-esm/src"]}', + code: '{"files": ["dist", "dist-esm/src"]}', filename: "package.json" }, // other valid formats { - code: '{"files": ["src/", "dist/", "dist-esm/src/"]}', + code: '{"files": ["dist/", "dist-esm/src/"]}', filename: "package.json" }, { - code: '{"files": ["./src", "./dist", "./dist-esm/src"]}', + code: '{"files": ["./dist", "./dist-esm/src"]}', filename: "package.json" }, { - code: '{"files": ["./src/", "./dist/", "./dist-esm/src/"]}', + code: '{"files": ["./dist/", "./dist-esm/src/"]}', filename: "package.json" }, { // mixed - code: '{"files": ["./src", "dist/", "./dist-esm/src/"]}', + code: '{"files": ["dist/", "./dist-esm/src/"]}', filename: "package.json" }, { @@ -286,7 +285,7 @@ ruleTester.run("ts-package-json-files-required", rule, { ], invalid: [ { - code: '{"notFiles": ["src", "dist", "dist-esm/src"]}', + code: '{"notFiles": ["dist", "dist-esm/src"]}', filename: "package.json", errors: [ { @@ -296,7 +295,7 @@ ruleTester.run("ts-package-json-files-required", rule, { }, { // name is in a nested object - code: '{"outer": {"files": ["src", "dist", "dist-esm/src"]}}', + code: '{"outer": {"files": ["dist", "dist-esm/src"]}}', filename: "package.json", errors: [ { @@ -305,16 +304,6 @@ ruleTester.run("ts-package-json-files-required", rule, { ] }, // missing values - { - code: '{"files": ["dist", "dist-esm/src"]}', - filename: "package.json", - errors: [ - { - message: "src is not included in files" - } - ], - output: '{"files": ["dist", "dist-esm/src", "src"]}' - }, { code: '{"files": ["dist", "src"]}', filename: "package.json", @@ -341,9 +330,6 @@ ruleTester.run("ts-package-json-files-required", rule, { errors: [ { message: "dist-esm/src is not included in files" - }, - { - message: "src is not included in files" } ], output: '{"files": ["dist", "dist-esm/src"]}' @@ -354,26 +340,10 @@ ruleTester.run("ts-package-json-files-required", rule, { errors: [ { message: "dist is not included in files" - }, - { - message: "src is not included in files" } ], output: '{"files": ["dist-esm/src", "dist"]}' }, - { - code: '{"files": ["src"]}', - filename: "package.json", - errors: [ - { - message: "dist is not included in files" - }, - { - message: "dist-esm/src is not included in files" - } - ], - output: '{"files": ["src", "dist"]}' - }, { code: '{"files": []}', filename: "package.json", @@ -383,64 +353,10 @@ ruleTester.run("ts-package-json-files-required", rule, { }, { message: "dist-esm/src is not included in files" - }, - { - message: "src is not included in files" } ], output: '{"files": ["dist"]}' }, - // test regex - { - code: '{"files": ["src1", "dist", "dist-esm/src"]}', - filename: "package.json", - errors: [ - { - message: "src is not included in files" - } - ], - output: '{"files": ["src1", "dist", "dist-esm/src", "src"]}' - }, - { - code: '{"files": ["1src", "dist", "dist-esm/src"]}', - filename: "package.json", - errors: [ - { - message: "src is not included in files" - } - ], - output: '{"files": ["1src", "dist", "dist-esm/src", "src"]}' - }, - { - code: '{"files": ["/src", "dist", "dist-esm/src"]}', - filename: "package.json", - errors: [ - { - message: "src is not included in files" - } - ], - output: '{"files": ["/src", "dist", "dist-esm/src", "src"]}' - }, - { - code: '{"files": [".src", "dist", "dist-esm/src"]}', - filename: "package.json", - errors: [ - { - message: "src is not included in files" - } - ], - output: '{"files": [".src", "dist", "dist-esm/src", "src"]}' - }, - { - code: '{"files": ["lib/src", "dist", "dist-esm/src"]}', - filename: "package.json", - errors: [ - { - message: "src is not included in files" - } - ], - output: '{"files": ["lib/src", "dist", "dist-esm/src", "src"]}' - }, { // example file with src not in files code: examplePackageBad, @@ -451,9 +367,6 @@ ruleTester.run("ts-package-json-files-required", rule, { }, { message: "dist-esm/src is not included in files" - }, - { - message: "src is not included in files" } ] } diff --git a/sdk/appconfiguration/app-configuration/LICENSE.txt b/sdk/appconfiguration/app-configuration/LICENSE similarity index 97% rename from sdk/appconfiguration/app-configuration/LICENSE.txt rename to sdk/appconfiguration/app-configuration/LICENSE index b73b4a1293c3..ea8fb1516028 100644 --- a/sdk/appconfiguration/app-configuration/LICENSE.txt +++ b/sdk/appconfiguration/app-configuration/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2019 Microsoft +Copyright (c) 2020 Microsoft Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/sdk/appconfiguration/app-configuration/package.json b/sdk/appconfiguration/app-configuration/package.json index 17f60c4d5457..6cadbd71caa0 100644 --- a/sdk/appconfiguration/app-configuration/package.json +++ b/sdk/appconfiguration/app-configuration/package.json @@ -33,10 +33,8 @@ "dist-esm/**/*.d.ts", "dist-esm/**/*.d.ts.map", "types/app-configuration.d.ts", - "src/**/*.ts", "README.md", - "rollup.config.js", - "tsconfig.json" + "LICENSE" ], "scripts": { "audit": "node ../../../common/scripts/rush-audit.js && rimraf node_modules package-lock.json && npm i --package-lock-only 2>&1 && npm audit", diff --git a/sdk/appconfiguration/app-configuration/tsconfig.json b/sdk/appconfiguration/app-configuration/tsconfig.json index d13e7b6390a3..1c1e32a4803d 100644 --- a/sdk/appconfiguration/app-configuration/tsconfig.json +++ b/sdk/appconfiguration/app-configuration/tsconfig.json @@ -5,6 +5,7 @@ "moduleResolution": "node", "strict": true, "sourceMap": true, + "inlineSources": true, "declarationMap": true, "declaration": true, "declarationDir": "./types", diff --git a/sdk/core/abort-controller/LICENSE b/sdk/core/abort-controller/LICENSE index 21071075c245..ea8fb1516028 100644 --- a/sdk/core/abort-controller/LICENSE +++ b/sdk/core/abort-controller/LICENSE @@ -1,21 +1,21 @@ - MIT License +The MIT License (MIT) - Copyright (c) Microsoft Corporation. All rights reserved. +Copyright (c) 2020 Microsoft - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/sdk/core/abort-controller/package.json b/sdk/core/abort-controller/package.json index 40471dd369d9..90de485c32c8 100644 --- a/sdk/core/abort-controller/package.json +++ b/sdk/core/abort-controller/package.json @@ -38,9 +38,9 @@ "files": [ "dist/", "dist-esm/src/", - "src/", "types/src", - "tsconfig.json" + "README.md", + "LICENSE" ], "repository": { "type": "git", diff --git a/sdk/core/core-amqp/LICENSE b/sdk/core/core-amqp/LICENSE index 21071075c245..ea8fb1516028 100644 --- a/sdk/core/core-amqp/LICENSE +++ b/sdk/core/core-amqp/LICENSE @@ -1,21 +1,21 @@ - MIT License +The MIT License (MIT) - Copyright (c) Microsoft Corporation. All rights reserved. +Copyright (c) 2020 Microsoft - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/sdk/core/core-amqp/package.json b/sdk/core/core-amqp/package.json index 3978bc23823b..c7ccd560ccdd 100644 --- a/sdk/core/core-amqp/package.json +++ b/sdk/core/core-amqp/package.json @@ -17,10 +17,10 @@ "files": [ "dist/", "dist-esm/src/", - "src/", "typings/src/", - "tsconfig.json", - "ThirdPartyNotices.txt" + "ThirdPartyNotices.txt", + "README.md", + "LICENSE" ], "scripts": { "audit": "node ../../../common/scripts/rush-audit.js && rimraf node_modules package-lock.json && npm i --package-lock-only 2>&1 && npm audit", diff --git a/sdk/textanalytics/ai-text-analytics/LICENSE.txt b/sdk/core/core-arm/LICENSE similarity index 97% rename from sdk/textanalytics/ai-text-analytics/LICENSE.txt rename to sdk/core/core-arm/LICENSE index b73b4a1293c3..ea8fb1516028 100644 --- a/sdk/textanalytics/ai-text-analytics/LICENSE.txt +++ b/sdk/core/core-arm/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2019 Microsoft +Copyright (c) 2020 Microsoft Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/sdk/core/core-arm/package.json b/sdk/core/core-arm/package.json index c184cfe7cb2c..4561c30bc512 100644 --- a/sdk/core/core-arm/package.json +++ b/sdk/core/core-arm/package.json @@ -36,7 +36,6 @@ "es/src/**/*.js.map", "es/src/**/*.d.ts", "es/src/**/*.d.ts.map", - "src/**/*.ts", "LICENSE", "README.md" ], diff --git a/sdk/core/core-asynciterator-polyfill/LICENSE b/sdk/core/core-asynciterator-polyfill/LICENSE index 21071075c245..ea8fb1516028 100644 --- a/sdk/core/core-asynciterator-polyfill/LICENSE +++ b/sdk/core/core-asynciterator-polyfill/LICENSE @@ -1,21 +1,21 @@ - MIT License +The MIT License (MIT) - Copyright (c) Microsoft Corporation. All rights reserved. +Copyright (c) 2020 Microsoft - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/sdk/core/core-asynciterator-polyfill/package.json b/sdk/core/core-asynciterator-polyfill/package.json index baa12403f0ed..57c73e2d3014 100644 --- a/sdk/core/core-asynciterator-polyfill/package.json +++ b/sdk/core/core-asynciterator-polyfill/package.json @@ -19,9 +19,8 @@ "main": "./dist-esm/index.js", "files": [ "dist-esm/**/*.js", - "LICENSE", "README.md", - "ThirdPartyNotices.txt" + "LICENSE" ], "license": "MIT", "homepage": "https://github.com/Azure/azure-sdk-for-js/tree/master/sdk/core/core-asynciterator-polyfill", diff --git a/sdk/keyvault/keyvault-certificates/LICENSE.md b/sdk/core/core-auth/LICENSE similarity index 94% rename from sdk/keyvault/keyvault-certificates/LICENSE.md rename to sdk/core/core-auth/LICENSE index 769167ada543..ea8fb1516028 100644 --- a/sdk/keyvault/keyvault-certificates/LICENSE.md +++ b/sdk/core/core-auth/LICENSE @@ -1,6 +1,6 @@ -The MIT License (MIT) +The MIT License (MIT) -Copyright (c) 2018 Microsoft +Copyright (c) 2020 Microsoft Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/sdk/core/core-auth/package.json b/sdk/core/core-auth/package.json index e8feca7f773f..90425ae03fc6 100644 --- a/sdk/core/core-auth/package.json +++ b/sdk/core/core-auth/package.json @@ -35,8 +35,9 @@ "files": [ "dist/", "dist-esm/src/", - "src/", - "types/core-auth.d.ts" + "types/core-auth.d.ts", + "README.md", + "LICENSE" ], "repository": "github:Azure/azure-sdk-for-js", "keywords": [ diff --git a/sdk/core/core-http/LICENSE b/sdk/core/core-http/LICENSE index 21071075c245..ea8fb1516028 100644 --- a/sdk/core/core-http/LICENSE +++ b/sdk/core/core-http/LICENSE @@ -1,21 +1,21 @@ - MIT License +The MIT License (MIT) - Copyright (c) Microsoft Corporation. All rights reserved. +Copyright (c) 2020 Microsoft - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/sdk/core/core-http/ThirdPartyNotices.txt b/sdk/core/core-http/ThirdPartyNotices.txt deleted file mode 100644 index b3ed28d9fc5f..000000000000 --- a/sdk/core/core-http/ThirdPartyNotices.txt +++ /dev/null @@ -1,55 +0,0 @@ -Third Party Notices for core-http - -This project incorporates material from the project(s) listed below (collectively, Third Party Code). -Microsoft, Inc. Microsoft is not the original author of the Third Party Code. -The original copyright notice and license, under which Microsoft received such Third Party Code, -are set out below. This Third Party Code is licensed to you under their original license terms set forth below. -Microsoft reserves all other rights not expressly granted, whether by implication, estoppel or otherwise. - -1. opentelemetry-js - -%% opentelemetry-js NOTICES AND INFORMATION BEGIN HERE -========================================= -Copyright 2019, OpenTelemetry Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - https://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -========================================= -END OF opentelemetry-js NOTICES AND INFORMATION - -2. uuid (https://github.com/kelektiv/node-uuid) - -%% uuid NOTICES AND INFORMATION BEGIN HERE -========================================= -The MIT License (MIT) - -Copyright (c) 2010-2016 Robert Kieffer and other contributors - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -========================================= -END OF uuid NOTICES AND INFORMATION diff --git a/sdk/core/core-http/package.json b/sdk/core/core-http/package.json index 4be22bba606c..f522ff7d2603 100644 --- a/sdk/core/core-http/package.json +++ b/sdk/core/core-http/package.json @@ -44,9 +44,8 @@ "es/src/**/*.js.map", "types/*/src/**/*.d.ts", "types/*/src/**/*.d.ts.map", - "src/**/*.ts", - "LICENSE", - "README.md" + "README.md", + "LICENSE" ], "browser": { "./es/src/policies/msRestUserAgentPolicy.js": "./es/src/policies/msRestUserAgentPolicy.browser.js", diff --git a/sdk/keyvault/keyvault-secrets/LICENSE.TXT b/sdk/core/core-lro/LICENSE similarity index 87% rename from sdk/keyvault/keyvault-secrets/LICENSE.TXT rename to sdk/core/core-lro/LICENSE index 75f196e24648..ea8fb1516028 100644 --- a/sdk/keyvault/keyvault-secrets/LICENSE.TXT +++ b/sdk/core/core-lro/LICENSE @@ -1,6 +1,6 @@ -Copyright (c) Microsoft Corporation. All rights reserved. +The MIT License (MIT) -MIT License +Copyright (c) 2020 Microsoft Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -12,7 +12,7 @@ furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER diff --git a/sdk/core/core-lro/package.json b/sdk/core/core-lro/package.json index 09d5e3cbbc6f..0a30fe63ef10 100644 --- a/sdk/core/core-lro/package.json +++ b/sdk/core/core-lro/package.json @@ -33,9 +33,8 @@ "types/core-lro.d.ts", "dist/", "dist-esm/", - "src/", - "LICENSE", - "README.md" + "README.md", + "LICENSE" ], "browser": { "os": false, diff --git a/sdk/core/core-paging/LICENSE b/sdk/core/core-paging/LICENSE index 21071075c245..ea8fb1516028 100644 --- a/sdk/core/core-paging/LICENSE +++ b/sdk/core/core-paging/LICENSE @@ -1,21 +1,21 @@ - MIT License +The MIT License (MIT) - Copyright (c) Microsoft Corporation. All rights reserved. +Copyright (c) 2020 Microsoft - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/sdk/core/core-paging/tsconfig.json b/sdk/core/core-paging/tsconfig.json index 4cf2eedb4beb..82013fe4dbfb 100644 --- a/sdk/core/core-paging/tsconfig.json +++ b/sdk/core/core-paging/tsconfig.json @@ -6,6 +6,7 @@ "declaration": false, "declarationMap": false, "sourceMap": false, + "inlineSources": false, "outDir": "./dist-esm", "resolveJsonModule": true }, diff --git a/sdk/core/core-tracing/LICENSE b/sdk/core/core-tracing/LICENSE new file mode 100644 index 000000000000..ea8fb1516028 --- /dev/null +++ b/sdk/core/core-tracing/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2020 Microsoft + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/sdk/core/core-tracing/LICENSE.txt b/sdk/core/core-tracing/LICENSE.txt deleted file mode 100644 index 21071075c245..000000000000 --- a/sdk/core/core-tracing/LICENSE.txt +++ /dev/null @@ -1,21 +0,0 @@ - MIT License - - Copyright (c) Microsoft Corporation. All rights reserved. - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE diff --git a/sdk/core/core-tracing/package.json b/sdk/core/core-tracing/package.json index 87857f23e12c..5d3db7454f33 100644 --- a/sdk/core/core-tracing/package.json +++ b/sdk/core/core-tracing/package.json @@ -38,7 +38,9 @@ "files": [ "dist/", "dist-esm/src/", - "types/core-tracing.d.ts" + "types/core-tracing.d.ts", + "README.md", + "LICENSE" ], "repository": "github:Azure/azure-sdk-for-js", "keywords": [ diff --git a/sdk/core/logger/LICENSE b/sdk/core/logger/LICENSE index 21071075c245..ea8fb1516028 100644 --- a/sdk/core/logger/LICENSE +++ b/sdk/core/logger/LICENSE @@ -1,21 +1,21 @@ - MIT License +The MIT License (MIT) - Copyright (c) Microsoft Corporation. All rights reserved. +Copyright (c) 2020 Microsoft - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/sdk/core/logger/package.json b/sdk/core/logger/package.json index 8ddf9eaa3cd5..15b9360eaff8 100644 --- a/sdk/core/logger/package.json +++ b/sdk/core/logger/package.json @@ -42,9 +42,9 @@ "files": [ "dist/", "dist-esm/src/", - "src/", "types/logger.d.ts", - "tsconfig.json" + "README.md", + "LICENSE" ], "repository": { "type": "git", diff --git a/sdk/cosmosdb/cosmos/LICENSE b/sdk/cosmosdb/cosmos/LICENSE index 21071075c245..ea8fb1516028 100644 --- a/sdk/cosmosdb/cosmos/LICENSE +++ b/sdk/cosmosdb/cosmos/LICENSE @@ -1,21 +1,21 @@ - MIT License +The MIT License (MIT) - Copyright (c) Microsoft Corporation. All rights reserved. +Copyright (c) 2020 Microsoft - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/sdk/cosmosdb/cosmos/package.json b/sdk/cosmosdb/cosmos/package.json index bf8b9b12d982..1c3424363366 100644 --- a/sdk/cosmosdb/cosmos/package.json +++ b/sdk/cosmosdb/cosmos/package.json @@ -28,9 +28,8 @@ "changelog.md", "dist/", "dist-esm/", - "LICENSE", - "src/", - "tsconfig.json" + "README.md", + "LICENSE" ], "homepage": "https://github.com/Azure/azure-sdk-for-js/tree/master/sdk/cosmosdb/cosmos#readme", "sideEffects": false, diff --git a/sdk/cosmosdb/cosmos/src/tsconfig.json b/sdk/cosmosdb/cosmos/src/tsconfig.json index 2de06e0c6bd4..9c53832ba041 100644 --- a/sdk/cosmosdb/cosmos/src/tsconfig.json +++ b/sdk/cosmosdb/cosmos/src/tsconfig.json @@ -13,6 +13,7 @@ "removeComments": false, "target": "es6", "sourceMap": true, + "inlineSources": true, "newLine": "LF", "resolveJsonModule": true, "composite": true, diff --git a/sdk/eventhub/event-hubs/License b/sdk/eventhub/event-hubs/License index cbf2717d8cd3..ea8fb1516028 100644 --- a/sdk/eventhub/event-hubs/License +++ b/sdk/eventhub/event-hubs/License @@ -1,22 +1,21 @@ - MIT License +The MIT License (MIT) - Copyright (c) Microsoft Corporation. All rights reserved. +Copyright (c) 2020 Microsoft - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/sdk/eventhub/event-hubs/package.json b/sdk/eventhub/event-hubs/package.json index b74c62913324..838114464c17 100644 --- a/sdk/eventhub/event-hubs/package.json +++ b/sdk/eventhub/event-hubs/package.json @@ -29,9 +29,9 @@ "files": [ "dist/", "dist-esm/src/", - "src/", "typings/event-hubs.d.ts", - "tsconfig.json" + "README.md", + "LICENSE" ], "scripts": { "audit": "node ../../../common/scripts/rush-audit.js && rimraf node_modules package-lock.json && npm i --package-lock-only 2>&1 && npm audit", diff --git a/sdk/eventhub/event-hubs/tsconfig.json b/sdk/eventhub/event-hubs/tsconfig.json index 008f6441a69d..bda27729f70b 100644 --- a/sdk/eventhub/event-hubs/tsconfig.json +++ b/sdk/eventhub/event-hubs/tsconfig.json @@ -7,6 +7,7 @@ "declaration": true /* Generates corresponding '.d.ts' file. */, "declarationMap": true /* Generates a sourcemap for each corresponding '.d.ts' file. */, "sourceMap": true /* Generates corresponding '.map' file. */, + "inlineSources": true, "outDir": "./dist-esm" /* Redirect output structure to the directory. */, "declarationDir": "./typings" /* Output directory for generated declaration files.*/, diff --git a/sdk/eventhub/event-processor-host/License b/sdk/eventhub/event-processor-host/License index 21071075c245..ea8fb1516028 100644 --- a/sdk/eventhub/event-processor-host/License +++ b/sdk/eventhub/event-processor-host/License @@ -1,21 +1,21 @@ - MIT License +The MIT License (MIT) - Copyright (c) Microsoft Corporation. All rights reserved. +Copyright (c) 2020 Microsoft - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/sdk/eventhub/event-processor-host/package.json b/sdk/eventhub/event-processor-host/package.json index 21f5699d4390..4002c38ac31e 100644 --- a/sdk/eventhub/event-processor-host/package.json +++ b/sdk/eventhub/event-processor-host/package.json @@ -27,9 +27,9 @@ "files": [ "dist/", "dist-esm/src/", - "src/", "typings/event-processor-host.d.ts", - "tsconfig.json" + "README.md", + "LICENSE" ], "scripts": { "audit": "node ../../../common/scripts/rush-audit.js && rimraf node_modules package-lock.json && npm i --package-lock-only 2>&1 && npm audit", diff --git a/sdk/eventhub/event-processor-host/tsconfig.json b/sdk/eventhub/event-processor-host/tsconfig.json index ee7d369b2d86..d0215dcc7094 100644 --- a/sdk/eventhub/event-processor-host/tsconfig.json +++ b/sdk/eventhub/event-processor-host/tsconfig.json @@ -7,6 +7,7 @@ "declaration": true /* Generates corresponding '.d.ts' file. */, "declarationMap": true /* Generates a sourcemap for each corresponding '.d.ts' file. */, "sourceMap": true /* Generates corresponding '.map' file. */, + "inlineSources": true, "outDir": "./dist-esm" /* Redirect output structure to the directory. */, "declarationDir": "./typings" /* Output directory for generated declaration files.*/, diff --git a/sdk/eventhub/eventhubs-checkpointstore-blob/License b/sdk/eventhub/eventhubs-checkpointstore-blob/License index cbf2717d8cd3..ea8fb1516028 100644 --- a/sdk/eventhub/eventhubs-checkpointstore-blob/License +++ b/sdk/eventhub/eventhubs-checkpointstore-blob/License @@ -1,22 +1,21 @@ - MIT License +The MIT License (MIT) - Copyright (c) Microsoft Corporation. All rights reserved. +Copyright (c) 2020 Microsoft - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/sdk/eventhub/eventhubs-checkpointstore-blob/package.json b/sdk/eventhub/eventhubs-checkpointstore-blob/package.json index 1d477428c5c4..361b59c8d7e2 100644 --- a/sdk/eventhub/eventhubs-checkpointstore-blob/package.json +++ b/sdk/eventhub/eventhubs-checkpointstore-blob/package.json @@ -26,9 +26,9 @@ "files": [ "dist/", "dist-esm/src/", - "src/", "typings/eventhubs-checkpointstore-blob.d.ts", - "tsconfig.json" + "README.md", + "LICENSE" ], "scripts": { "audit": "node ../../../common/scripts/rush-audit.js && rimraf node_modules package-lock.json && npm i --package-lock-only 2>&1 && npm audit", diff --git a/sdk/eventhub/eventhubs-checkpointstore-blob/tsconfig.json b/sdk/eventhub/eventhubs-checkpointstore-blob/tsconfig.json index 008f6441a69d..bda27729f70b 100644 --- a/sdk/eventhub/eventhubs-checkpointstore-blob/tsconfig.json +++ b/sdk/eventhub/eventhubs-checkpointstore-blob/tsconfig.json @@ -7,6 +7,7 @@ "declaration": true /* Generates corresponding '.d.ts' file. */, "declarationMap": true /* Generates a sourcemap for each corresponding '.d.ts' file. */, "sourceMap": true /* Generates corresponding '.map' file. */, + "inlineSources": true, "outDir": "./dist-esm" /* Redirect output structure to the directory. */, "declarationDir": "./typings" /* Output directory for generated declaration files.*/, diff --git a/sdk/eventhub/testhub/LICENSE b/sdk/eventhub/testhub/LICENSE new file mode 100644 index 000000000000..ea8fb1516028 --- /dev/null +++ b/sdk/eventhub/testhub/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2020 Microsoft + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/sdk/eventhub/testhub/tsconfig.json b/sdk/eventhub/testhub/tsconfig.json index 03da73c1abb6..2c1eeea2c079 100644 --- a/sdk/eventhub/testhub/tsconfig.json +++ b/sdk/eventhub/testhub/tsconfig.json @@ -6,18 +6,19 @@ "declaration": true /* Generates corresponding '.d.ts' file. */, "declarationMap": true /* Generates a sourcemap for each corresponding '.d.ts' file. */, "sourceMap": true /* Generates corresponding '.map' file. */, + "inlineSources": true, "outDir": "./dist-esm" /* Redirect output structure to the directory. */, - "declarationDir": "./typings", /* Output directory for generated declaration files.*/ + "declarationDir": "./typings" /* Output directory for generated declaration files.*/, "importHelpers": true /* Import emit helpers from 'tslib'. */, /* Strict Type-Checking Options */ "alwaysStrict": true, "strictNullChecks": true, "strictPropertyInitialization": true, - "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + "noImplicitReturns": true /* Report error when not all code paths in function return a value. */, /* Additional Checks */ - "noUnusedLocals": true, /* Report errors on unused locals. */ + "noUnusedLocals": true /* Report errors on unused locals. */, /* Module Resolution Options */ "moduleResolution": "node" /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */, @@ -28,8 +29,8 @@ "forceConsistentCasingInFileNames": true, /* Other options */ - "newLine": "LF", /* Use the specified end of line sequence to be used when emitting files: "crlf" (windows) or "lf" (unix).”*/ - "allowJs": false, /* Don't allow JavaScript files to be compiled.*/ + "newLine": "LF" /* Use the specified end of line sequence to be used when emitting files: "crlf" (windows) or "lf" (unix).”*/, + "allowJs": false /* Don't allow JavaScript files to be compiled.*/, "lib": [ "es2015", "dom", @@ -43,11 +44,6 @@ ] }, "compileOnSave": true, - "exclude": [ - "node_modules" - ], - "include": [ - "cli.ts", - "./commands" - ] + "exclude": ["node_modules"], + "include": ["cli.ts", "./commands"] } diff --git a/sdk/identity/identity/LICENSE b/sdk/identity/identity/LICENSE new file mode 100644 index 000000000000..ea8fb1516028 --- /dev/null +++ b/sdk/identity/identity/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2020 Microsoft + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/sdk/identity/identity/package.json b/sdk/identity/identity/package.json index e836c8f34305..c5a3d0642e8c 100644 --- a/sdk/identity/identity/package.json +++ b/sdk/identity/identity/package.json @@ -47,8 +47,9 @@ "files": [ "dist/", "dist-esm/src/", - "src/", - "types/identity.d.ts" + "types/identity.d.ts", + "README.md", + "LICENSE" ], "repository": "github:Azure/azure-sdk-for-js", "keywords": [ diff --git a/sdk/identity/identity/tsconfig.json b/sdk/identity/identity/tsconfig.json index 5bc901cfc260..281793c5edd8 100644 --- a/sdk/identity/identity/tsconfig.json +++ b/sdk/identity/identity/tsconfig.json @@ -11,6 +11,7 @@ "declarationMap": true /* Generates a sourcemap for each corresponding '.d.ts' file. */, "declarationDir": "./types", "sourceMap": true /* Generates corresponding '.map' file. */, + "inlineSources": true, // "outFile": "./", /* Concatenate and emit output to single file. */ "outDir": "./dist-esm" /* Redirect output structure to the directory. */, // "rootDir": "." /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */, @@ -47,7 +48,6 @@ // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ - // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ /* Experimental Options */ // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ diff --git a/sdk/keyvault/keyvault-certificates/.eslintrc.json b/sdk/keyvault/keyvault-certificates/.eslintrc.json index 39286a4c4724..d2b796507a59 100644 --- a/sdk/keyvault/keyvault-certificates/.eslintrc.json +++ b/sdk/keyvault/keyvault-certificates/.eslintrc.json @@ -7,6 +7,7 @@ "ignorePatterns": ["src/core"], "rules": { "@typescript-eslint/no-this-alias": "off", - "no-invalid-this": "off" + "no-invalid-this": "off", + "@azure/azure-sdk/ts-package-json-files-required": "off" } } diff --git a/sdk/keyvault/keyvault-certificates/LICENSE b/sdk/keyvault/keyvault-certificates/LICENSE new file mode 100644 index 000000000000..ea8fb1516028 --- /dev/null +++ b/sdk/keyvault/keyvault-certificates/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2020 Microsoft + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/sdk/keyvault/keyvault-certificates/ThirdPartyNotices.txt b/sdk/keyvault/keyvault-certificates/ThirdPartyNotices.txt deleted file mode 100644 index f9f562833719..000000000000 --- a/sdk/keyvault/keyvault-certificates/ThirdPartyNotices.txt +++ /dev/null @@ -1,135 +0,0 @@ -Third Party Notices for keyvault-certificates - -This project incorporates material from the project(s) listed below -(collectively, Third Party Code). -Microsoft, Inc. Microsoft is not the original author of the Third Party Code. -The original copyright notice and license, under which Microsoft received such -Third Party Code, are set out below. This Third Party Code is licensed to you -under their original license terms set forth below. -Microsoft reserves all other rights not expressly granted, whether by -implication, estoppel or otherwise. - -1. uuid (https://github.com/kelektiv/node-uuid) - -%% uuid NOTICES AND INFORMATION BEGIN HERE -========================================= -The MIT License (MIT) - -Copyright (c) 2010-2016 Robert Kieffer and other contributors - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -========================================= -END OF uuid NOTICES AND INFORMATION - -2. opentelemetry-js (https://github.com/open-telemetry/opentelemetry-js) - -%% opentelemetry-js NOTICES AND INFORMATION BEGIN HERE -========================================= -Copyright 2019, OpenTelemetry Authors -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - https://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -========================================= -END OF opentelemetry-js NOTICES AND INFORMATION - -3. punycode (https://github.com/bestiejs/punycode.js) - -%% punycode NOTICES AND INFORMATION BEGIN HERE -========================================= -Copyright Mathias Bynens - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -========================================= -END OF punycode NOTICES AND INFORMATION - -4. url (https://github.com/defunctzombie/node-url) - -%% url NOTICES AND INFORMATION BEGIN HERE -========================================= -The MIT License (MIT) - -Copyright Joyent, Inc. and other Node contributors. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -========================================= -END OF url NOTICES AND INFORMATION - -5. querystring (https://github.com/Gozala/querystring) - -%% querystring NOTICES AND INFORMATION BEGIN HERE -========================================= -Copyright 2012 Irakli Gozalishvili. All rights reserved. -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to -deal in the Software without restriction, including without limitation the -rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -sell copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -IN THE SOFTWARE. -========================================= -END OF querystring NOTICES AND INFORMATION diff --git a/sdk/keyvault/keyvault-certificates/package.json b/sdk/keyvault/keyvault-certificates/package.json index 98fe169fe9a6..81e966ea8755 100644 --- a/sdk/keyvault/keyvault-certificates/package.json +++ b/sdk/keyvault/keyvault-certificates/package.json @@ -26,14 +26,11 @@ "node": ">=8.0.0" }, "files": [ - "LICENSE.txt", - "README.md", "types/keyvault-certificates.d.ts", - "lib/", "dist/", "dist-esm/src", - "src/", - "tsconfig.json" + "README.md", + "LICENSE" ], "browser": { "os": false, diff --git a/sdk/keyvault/keyvault-certificates/tsconfig.json b/sdk/keyvault/keyvault-certificates/tsconfig.json index 8a51525faa94..9a0426a5d935 100644 --- a/sdk/keyvault/keyvault-certificates/tsconfig.json +++ b/sdk/keyvault/keyvault-certificates/tsconfig.json @@ -4,6 +4,7 @@ "noImplicitAny": true, "preserveConstEnums": true, "sourceMap": true, + "inlineSources": true, "newLine": "LF", "target": "es5", "moduleResolution": "node", diff --git a/sdk/keyvault/keyvault-keys/LICENSE b/sdk/keyvault/keyvault-keys/LICENSE new file mode 100644 index 000000000000..ea8fb1516028 --- /dev/null +++ b/sdk/keyvault/keyvault-keys/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2020 Microsoft + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/sdk/keyvault/keyvault-keys/LICENSE.TXT b/sdk/keyvault/keyvault-keys/LICENSE.TXT deleted file mode 100644 index 75f196e24648..000000000000 --- a/sdk/keyvault/keyvault-keys/LICENSE.TXT +++ /dev/null @@ -1,21 +0,0 @@ -Copyright (c) Microsoft Corporation. All rights reserved. - -MIT License - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/sdk/keyvault/keyvault-keys/ThirdPartyNotices.txt b/sdk/keyvault/keyvault-keys/ThirdPartyNotices.txt deleted file mode 100644 index 6af21e1efc28..000000000000 --- a/sdk/keyvault/keyvault-keys/ThirdPartyNotices.txt +++ /dev/null @@ -1,135 +0,0 @@ -Third Party Notices for keyvault-keys - -This project incorporates material from the project(s) listed below -(collectively, Third Party Code). -Microsoft, Inc. Microsoft is not the original author of the Third Party Code. -The original copyright notice and license, under which Microsoft received such -Third Party Code, are set out below. This Third Party Code is licensed to you -under their original license terms set forth below. -Microsoft reserves all other rights not expressly granted, whether by -implication, estoppel or otherwise. - -1. uuid (https://github.com/kelektiv/node-uuid) - -%% uuid NOTICES AND INFORMATION BEGIN HERE -========================================= -The MIT License (MIT) - -Copyright (c) 2010-2016 Robert Kieffer and other contributors - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -========================================= -END OF uuid NOTICES AND INFORMATION - -2. opentelemetry-js (https://github.com/open-telemetry/opentelemetry-js) - -%% opentelemetry-js NOTICES AND INFORMATION BEGIN HERE -========================================= -Copyright 2019, OpenTelemetry Authors -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - https://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -========================================= -END OF opentelemetry-js NOTICES AND INFORMATION - -3. punycode (https://github.com/bestiejs/punycode.js) - -%% punycode NOTICES AND INFORMATION BEGIN HERE -========================================= -Copyright Mathias Bynens - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -========================================= -END OF punycode NOTICES AND INFORMATION - -4. url (https://github.com/defunctzombie/node-url) - -%% url NOTICES AND INFORMATION BEGIN HERE -========================================= -The MIT License (MIT) - -Copyright Joyent, Inc. and other Node contributors. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -========================================= -END OF url NOTICES AND INFORMATION - -5. querystring (https://github.com/Gozala/querystring) - -%% querystring NOTICES AND INFORMATION BEGIN HERE -========================================= -Copyright 2012 Irakli Gozalishvili. All rights reserved. -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to -deal in the Software without restriction, including without limitation the -rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -sell copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -IN THE SOFTWARE. -========================================= -END OF querystring NOTICES AND INFORMATION diff --git a/sdk/keyvault/keyvault-keys/package.json b/sdk/keyvault/keyvault-keys/package.json index 3d91c05f61a2..53aee4276b00 100644 --- a/sdk/keyvault/keyvault-keys/package.json +++ b/sdk/keyvault/keyvault-keys/package.json @@ -25,13 +25,11 @@ "node": ">=8.0.0" }, "files": [ - "LICENSE.txt", - "README.md", "types/keyvault-keys.d.ts", "dist/", "dist-esm/", - "src/", - "tsconfig.json" + "README.md", + "LICENSE" ], "browser": { "os": false, diff --git a/sdk/keyvault/keyvault-keys/tsconfig.json b/sdk/keyvault/keyvault-keys/tsconfig.json index 8a51525faa94..9a0426a5d935 100644 --- a/sdk/keyvault/keyvault-keys/tsconfig.json +++ b/sdk/keyvault/keyvault-keys/tsconfig.json @@ -4,6 +4,7 @@ "noImplicitAny": true, "preserveConstEnums": true, "sourceMap": true, + "inlineSources": true, "newLine": "LF", "target": "es5", "moduleResolution": "node", diff --git a/sdk/keyvault/keyvault-secrets/LICENSE b/sdk/keyvault/keyvault-secrets/LICENSE new file mode 100644 index 000000000000..ea8fb1516028 --- /dev/null +++ b/sdk/keyvault/keyvault-secrets/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2020 Microsoft + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/sdk/keyvault/keyvault-secrets/package.json b/sdk/keyvault/keyvault-secrets/package.json index 7e65574b98f9..a596f5688606 100644 --- a/sdk/keyvault/keyvault-secrets/package.json +++ b/sdk/keyvault/keyvault-secrets/package.json @@ -25,13 +25,11 @@ "node": ">=8.0.0" }, "files": [ - "LICENSE.txt", - "README.md", "types/keyvault-secrets.d.ts", "dist/", "dist-esm/", - "src/", - "tsconfig.json" + "README.md", + "LICENSE" ], "browser": { "os": false, diff --git a/sdk/keyvault/keyvault-secrets/tsconfig.json b/sdk/keyvault/keyvault-secrets/tsconfig.json index 8a51525faa94..9a0426a5d935 100644 --- a/sdk/keyvault/keyvault-secrets/tsconfig.json +++ b/sdk/keyvault/keyvault-secrets/tsconfig.json @@ -4,6 +4,7 @@ "noImplicitAny": true, "preserveConstEnums": true, "sourceMap": true, + "inlineSources": true, "newLine": "LF", "target": "es5", "moduleResolution": "node", diff --git a/sdk/search/search/LICENSE b/sdk/search/search/LICENSE new file mode 100644 index 000000000000..ea8fb1516028 --- /dev/null +++ b/sdk/search/search/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2020 Microsoft + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/sdk/search/search/package.json b/sdk/search/search/package.json index d801848ec50f..0128c3d60808 100644 --- a/sdk/search/search/package.json +++ b/sdk/search/search/package.json @@ -37,8 +37,9 @@ "files": [ "dist/", "dist-esm/src/", - "src/", - "types/search.d.ts" + "types/search.d.ts", + "README.md", + "LICENSE" ], "//metadata": { "constantPaths": [ diff --git a/sdk/servicebus/service-bus/License b/sdk/servicebus/service-bus/License index 8cb179cdb694..ea8fb1516028 100644 --- a/sdk/servicebus/service-bus/License +++ b/sdk/servicebus/service-bus/License @@ -1,6 +1,6 @@ -Copyright (c) Microsoft Corporation. All rights reserved. +The MIT License (MIT) -MIT License +Copyright (c) 2020 Microsoft Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -12,7 +12,7 @@ furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER diff --git a/sdk/servicebus/service-bus/package.json b/sdk/servicebus/service-bus/package.json index ec6474d43c6a..3614f298e2b3 100644 --- a/sdk/servicebus/service-bus/package.json +++ b/sdk/servicebus/service-bus/package.json @@ -31,9 +31,9 @@ "files": [ "dist/", "dist-esm/src/", - "src/", "typings/service-bus.d.ts", - "tsconfig.json" + "README.md", + "LICENSE" ], "scripts": { "audit": "node ../../../common/scripts/rush-audit.js && rimraf node_modules package-lock.json && npm i --package-lock-only 2>&1 && npm audit", diff --git a/sdk/servicebus/service-bus/tsconfig.json b/sdk/servicebus/service-bus/tsconfig.json index 7cc660575697..0bc52cd946d1 100644 --- a/sdk/servicebus/service-bus/tsconfig.json +++ b/sdk/servicebus/service-bus/tsconfig.json @@ -6,6 +6,7 @@ "declaration": true /* Generates corresponding '.d.ts' file. */, "declarationMap": true /* Generates a sourcemap for each corresponding '.d.ts' file. */, "sourceMap": true /* Generates corresponding '.map' file. */, + "inlineSources": true, "outDir": "./dist-esm" /* Redirect output structure to the directory. */, "stripInternal": true /* Do not emit declarations for code with @internal annotation*/, "declarationDir": "./typings" /* Output directory for generated declaration files.*/, diff --git a/sdk/storage/storage-blob/LICENSE b/sdk/storage/storage-blob/LICENSE index 21071075c245..ea8fb1516028 100644 --- a/sdk/storage/storage-blob/LICENSE +++ b/sdk/storage/storage-blob/LICENSE @@ -1,21 +1,21 @@ - MIT License +The MIT License (MIT) - Copyright (c) Microsoft Corporation. All rights reserved. +Copyright (c) 2020 Microsoft - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/sdk/storage/storage-blob/package.json b/sdk/storage/storage-blob/package.json index 9115fb7c87cc..ac2b14f22ba1 100644 --- a/sdk/storage/storage-blob/package.json +++ b/sdk/storage/storage-blob/package.json @@ -64,11 +64,10 @@ "BreakingChanges.md", "dist/", "dist-esm/src/", - "LICENSE", - "src/", "typings/latest/src", "typings/3.1/src", - "tsconfig.json" + "README.md", + "LICENSE" ], "repository": { "type": "git", diff --git a/sdk/storage/storage-blob/tsconfig.json b/sdk/storage/storage-blob/tsconfig.json index 83d57c81c3fb..7d0f438f54f6 100644 --- a/sdk/storage/storage-blob/tsconfig.json +++ b/sdk/storage/storage-blob/tsconfig.json @@ -4,6 +4,7 @@ "noImplicitAny": true, "preserveConstEnums": true, "sourceMap": true, + "inlineSources": true, "newLine": "LF", "target": "es5", "moduleResolution": "node", diff --git a/sdk/storage/storage-file-datalake/LICENSE b/sdk/storage/storage-file-datalake/LICENSE index 21071075c245..ea8fb1516028 100644 --- a/sdk/storage/storage-file-datalake/LICENSE +++ b/sdk/storage/storage-file-datalake/LICENSE @@ -1,21 +1,21 @@ - MIT License +The MIT License (MIT) - Copyright (c) Microsoft Corporation. All rights reserved. +Copyright (c) 2020 Microsoft - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/sdk/storage/storage-file-datalake/package.json b/sdk/storage/storage-file-datalake/package.json index 332c5703c364..356a891607d5 100644 --- a/sdk/storage/storage-file-datalake/package.json +++ b/sdk/storage/storage-file-datalake/package.json @@ -58,11 +58,10 @@ "BreakingChanges.md", "dist/", "dist-esm/src/", - "LICENSE", - "src/", "typings/latest/src", "typings/3.1/src", - "tsconfig.json" + "README.md", + "LICENSE" ], "sideEffects": false, "repository": { diff --git a/sdk/storage/storage-file-datalake/tsconfig.json b/sdk/storage/storage-file-datalake/tsconfig.json index 91d7c05371eb..ebafcd4607a2 100644 --- a/sdk/storage/storage-file-datalake/tsconfig.json +++ b/sdk/storage/storage-file-datalake/tsconfig.json @@ -4,6 +4,7 @@ "noImplicitAny": true, "preserveConstEnums": true, "sourceMap": true, + "inlineSources": true, "newLine": "LF", "target": "es5", "moduleResolution": "node", diff --git a/sdk/storage/storage-file-share/LICENSE b/sdk/storage/storage-file-share/LICENSE index 21071075c245..ea8fb1516028 100644 --- a/sdk/storage/storage-file-share/LICENSE +++ b/sdk/storage/storage-file-share/LICENSE @@ -1,21 +1,21 @@ - MIT License +The MIT License (MIT) - Copyright (c) Microsoft Corporation. All rights reserved. +Copyright (c) 2020 Microsoft - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/sdk/storage/storage-file-share/package.json b/sdk/storage/storage-file-share/package.json index dd1b6dbfe72f..13b097f26a60 100644 --- a/sdk/storage/storage-file-share/package.json +++ b/sdk/storage/storage-file-share/package.json @@ -63,11 +63,10 @@ "ThirdPartyNotices.txt", "dist/", "dist-esm/src/", - "LICENSE", - "src/", "typings/latest/src", "typings/3.1/src", - "tsconfig.json" + "README.md", + "LICENSE" ], "repository": { "type": "git", diff --git a/sdk/storage/storage-file-share/tsconfig.json b/sdk/storage/storage-file-share/tsconfig.json index 91d7c05371eb..ebafcd4607a2 100644 --- a/sdk/storage/storage-file-share/tsconfig.json +++ b/sdk/storage/storage-file-share/tsconfig.json @@ -4,6 +4,7 @@ "noImplicitAny": true, "preserveConstEnums": true, "sourceMap": true, + "inlineSources": true, "newLine": "LF", "target": "es5", "moduleResolution": "node", diff --git a/sdk/storage/storage-queue/LICENSE b/sdk/storage/storage-queue/LICENSE index 21071075c245..ea8fb1516028 100644 --- a/sdk/storage/storage-queue/LICENSE +++ b/sdk/storage/storage-queue/LICENSE @@ -1,21 +1,21 @@ - MIT License +The MIT License (MIT) - Copyright (c) Microsoft Corporation. All rights reserved. +Copyright (c) 2020 Microsoft - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/sdk/storage/storage-queue/package.json b/sdk/storage/storage-queue/package.json index a793c7009131..7f90aa80222f 100644 --- a/sdk/storage/storage-queue/package.json +++ b/sdk/storage/storage-queue/package.json @@ -60,11 +60,10 @@ "BreakingChanges.md", "dist/", "dist-esm/src/", - "LICENSE", - "src/", "typings/latest/src", "typings/3.1/src", - "tsconfig.json" + "README.md", + "LICENSE" ], "repository": { "type": "git", diff --git a/sdk/storage/storage-queue/tsconfig.json b/sdk/storage/storage-queue/tsconfig.json index 91d7c05371eb..ebafcd4607a2 100644 --- a/sdk/storage/storage-queue/tsconfig.json +++ b/sdk/storage/storage-queue/tsconfig.json @@ -4,6 +4,7 @@ "noImplicitAny": true, "preserveConstEnums": true, "sourceMap": true, + "inlineSources": true, "newLine": "LF", "target": "es5", "moduleResolution": "node", diff --git a/sdk/template/template/LICENSE b/sdk/template/template/LICENSE new file mode 100644 index 000000000000..ea8fb1516028 --- /dev/null +++ b/sdk/template/template/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2020 Microsoft + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/sdk/template/template/package.json b/sdk/template/template/package.json index 929b15090791..789140c1414f 100644 --- a/sdk/template/template/package.json +++ b/sdk/template/template/package.json @@ -39,8 +39,9 @@ "files": [ "dist/", "dist-esm/src/", - "src/", - "types/template.d.ts" + "types/template.d.ts", + "README.md", + "LICENSE" ], "repository": "github:Azure/azure-sdk-for-js", "keywords": [ diff --git a/sdk/test-utils/recorder/LICENSE b/sdk/test-utils/recorder/LICENSE index 21071075c245..ea8fb1516028 100644 --- a/sdk/test-utils/recorder/LICENSE +++ b/sdk/test-utils/recorder/LICENSE @@ -1,21 +1,21 @@ - MIT License +The MIT License (MIT) - Copyright (c) Microsoft Corporation. All rights reserved. +Copyright (c) 2020 Microsoft - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/sdk/test-utils/recorder/package.json b/sdk/test-utils/recorder/package.json index e4c1ada8f67a..fbe0e7f4d868 100644 --- a/sdk/test-utils/recorder/package.json +++ b/sdk/test-utils/recorder/package.json @@ -4,8 +4,7 @@ "description": "This library provides interfaces and helper methods to provide recording and playback capabilities for the tests in Azure JS/TS SDKs", "main": "dist/index.js", "module": "dist-esm/src/index.js", - "browser": { - }, + "browser": {}, "types": "./typings/src/index.d.ts", "scripts": { "audit": "node ../../../common/scripts/rush-audit.js && rimraf node_modules package-lock.json && npm i --package-lock-only 2>&1 && npm audit", @@ -36,7 +35,9 @@ "dist/", "dist-esm/src/", "src/", - "typings/src" + "typings/src", + "README.md", + "LICENSE" ], "repository": "github:Azure/azure-sdk-for-js", "keywords": [ diff --git a/sdk/textanalytics/ai-text-analytics/LICENSE b/sdk/textanalytics/ai-text-analytics/LICENSE new file mode 100644 index 000000000000..ea8fb1516028 --- /dev/null +++ b/sdk/textanalytics/ai-text-analytics/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2020 Microsoft + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/sdk/textanalytics/ai-text-analytics/package.json b/sdk/textanalytics/ai-text-analytics/package.json index c0da0e242024..674ba1bc664b 100644 --- a/sdk/textanalytics/ai-text-analytics/package.json +++ b/sdk/textanalytics/ai-text-analytics/package.json @@ -27,8 +27,8 @@ "dist/", "dist-esm/src/", "types/ai-text-analytics.d.ts", - "src/", - "README.md" + "README.md", + "LICENSE" ], "//metadata": { "constantPaths": [ diff --git a/tsconfig.json b/tsconfig.json index 506662b5a428..cefae51eb534 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,6 +5,7 @@ "lib": [], "declaration": true, "declarationMap": true, + "inlineSources": true, "sourceMap": true, "importHelpers": true, "strict": true, From 67a8f821ee9ba2ab5efecce700decd2207f5033d Mon Sep 17 00:00:00 2001 From: Scott Schaab Date: Wed, 11 Mar 2020 10:35:22 -0700 Subject: [PATCH 05/28] Adding schaabs to identity codeowners (#7756) --- .github/CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index c818a47a8c5a..a60938d7bb0b 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -27,7 +27,7 @@ /sdk/eventhub/ @ramya-rao-a @chradek @richardpark-msft /sdk/servicebus/ @ramya-rao-a @chradek @richardpark-msft -/sdk/identity/ @daviwil @jonathandturner +/sdk/identity/ @schaabs @daviwil @jonathandturner /sdk/keyvault/ @jonathandturner @sadasant /sdk/storage/ @XiaoningLiu @jeremymeng @HarshaNalluru @vinjiang @jiacfan @ljian3377 From cdb853d9bcb664abfbdabcb13b85a55cef85afc3 Mon Sep 17 00:00:00 2001 From: Richard Park <51494936+richardpark-msft@users.noreply.github.com> Date: Thu, 12 Mar 2020 12:03:12 -0700 Subject: [PATCH 06/28] [event-hubs] Link to migration guide from the readme (#7733) Add link to the migration guide to the readme and made the homepage URL for our package point to the actual code for the package and not the parent. --- sdk/eventhub/event-hubs/README.md | 5 ++++- sdk/eventhub/event-hubs/package.json | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/sdk/eventhub/event-hubs/README.md b/sdk/eventhub/event-hubs/README.md index c2bf6e626e65..1212e37d98ad 100644 --- a/sdk/eventhub/event-hubs/README.md +++ b/sdk/eventhub/event-hubs/README.md @@ -10,7 +10,10 @@ The Azure Event Hubs client library allows you to send and receive events in you [Product documentation](https://azure.microsoft.com/en-us/services/event-hubs/) | [Samples](https://github.com/Azure/azure-sdk-for-js/tree/master/sdk/eventhub/event-hubs/samples) -**NOTE**: If you are using version 2.1.0 or lower, then please use the below links instead +**NOTE**: If you are using version 2.1.0 or lower and want to migrate to the latest version +of this package please look at our [migration guide to move from EventHubs V2 to EventHubs V5](https://github.com/Azure/azure-sdk-for-js/tree/master/sdk/eventhub/event-hubs/migrationguide.md) + +Samples for v2 and documentation are still available here: [Source code for v2.1.0](https://github.com/Azure/azure-sdk-for-js/tree/%40azure/event-hubs_2.1.0/sdk/eventhub/event-hubs) | [Package for v2.1.0 (npm)](https://www.npmjs.com/package/@azure/event-hubs/v/2.1.0) | diff --git a/sdk/eventhub/event-hubs/package.json b/sdk/eventhub/event-hubs/package.json index 838114464c17..ae117731a1ff 100644 --- a/sdk/eventhub/event-hubs/package.json +++ b/sdk/eventhub/event-hubs/package.json @@ -5,7 +5,7 @@ "description": "Azure Event Hubs SDK for JS.", "author": "Microsoft Corporation", "license": "MIT", - "homepage": "https://github.com/Azure/azure-sdk-for-js/tree/master/sdk/eventhub", + "homepage": "https://github.com/Azure/azure-sdk-for-js/tree/master/sdk/eventhub/event-hubs", "repository": "github:Azure/azure-sdk-for-js", "sideEffects": false, "keywords": [ From 4ca4172a7a9f7407d700576c3ac5b0407f6b3b8f Mon Sep 17 00:00:00 2001 From: Azure SDK Bot <53356347+azure-sdk@users.noreply.github.com> Date: Thu, 12 Mar 2020 19:09:57 -0700 Subject: [PATCH 07/28] Increment version for storage releases (#7796) * Increment package version after release of azure-storage-blob * Increment package version after release of azure-storage-file-datalake * Increment package version after release of azure-storage-queue * Increment package version after release of azure-storage-file-share --- sdk/storage/storage-blob/CHANGELOG.md | 3 +++ sdk/storage/storage-blob/package.json | 2 +- sdk/storage/storage-blob/src/utils/constants.ts | 2 +- sdk/storage/storage-file-datalake/CHANGELOG.md | 3 +++ sdk/storage/storage-file-datalake/package.json | 2 +- sdk/storage/storage-file-datalake/src/utils/constants.ts | 2 +- sdk/storage/storage-file-share/CHANGELOG.md | 3 +++ sdk/storage/storage-file-share/package.json | 2 +- .../src/generated/src/storageClientContext.ts | 2 +- sdk/storage/storage-file-share/src/utils/constants.ts | 2 +- sdk/storage/storage-queue/CHANGELOG.md | 3 +++ sdk/storage/storage-queue/package.json | 2 +- .../storage-queue/src/generated/src/storageClientContext.ts | 2 +- sdk/storage/storage-queue/src/utils/constants.ts | 2 +- 14 files changed, 22 insertions(+), 10 deletions(-) diff --git a/sdk/storage/storage-blob/CHANGELOG.md b/sdk/storage/storage-blob/CHANGELOG.md index 80c5fbe63626..887a0e2ebf31 100644 --- a/sdk/storage/storage-blob/CHANGELOG.md +++ b/sdk/storage/storage-blob/CHANGELOG.md @@ -1,5 +1,8 @@ # Release History +## 12.1.2 (Unreleased) + + ## 12.1.1 (2020.03) - Bug fix - Blob SAS's `sr` field is now properly set when generating SAS for a blob using a stored policy with `signedpermissions`. For more details about Service SAS, please refer to [link](https://docs.microsoft.com/en-us/rest/api/storageservices/create-service-sas). diff --git a/sdk/storage/storage-blob/package.json b/sdk/storage/storage-blob/package.json index ac2b14f22ba1..ff5691a0ea35 100644 --- a/sdk/storage/storage-blob/package.json +++ b/sdk/storage/storage-blob/package.json @@ -1,7 +1,7 @@ { "name": "@azure/storage-blob", "sdk-type": "client", - "version": "12.1.1", + "version": "12.1.2", "description": "Microsoft Azure Storage SDK for JavaScript - Blob", "main": "./dist/index.js", "module": "./dist-esm/src/index.js", diff --git a/sdk/storage/storage-blob/src/utils/constants.ts b/sdk/storage/storage-blob/src/utils/constants.ts index 167056dd4ec8..59c91c9bbcd8 100644 --- a/sdk/storage/storage-blob/src/utils/constants.ts +++ b/sdk/storage/storage-blob/src/utils/constants.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -export const SDK_VERSION: string = "12.1.1"; +export const SDK_VERSION: string = "12.1.2"; export const SERVICE_VERSION: string = "2019-07-07"; export const BLOCK_BLOB_MAX_UPLOAD_BLOB_BYTES: number = 256 * 1024 * 1024; // 256MB diff --git a/sdk/storage/storage-file-datalake/CHANGELOG.md b/sdk/storage/storage-file-datalake/CHANGELOG.md index 8b58aeebfe58..ca3742ecba53 100644 --- a/sdk/storage/storage-file-datalake/CHANGELOG.md +++ b/sdk/storage/storage-file-datalake/CHANGELOG.md @@ -1,5 +1,8 @@ # Release History +## 12.0.1 (Unreleased) + + ## 12.0.0 (2020.03) - Added exists() on `FileSystemClient` and `PathClient`. diff --git a/sdk/storage/storage-file-datalake/package.json b/sdk/storage/storage-file-datalake/package.json index 356a891607d5..c3be4d12fec2 100644 --- a/sdk/storage/storage-file-datalake/package.json +++ b/sdk/storage/storage-file-datalake/package.json @@ -1,6 +1,6 @@ { "name": "@azure/storage-file-datalake", - "version": "12.0.0", + "version": "12.0.1", "description": "Microsoft Azure Storage SDK for JavaScript - DataLake", "sdk-type": "client", "main": "./dist/index.js", diff --git a/sdk/storage/storage-file-datalake/src/utils/constants.ts b/sdk/storage/storage-file-datalake/src/utils/constants.ts index 2ee52bab38a0..dcceca94a1b4 100644 --- a/sdk/storage/storage-file-datalake/src/utils/constants.ts +++ b/sdk/storage/storage-file-datalake/src/utils/constants.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -export const SDK_VERSION: string = "12.0.0"; +export const SDK_VERSION: string = "12.0.1"; export const SERVICE_VERSION: string = "2019-07-07"; export const KB: number = 1024; diff --git a/sdk/storage/storage-file-share/CHANGELOG.md b/sdk/storage/storage-file-share/CHANGELOG.md index 5cbc76d19e6f..89994b220a43 100644 --- a/sdk/storage/storage-file-share/CHANGELOG.md +++ b/sdk/storage/storage-file-share/CHANGELOG.md @@ -1,5 +1,8 @@ # Release History +## 12.1.2 (Unreleased) + + ## 12.1.1 (2020-03-10) - Fixed unexpected hang issue when uploading empty body. Fixed bug [6904](https://github.com/Azure/azure-sdk-for-js/issues/6904). diff --git a/sdk/storage/storage-file-share/package.json b/sdk/storage/storage-file-share/package.json index 13b097f26a60..19ff9bd4635e 100644 --- a/sdk/storage/storage-file-share/package.json +++ b/sdk/storage/storage-file-share/package.json @@ -1,7 +1,7 @@ { "name": "@azure/storage-file-share", "sdk-type": "client", - "version": "12.1.1", + "version": "12.1.2", "description": "Microsoft Azure Storage SDK for JavaScript - File", "main": "./dist/index.js", "module": "./dist-esm/src/index.js", diff --git a/sdk/storage/storage-file-share/src/generated/src/storageClientContext.ts b/sdk/storage/storage-file-share/src/generated/src/storageClientContext.ts index c01364e8dc68..8992aa042d4b 100644 --- a/sdk/storage/storage-file-share/src/generated/src/storageClientContext.ts +++ b/sdk/storage/storage-file-share/src/generated/src/storageClientContext.ts @@ -11,7 +11,7 @@ import * as coreHttp from "@azure/core-http"; const packageName = "azure-storage-file-share"; -const packageVersion = "12.1.1"; +const packageVersion = "12.1.2"; export class StorageClientContext extends coreHttp.ServiceClient { version: string; diff --git a/sdk/storage/storage-file-share/src/utils/constants.ts b/sdk/storage/storage-file-share/src/utils/constants.ts index 9aad5d4b294d..101e9b506655 100644 --- a/sdk/storage/storage-file-share/src/utils/constants.ts +++ b/sdk/storage/storage-file-share/src/utils/constants.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -export const SDK_VERSION: string = "12.1.1"; +export const SDK_VERSION: string = "12.1.2"; export const SERVICE_VERSION: string = "2019-07-07"; export const FILE_MAX_SIZE_BYTES: number = 1024 * 1024 * 1024 * 1024; // 1TB diff --git a/sdk/storage/storage-queue/CHANGELOG.md b/sdk/storage/storage-queue/CHANGELOG.md index 3494114bf863..43c10d633612 100644 --- a/sdk/storage/storage-queue/CHANGELOG.md +++ b/sdk/storage/storage-queue/CHANGELOG.md @@ -1,5 +1,8 @@ # Release History +## 12.0.5 (Unreleased) + + ## 12.0.4 (2020.03) - Buf fix - Fixed typings support for TypeScript 3.1. [PR #7350](https://github.com/Azure/azure-sdk-for-js/pull/7350) diff --git a/sdk/storage/storage-queue/package.json b/sdk/storage/storage-queue/package.json index 7f90aa80222f..2a273ba87154 100644 --- a/sdk/storage/storage-queue/package.json +++ b/sdk/storage/storage-queue/package.json @@ -1,7 +1,7 @@ { "name": "@azure/storage-queue", "sdk-type": "client", - "version": "12.0.4", + "version": "12.0.5", "description": "Microsoft Azure Storage SDK for JavaScript - Queue", "main": "./dist/index.js", "module": "./dist-esm/src/index.js", diff --git a/sdk/storage/storage-queue/src/generated/src/storageClientContext.ts b/sdk/storage/storage-queue/src/generated/src/storageClientContext.ts index 7b0b21c9c6a5..2b30834cc69d 100644 --- a/sdk/storage/storage-queue/src/generated/src/storageClientContext.ts +++ b/sdk/storage/storage-queue/src/generated/src/storageClientContext.ts @@ -11,7 +11,7 @@ import * as coreHttp from "@azure/core-http"; const packageName = "azure-storage-queue"; -const packageVersion = "12.0.4"; +const packageVersion = "12.0.5"; export class StorageClientContext extends coreHttp.ServiceClient { url: string; diff --git a/sdk/storage/storage-queue/src/utils/constants.ts b/sdk/storage/storage-queue/src/utils/constants.ts index fb6ac0978820..3d963edd9fe2 100644 --- a/sdk/storage/storage-queue/src/utils/constants.ts +++ b/sdk/storage/storage-queue/src/utils/constants.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -export const SDK_VERSION: string = "12.0.4"; +export const SDK_VERSION: string = "12.0.5"; export const SERVICE_VERSION: string = "2019-07-07"; /** From aa36a125c28c1604e45d445989195a8bddaf6166 Mon Sep 17 00:00:00 2001 From: Harsha Nalluru Date: Thu, 12 Mar 2020 20:32:26 -0700 Subject: [PATCH 08/28] [Service Bus] Settlement methods should throw error in receiveAndDelete mode (#7787) * Unsupported features in ReceiveAndDelete mode - settlement methods - uncomment tests * new build - generated api report * add toLowercase to satisfy - deadLetter vs deadletter Co-authored-by: HarshaNalluru <10452642+HarshaNalluru@users.noreply.github.com> --- .../service-bus/review/service-bus.api.md | 25 -- .../test/receiveAndDeleteMode.spec.ts | 347 +++++++++--------- 2 files changed, 174 insertions(+), 198 deletions(-) diff --git a/sdk/servicebus/service-bus/review/service-bus.api.md b/sdk/servicebus/service-bus/review/service-bus.api.md index 16c6df65e680..1ef0f6bb8bf0 100644 --- a/sdk/servicebus/service-bus/review/service-bus.api.md +++ b/sdk/servicebus/service-bus/review/service-bus.api.md @@ -122,33 +122,23 @@ export { MessagingError } // @public export interface NonSessionReceiver { - // (undocumented) - close(): Promise; close(): Promise; diagnostics: { peek(maxMessageCount?: number): Promise; peekBySequenceNumber(fromSequenceNumber: Long, maxMessageCount?: number): Promise; }; - // (undocumented) entityPath: string; - // (undocumented) entityType: "queue" | "subscription"; - // (undocumented) getDeadLetterPath(): string; - // (undocumented) isReceivingMessages(): boolean; iterateMessages(options?: IterateMessagesOptions): MessageIterator>; receiveBatch(maxMessages: number, maxWaitTimeInSeconds?: number, options?: ReceiveBatchOptions): Promise<{ messages: ReceivedMessage[]; context: ContextType; }>; - // (undocumented) receiveDeferredMessage(sequenceNumber: Long): Promise; - // (undocumented) receiveDeferredMessages(sequenceNumbers: Long[]): Promise; - // (undocumented) receiveMode: "peekLock" | "receiveAndDelete"; - // (undocumented) renewMessageLock(lockTokenOrMessage: string | ReceivedMessage): Promise; subscribe(handler: MessageHandlers>, options?: SubscribeOptions): void; } @@ -391,42 +381,27 @@ export interface SessionMessageHandlerOptions { // @public export interface SessionReceiver { - // (undocumented) - close(): Promise; close(): Promise; diagnostics: { peek(maxMessageCount?: number): Promise; peekBySequenceNumber(fromSequenceNumber: Long, maxMessageCount?: number): Promise; }; - // (undocumented) entityPath: string; - // (undocumented) entityType: "queue" | "subscription"; - // (undocumented) getDeadLetterPath(): string; - // (undocumented) getState(): Promise; - // (undocumented) isReceivingMessages(): boolean; iterateMessages(options?: IterateMessagesOptions): MessageIterator>; receiveBatch(maxMessages: number, maxWaitTimeInSeconds?: number, options?: ReceiveBatchOptions): Promise<{ messages: ReceivedMessage[]; context: ContextType; }>; - // (undocumented) receiveDeferredMessage(sequenceNumber: Long): Promise; - // (undocumented) receiveDeferredMessages(sequenceNumbers: Long[]): Promise; - // (undocumented) receiveMode: "peekLock" | "receiveAndDelete"; - // (undocumented) - renewSessionLock(): Promise; renewSessionLock(): Promise; - // (undocumented) sessionId: string | undefined; - // (undocumented) sessionLockedUntilUtc: Date | undefined; - // (undocumented) setState(state: any): Promise; subscribe(handlers: MessageHandlers>, options?: SubscribeOptions): void; } diff --git a/sdk/servicebus/service-bus/test/receiveAndDeleteMode.spec.ts b/sdk/servicebus/service-bus/test/receiveAndDeleteMode.spec.ts index dc041aea9640..536a7b288d3f 100644 --- a/sdk/servicebus/service-bus/test/receiveAndDeleteMode.spec.ts +++ b/sdk/servicebus/service-bus/test/receiveAndDeleteMode.spec.ts @@ -3,6 +3,7 @@ import chai from "chai"; const should = chai.should(); +const expect = chai.expect; import chaiAsPromised from "chai-as-promised"; chai.use(chaiAsPromised); import { @@ -26,6 +27,7 @@ import { import { getErrorMessageNotSupportedInReceiveAndDeleteMode } from "../src/util/errors"; import { NonSessionReceiver } from "../src/serviceBusReceiverClient"; +import { DispositionType } from "../src/serviceBusMessage"; async function testPeekMsgsLength( client: ReceiverClientTypeForUser, @@ -341,179 +343,178 @@ describe("Unsupported features in ReceiveAndDelete mode", function(): void { return { message: msgs[0], context: {} as ContextWithSettlement }; } - // #RevisitCommentedTestsAfterTheSingleClientAPI - // Settlement methods don't exist in the received context in ReceiveAndDelete mode - // const testError = (err: Error, operation: DispositionType): void => { - // should.equal( - // err.message, - // getErrorMessageNotSupportedInReceiveAndDeleteMode(`${operation} the message`), - // "ErrorMessage is different than expected" - // ); - // errorWasThrown = true; - // }; - - // async function testSettlement(operation: DispositionType, useSessions?: boolean): Promise { - // const testMessages = useSessions ? TestMessage.getSessionSample() : TestMessage.getSample(); - // const msg = await sendReceiveMsg(testMessages); - - // if (operation === DispositionType.complete) { - // await msg.context.complete(msg.message).catch((err) => testError(err, operation)); - // } else if (operation === DispositionType.abandon) { - // await msg.context.abandon(msg.message).catch((err) => testError(err, operation)); - // } else if (operation === DispositionType.deadletter) { - // await msg.context.deadLetter(msg.message).catch((err) => testError(err, operation)); - // } else if (operation === DispositionType.defer) { - // await msg.context.defer(msg.message).catch((err) => testError(err, operation)); - // } - - // should.equal(errorWasThrown, true, "Error thrown flag must be true"); - - // await testPeekMsgsLength(receiverClient, 0); - // } - - // it("Partitioned Queue: complete() throws error", async function(): Promise { - // await beforeEachTest(TestClientType.PartitionedQueue); - // await testSettlement(DispositionType.complete); - // }); - - // it("Partitioned Subscription: complete() throws error", async function(): Promise { - // await beforeEachTest(TestClientType.PartitionedSubscription); - // await testSettlement(DispositionType.complete); - // }); - - // /* it("Unpartitioned Queue: complete() throws error", async function(): Promise { - // await beforeEachTest(ClientType.UnpartitionedQueue, ClientType.UnpartitionedQueue); - // await testSettlement(DispositionType.complete); - // }); - - // it("Unpartitioned Subscription: complete() throws error", async function(): Promise< - // void - // > { - // await beforeEachTest(ClientType.UnpartitionedTopic, ClientType.UnpartitionedSubscription); - // await testSettlement(DispositionType.complete); - // });*/ - - // it("Partitioned Queue with Sessions: complete() throws error #RunInBrowser", async function(): Promise< - // void - // > { - // await beforeEachTest(TestClientType.PartitionedQueueWithSessions); - // await testSettlement(DispositionType.complete, true); - // }); - - // it("Partitioned Subscription with Sessions: complete() throws error", async function(): Promise< - // void - // > { - // await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); - // await testSettlement(DispositionType.complete, true); - // }); - - // it("Partitioned Queue: abandon() throws error", async function(): Promise { - // await beforeEachTest(TestClientType.PartitionedQueue); - // await testSettlement(DispositionType.abandon); - // }); - - // it("Partitioned Subscription: abandon() throws error", async function(): Promise { - // await beforeEachTest(TestClientType.PartitionedSubscription); - // await testSettlement(DispositionType.abandon); - // }); - - // /* it("Unpartitioned Queue: abandon() throws error", async function(): Promise { - // await beforeEachTest(ClientType.UnpartitionedQueue, ClientType.UnpartitionedQueue); - // await testSettlement(DispositionType.abandon); - // }); - - // it("Unpartitioned Subscription: abandon() throws error", async function(): Promise< - // void - // > { - // await beforeEachTest(ClientType.UnpartitionedTopic, ClientType.UnpartitionedSubscription); - // await testSettlement(DispositionType.abandon); - // });*/ - - // it("Partitioned Queue with Sessions: abandon() throws error #RunInBrowser", async function(): Promise< - // void - // > { - // await beforeEachTest(TestClientType.PartitionedQueueWithSessions); - // await testSettlement(DispositionType.abandon, true); - // }); - - // it("Partitioned Subscription with Sessions: abandon() throws error", async function(): Promise< - // void - // > { - // await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); - // await testSettlement(DispositionType.abandon, true); - // }); - - // it("Partitioned Queue: defer() throws error", async function(): Promise { - // await beforeEachTest(TestClientType.PartitionedQueue); - // await testSettlement(DispositionType.defer); - // }); - - // it("Partitioned Subscription: defer() throws error", async function(): Promise { - // await beforeEachTest(TestClientType.PartitionedSubscription); - // await testSettlement(DispositionType.defer); - // }); - - // /* it("Unpartitioned Queue: defer() throws error", async function(): Promise { - // await beforeEachTest(ClientType.UnpartitionedQueue, ClientType.UnpartitionedQueue); - // await testSettlement(DispositionType.defer); - // }); - - // it("Unpartitioned Subscription: defer() throws error", async function(): Promise< - // void - // > { - // await beforeEachTest(ClientType.UnpartitionedTopic, ClientType.UnpartitionedSubscription); - // await testSettlement(DispositionType.defer); - // });*/ - - // it("Partitioned Queue with Sessions: defer() throws error #RunInBrowser", async function(): Promise< - // void - // > { - // await beforeEachTest(TestClientType.PartitionedQueueWithSessions); - // await testSettlement(DispositionType.defer, true); - // }); - - // it("Partitioned Subscription with Sessions: defer() throws error", async function(): Promise< - // void - // > { - // await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); - // await testSettlement(DispositionType.defer, true); - // }); - - // it("Partitioned Queue: deadLetter() throws error", async function(): Promise { - // await beforeEachTest(TestClientType.PartitionedQueue); - // await testSettlement(DispositionType.deadletter); - // }); - - // it("Partitioned Subscription: deadLetter() throws error", async function(): Promise { - // await beforeEachTest(TestClientType.PartitionedSubscription); - // await testSettlement(DispositionType.deadletter); - // }); - - // /* it("Unpartitioned Queue: deadLetter() throws error", async function(): Promise { - // await beforeEachTest(ClientType.UnpartitionedQueue, ClientType.UnpartitionedQueue); - // await testSettlement(DispositionType.deadletter); - // }); - - // it("Unpartitioned Subscription: deadLetter() throws error", async function(): Promise< - // void - // > { - // await beforeEachTest(ClientType.UnpartitionedTopic, ClientType.UnpartitionedSubscription); - // await testSettlement(DispositionType.deadletter); - // });*/ - - // it("Partitioned Queue with Sessions: deadLetter() throws error #RunInBrowser", async function(): Promise< - // void - // > { - // await beforeEachTest(TestClientType.PartitionedQueueWithSessions); - // await testSettlement(DispositionType.deadletter, true); - // }); - - // it("Partitioned Subscription with Sessions: deadLetter() throws error", async function(): Promise< - // void - // > { - // await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); - // await testSettlement(DispositionType.deadletter, true); - // }); + const testError = (err: Error, operation: DispositionType): void => { + expect(err.message.toLowerCase(), "ErrorMessage is different than expected").includes( + `.context.${operation} is not a function` + ); + }; + + async function testSettlement(operation: DispositionType, useSessions?: boolean): Promise { + const testMessages = useSessions ? TestMessage.getSessionSample() : TestMessage.getSample(); + const msg = await sendReceiveMsg(testMessages); + try { + if (operation === DispositionType.complete) { + await msg.context.complete(msg.message); + } else if (operation === DispositionType.abandon) { + await msg.context.abandon(msg.message); + } else if (operation === DispositionType.deadletter) { + await msg.context.deadLetter(msg.message); + } else if (operation === DispositionType.defer) { + await msg.context.defer(msg.message); + } + } catch (err) { + errorWasThrown = true; + testError(err, operation); + } + + should.equal(errorWasThrown, true, "Error thrown flag must be true"); + + await testPeekMsgsLength(receiverClient, 0); + } + + it("Partitioned Queue: complete() throws error", async function(): Promise { + await beforeEachTest(TestClientType.PartitionedQueue); + await testSettlement(DispositionType.complete); + }); + + it("Partitioned Subscription: complete() throws error", async function(): Promise { + await beforeEachTest(TestClientType.PartitionedSubscription); + await testSettlement(DispositionType.complete); + }); + + /* it("Unpartitioned Queue: complete() throws error", async function(): Promise { + await beforeEachTest(ClientType.UnpartitionedQueue, ClientType.UnpartitionedQueue); + await testSettlement(DispositionType.complete); + }); + + it("Unpartitioned Subscription: complete() throws error", async function(): Promise< + void + > { + await beforeEachTest(ClientType.UnpartitionedTopic, ClientType.UnpartitionedSubscription); + await testSettlement(DispositionType.complete); + });*/ + + it("Partitioned Queue with Sessions: complete() throws error #RunInBrowser", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedQueueWithSessions); + await testSettlement(DispositionType.complete, true); + }); + + it("Partitioned Subscription with Sessions: complete() throws error", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); + await testSettlement(DispositionType.complete, true); + }); + + it("Partitioned Queue: abandon() throws error", async function(): Promise { + await beforeEachTest(TestClientType.PartitionedQueue); + await testSettlement(DispositionType.abandon); + }); + + it("Partitioned Subscription: abandon() throws error", async function(): Promise { + await beforeEachTest(TestClientType.PartitionedSubscription); + await testSettlement(DispositionType.abandon); + }); + + /* it("Unpartitioned Queue: abandon() throws error", async function(): Promise { + await beforeEachTest(ClientType.UnpartitionedQueue, ClientType.UnpartitionedQueue); + await testSettlement(DispositionType.abandon); + }); + + it("Unpartitioned Subscription: abandon() throws error", async function(): Promise< + void + > { + await beforeEachTest(ClientType.UnpartitionedTopic, ClientType.UnpartitionedSubscription); + await testSettlement(DispositionType.abandon); + });*/ + + it("Partitioned Queue with Sessions: abandon() throws error #RunInBrowser", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedQueueWithSessions); + await testSettlement(DispositionType.abandon, true); + }); + + it("Partitioned Subscription with Sessions: abandon() throws error", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); + await testSettlement(DispositionType.abandon, true); + }); + + it("Partitioned Queue: defer() throws error", async function(): Promise { + await beforeEachTest(TestClientType.PartitionedQueue); + await testSettlement(DispositionType.defer); + }); + + it("Partitioned Subscription: defer() throws error", async function(): Promise { + await beforeEachTest(TestClientType.PartitionedSubscription); + await testSettlement(DispositionType.defer); + }); + + /* it("Unpartitioned Queue: defer() throws error", async function(): Promise { + await beforeEachTest(ClientType.UnpartitionedQueue, ClientType.UnpartitionedQueue); + await testSettlement(DispositionType.defer); + }); + + it("Unpartitioned Subscription: defer() throws error", async function(): Promise< + void + > { + await beforeEachTest(ClientType.UnpartitionedTopic, ClientType.UnpartitionedSubscription); + await testSettlement(DispositionType.defer); + });*/ + + it("Partitioned Queue with Sessions: defer() throws error #RunInBrowser", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedQueueWithSessions); + await testSettlement(DispositionType.defer, true); + }); + + it("Partitioned Subscription with Sessions: defer() throws error", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); + await testSettlement(DispositionType.defer, true); + }); + + it("Partitioned Queue: deadLetter() throws error", async function(): Promise { + await beforeEachTest(TestClientType.PartitionedQueue); + await testSettlement(DispositionType.deadletter); + }); + + it("Partitioned Subscription: deadLetter() throws error", async function(): Promise { + await beforeEachTest(TestClientType.PartitionedSubscription); + await testSettlement(DispositionType.deadletter); + }); + + /* it("Unpartitioned Queue: deadLetter() throws error", async function(): Promise { + await beforeEachTest(ClientType.UnpartitionedQueue, ClientType.UnpartitionedQueue); + await testSettlement(DispositionType.deadletter); + }); + + it("Unpartitioned Subscription: deadLetter() throws error", async function(): Promise< + void + > { + await beforeEachTest(ClientType.UnpartitionedTopic, ClientType.UnpartitionedSubscription); + await testSettlement(DispositionType.deadletter); + });*/ + + it("Partitioned Queue with Sessions: deadLetter() throws error #RunInBrowser", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedQueueWithSessions); + await testSettlement(DispositionType.deadletter, true); + }); + + it("Partitioned Subscription with Sessions: deadLetter() throws error", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); + await testSettlement(DispositionType.deadletter, true); + }); async function testRenewLock(): Promise { const msg = await sendReceiveMsg(TestMessage.getSample()); From 738c902c444affe55c42705c90dcbf8d55e03da5 Mon Sep 17 00:00:00 2001 From: Azure SDK Bot <53356347+azure-sdk@users.noreply.github.com> Date: Fri, 13 Mar 2020 13:59:09 -0700 Subject: [PATCH 09/28] Sync eng/common directory with azure-sdk-tools repository (#7813) --- eng/common/TestResources/deploy-test-resources.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eng/common/TestResources/deploy-test-resources.yml b/eng/common/TestResources/deploy-test-resources.yml index c2c0c616427f..d63c0e1ac161 100644 --- a/eng/common/TestResources/deploy-test-resources.yml +++ b/eng/common/TestResources/deploy-test-resources.yml @@ -11,7 +11,7 @@ parameters: steps: # New-TestResources command requires Az module - - pwsh: Install-Module -Name Az -Scope CurrentUser -Force + - pwsh: Install-Module -Name Az -Scope CurrentUser -AllowClobber -Force -Verbose displayName: Install Azure PowerShell module # Command sets an environment variable in the pipeline's context: From 1ef7dcc58a4ffc07787595bb498ce84fc70b720a Mon Sep 17 00:00:00 2001 From: Richard Park <51494936+richardpark-msft@users.noreply.github.com> Date: Fri, 13 Mar 2020 14:31:45 -0700 Subject: [PATCH 10/28] [service-bus] Implementing the top level `ServiceBusClient` design (#7803) - Created a parent object (ServiceBusClient) that you can use to spawn Receiver's and Senders' - Removed ServiceBusClientReceiver and ServiceBusClientSender and replaced them with interfaces. - Reworked the tests in preparation for running them in parallel. --- .../service-bus/review/service-bus.api.md | 112 +- sdk/servicebus/service-bus/src/index.ts | 15 +- .../service-bus/src/internalReceivers.ts | 67 +- sdk/servicebus/service-bus/src/models.ts | 22 +- .../service-bus/src/receivers/receiver.ts | 657 ++++++ .../src/receivers/sessionReceiver.ts | 624 ++++++ .../service-bus/src/receivers/shared.ts | 88 + .../service-bus/src/serviceBusClient.ts | 269 +++ .../src/serviceBusReceiverClient.ts | 897 -------- .../service-bus/src/serviceBusSenderClient.ts | 204 -- .../service-bus/test/batchReceiver.spec.ts | 1673 ++++++++------- .../service-bus/test/deferredMessage.spec.ts | 627 +++--- .../test/invalidParameters.spec.ts | 1791 ++++++++-------- .../test/receiveAndDeleteMode.spec.ts | 1160 ++++++----- .../service-bus/test/renewLock.spec.ts | 891 ++++---- .../test/renewLockSessions.spec.ts | 1050 +++++----- .../service-bus/test/sendSchedule.spec.ts | 1151 ++++++----- .../sessionsRequiredCleanEntityTests.spec.ts | 329 +++ .../service-bus/test/sessionsTests.spec.ts | 778 +++---- .../test/streamingReceiver.spec.ts | 1834 +++++++++-------- .../test/streamingReceiverSessions.spec.ts | 1763 ++++++++-------- .../service-bus/test/topicFilters.spec.ts | 935 ++++----- .../service-bus/test/track2.samples.spec.ts | 945 +++------ .../service-bus/test/utils/testUtils.ts | 270 +-- .../service-bus/test/utils/testutils2.ts | 365 ++++ 25 files changed, 9520 insertions(+), 8997 deletions(-) create mode 100644 sdk/servicebus/service-bus/src/receivers/receiver.ts create mode 100644 sdk/servicebus/service-bus/src/receivers/sessionReceiver.ts create mode 100644 sdk/servicebus/service-bus/src/receivers/shared.ts create mode 100644 sdk/servicebus/service-bus/src/serviceBusClient.ts delete mode 100644 sdk/servicebus/service-bus/src/serviceBusReceiverClient.ts delete mode 100644 sdk/servicebus/service-bus/src/serviceBusSenderClient.ts create mode 100644 sdk/servicebus/service-bus/test/sessionsRequiredCleanEntityTests.spec.ts create mode 100644 sdk/servicebus/service-bus/test/utils/testutils2.ts diff --git a/sdk/servicebus/service-bus/review/service-bus.api.md b/sdk/servicebus/service-bus/review/service-bus.api.md index 1ef0f6bb8bf0..7489dd73819c 100644 --- a/sdk/servicebus/service-bus/review/service-bus.api.md +++ b/sdk/servicebus/service-bus/review/service-bus.api.md @@ -30,9 +30,6 @@ export type AuthorizationRule = { secondaryKey?: string; }; -// @public -export type ClientTypeT = SessionsEnabledT extends "nosessions" ? EntityTypeT extends "queue" ? NonSessionReceiver : NonSessionReceiver & SubscriptionRuleManagement : EntityTypeT extends "queue" ? SessionReceiver : SessionReceiver & SubscriptionRuleManagement; - // @public export interface Closeable { close(): Promise; @@ -81,12 +78,12 @@ export { Delivery } // @public export type EntityStatus = "Active" | "Creating" | "Deleting" | "ReceiveDisabled" | "SendDisabled" | "Disabled" | "Renaming" | "Restoring" | "Unknown"; -export { HttpOperationResponse } - // @public -export interface IterateMessagesOptions extends OperationOptions { +export interface GetMessageIteratorOptions extends OperationOptions { } +export { HttpOperationResponse } + // @public export interface MessageAndContext { context: ContextT; @@ -120,29 +117,6 @@ export type MessageIterator = AsyncIterable { - close(): Promise; - diagnostics: { - peek(maxMessageCount?: number): Promise; - peekBySequenceNumber(fromSequenceNumber: Long, maxMessageCount?: number): Promise; - }; - entityPath: string; - entityType: "queue" | "subscription"; - getDeadLetterPath(): string; - isReceivingMessages(): boolean; - iterateMessages(options?: IterateMessagesOptions): MessageIterator>; - receiveBatch(maxMessages: number, maxWaitTimeInSeconds?: number, options?: ReceiveBatchOptions): Promise<{ - messages: ReceivedMessage[]; - context: ContextType; - }>; - receiveDeferredMessage(sequenceNumber: Long): Promise; - receiveDeferredMessages(sequenceNumbers: Long[]): Promise; - receiveMode: "peekLock" | "receiveAndDelete"; - renewMessageLock(lockTokenOrMessage: string | ReceivedMessage): Promise; - subscribe(handler: MessageHandlers>, options?: SubscribeOptions): void; -} - // @public export interface OnError { (error: MessagingError | Error): void; @@ -242,6 +216,32 @@ export enum ReceiveMode { receiveAndDelete = 2 } +// @public +export interface Receiver { + close(): Promise; + diagnostics: { + peek(maxMessageCount?: number): Promise; + peekBySequenceNumber(fromSequenceNumber: Long, maxMessageCount?: number): Promise; + }; + entityPath: string; + entityType: "queue" | "subscription"; + getDeadLetterPath(): string; + getMessageIterator(options?: GetMessageIteratorOptions): AsyncIterableIterator<{ + message: ReceivedMessage; + context: ContextT; + }>; + isReceivingMessages(): boolean; + receiveBatch(maxMessages: number, maxWaitTimeInSeconds?: number, options?: ReceiveBatchOptions): Promise<{ + messages: ReceivedMessage[]; + context: ContextT; + }>; + receiveDeferredMessage(sequenceNumber: Long, options?: OperationOptions): Promise; + receiveDeferredMessages(sequenceNumbers: Long[], options?: OperationOptions): Promise; + receiveMode: "peekLock" | "receiveAndDelete"; + renewMessageLock(lockTokenOrMessage: string | ReceivedMessage): Promise; + subscribe(handler: MessageHandlers, options?: SubscribeOptions): void; +} + export { RetryOptions } // @public @@ -333,35 +333,6 @@ export class ServiceBusMessage implements ReceivedMessageInfo { viaPartitionKey?: string; } -// @public -export interface ServiceBusReceiverClient { - new (queueAuth: QueueAuth, receiveMode: "peekLock", options?: ServiceBusClientOptions): ClientTypeT<"peekLock", "queue", "nosessions">; - new (queueAuth: QueueAuth, receiveMode: "receiveAndDelete", options?: ServiceBusClientOptions): ClientTypeT<"receiveAndDelete", "queue", "nosessions">; - new (queueAuth: QueueAuth, receiveMode: "peekLock", session: Session, options?: ServiceBusClientOptions): ClientTypeT<"peekLock", "queue", "sessions">; - new (queueAuth: QueueAuth, receiveMode: "receiveAndDelete", session: Session, options?: ServiceBusClientOptions): ClientTypeT<"receiveAndDelete", "queue", "sessions">; - new (subscriptionAuth: SubscriptionAuth, receiveMode: "peekLock", options?: ServiceBusClientOptions): ClientTypeT<"peekLock", "subscription", "nosessions">; - new (subscriptionAuth: SubscriptionAuth, receiveMode: "receiveAndDelete", options?: ServiceBusClientOptions): ClientTypeT<"receiveAndDelete", "subscription", "nosessions">; - new (subscriptionAuth: SubscriptionAuth, receiveMode: "peekLock", session: Session, options?: ServiceBusClientOptions): ClientTypeT<"peekLock", "subscription", "sessions">; - new (subscriptionAuth: SubscriptionAuth, receiveMode: "receiveAndDelete", session: Session, options?: ServiceBusClientOptions): ClientTypeT<"receiveAndDelete", "subscription", "sessions">; -} - -// @public -export const ServiceBusReceiverClient: ServiceBusReceiverClient; - -// @public -export class ServiceBusSenderClient { - constructor(entityConnectionString: string, options?: ServiceBusClientOptions); - constructor(serviceBusConnectionString: string, entityName: string, options?: ServiceBusClientOptions); - constructor(host: string, entityName: string, credential: TokenCredential, options?: ServiceBusClientOptions); - cancelScheduledMessage(sequenceNumber: Long): Promise; - cancelScheduledMessages(sequenceNumbers: Long[]): Promise; - close(): Promise; - scheduleMessage(scheduledEnqueueTimeUtc: Date, message: SendableMessageInfo): Promise; - scheduleMessages(scheduledEnqueueTimeUtc: Date, messages: SendableMessageInfo[]): Promise; - send(message: SendableMessageInfo): Promise; - sendBatch(messages: SendableMessageInfo[]): Promise; -} - // @public export interface Session { connections?: SessionConnections; @@ -380,30 +351,16 @@ export interface SessionMessageHandlerOptions { } // @public -export interface SessionReceiver { - close(): Promise; +export interface SessionReceiver extends Receiver { diagnostics: { peek(maxMessageCount?: number): Promise; peekBySequenceNumber(fromSequenceNumber: Long, maxMessageCount?: number): Promise; }; - entityPath: string; - entityType: "queue" | "subscription"; - getDeadLetterPath(): string; getState(): Promise; - isReceivingMessages(): boolean; - iterateMessages(options?: IterateMessagesOptions): MessageIterator>; - receiveBatch(maxMessages: number, maxWaitTimeInSeconds?: number, options?: ReceiveBatchOptions): Promise<{ - messages: ReceivedMessage[]; - context: ContextType; - }>; - receiveDeferredMessage(sequenceNumber: Long): Promise; - receiveDeferredMessages(sequenceNumbers: Long[]): Promise; - receiveMode: "peekLock" | "receiveAndDelete"; renewSessionLock(): Promise; sessionId: string | undefined; sessionLockedUntilUtc: Date | undefined; setState(state: any): Promise; - subscribe(handlers: MessageHandlers>, options?: SubscribeOptions): void; } // @public @@ -493,15 +450,6 @@ export interface SubscriptionOptions { userMetadata?: string; } -// @public -export interface SubscriptionRuleManagement { - addRule(ruleName: string, filter: boolean | string | CorrelationFilter, sqlRuleActionExpression?: string): Promise; - // (undocumented) - readonly defaultRuleName: string; - getRules(): Promise; - removeRule(ruleName: string): Promise; -} - export { TokenCredential } export { TokenType } diff --git a/sdk/servicebus/service-bus/src/index.ts b/sdk/servicebus/service-bus/src/index.ts index bd2c28ab7cc0..bfbfe0d3becb 100644 --- a/sdk/servicebus/service-bus/src/index.ts +++ b/sdk/servicebus/service-bus/src/index.ts @@ -47,16 +47,6 @@ export { export { MessageCountDetails, AuthorizationRule, EntityStatus } from "./util/utils"; -export { ServiceBusSenderClient } from "./serviceBusSenderClient"; - -export { - ServiceBusReceiverClient, - NonSessionReceiver, - SessionReceiver, - ClientTypeT, - SubscriptionRuleManagement -} from "./serviceBusReceiverClient"; - export { SessionConnections, ReceivedMessage, @@ -64,7 +54,7 @@ export { Session, QueueAuth, SubscriptionAuth, - IterateMessagesOptions, + GetMessageIteratorOptions, ReceiveBatchOptions, SubscribeOptions, MessageHandlerOptions, @@ -74,3 +64,6 @@ export { MessageAndContext, MessageIterator } from "./models"; + +export { Receiver } from "./receivers/receiver"; +export { SessionReceiver } from "./receivers/sessionReceiver"; diff --git a/sdk/servicebus/service-bus/src/internalReceivers.ts b/sdk/servicebus/service-bus/src/internalReceivers.ts index 69f81708c3bc..bb82c92d8e09 100644 --- a/sdk/servicebus/service-bus/src/internalReceivers.ts +++ b/sdk/servicebus/service-bus/src/internalReceivers.ts @@ -1,5 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. + import Long from "long"; import * as log from "./log"; import { StreamingReceiver } from "./core/streamingReceiver"; @@ -25,6 +26,11 @@ import { } from "./util/errors"; import { RuleDescription, CorrelationFilter } from "."; import { MessageHandlerOptions, ReceivedMessage } from "./models"; +import { + addSubscriptionRule, + removeSubscriptionRule, + getSubscriptionRules +} from "./receivers/shared"; /** * The Receiver class can be used to receive messages in a batch or by registering handlers. @@ -354,21 +360,20 @@ export class InternalReceiver { // #region topic-filters - getRules(entityPath: string): Promise { - return getRules(this._context, entityPath); + getRules(): Promise { + return getSubscriptionRules(this._context); } - removeRule(entityPath: string, ruleName: string): Promise { - return removeRule(this._context, entityPath, ruleName); + removeRule(ruleName: string): Promise { + return removeSubscriptionRule(this._context, ruleName); } addRule( - entityPath: string, ruleName: string, filter: boolean | string | CorrelationFilter, sqlRuleActionExpression?: string ): Promise { - return addRule(this._context, entityPath, ruleName, filter, sqlRuleActionExpression); + return addSubscriptionRule(this._context, ruleName, filter, sqlRuleActionExpression); } // #endregion @@ -838,21 +843,20 @@ export class InternalSessionReceiver { // #region topic-filters - getRules(entityPath: string): Promise { - return getRules(this._context, entityPath); + getRules(): Promise { + return getSubscriptionRules(this._context); } - removeRule(entityPath: string, ruleName: string): Promise { - return removeRule(this._context, entityPath, ruleName); + removeRule(ruleName: string): Promise { + return removeSubscriptionRule(this._context, ruleName); } addRule( - entityPath: string, ruleName: string, filter: boolean | string | CorrelationFilter, sqlRuleActionExpression?: string ): Promise { - return addRule(this._context, entityPath, ruleName, filter, sqlRuleActionExpression); + return addSubscriptionRule(this._context, ruleName, filter, sqlRuleActionExpression); } // #endregion @@ -895,42 +899,3 @@ export class InternalSessionReceiver { } // #region topic-filters - -function getRules(context: ClientEntityContext, entityPath: string): Promise { - if (entityPath.includes("/Subscriptions/")) { - throwErrorIfClientOrConnectionClosed(context.namespace, entityPath, context.isClosed); - return context.managementClient!.getRules(); - } else { - throw new Error("Only for a subscription"); - } -} - -function removeRule( - context: ClientEntityContext, - entityPath: string, - ruleName: string -): Promise { - if (entityPath.includes("/Subscriptions/")) { - throwErrorIfClientOrConnectionClosed(context.namespace, entityPath, context.isClosed); - return context.managementClient!.removeRule(ruleName); - } else { - throw new Error("Only for a subscription"); - } -} - -function addRule( - context: ClientEntityContext, - entityPath: string, - ruleName: string, - filter: boolean | string | CorrelationFilter, - sqlRuleActionExpression?: string -): Promise { - if (entityPath.includes("/Subscriptions/")) { - throwErrorIfClientOrConnectionClosed(context.namespace, entityPath, context.isClosed); - return context.managementClient!.addRule(ruleName, filter, sqlRuleActionExpression); - } else { - throw new Error("Only for a subscription"); - } -} - -// #endregion diff --git a/sdk/servicebus/service-bus/src/models.ts b/sdk/servicebus/service-bus/src/models.ts index 55e71d6856c8..8a73307a5e18 100644 --- a/sdk/servicebus/service-bus/src/models.ts +++ b/sdk/servicebus/service-bus/src/models.ts @@ -217,7 +217,7 @@ export interface ReceiveBatchOptions extends OperationOptions {} /** * Options when getting an iterable iterator from Service Bus. */ -export interface IterateMessagesOptions extends OperationOptions {} +export interface GetMessageIteratorOptions extends OperationOptions {} /** * Options used when subscribing to a Service Bus queue or subscription. @@ -253,3 +253,23 @@ export interface MessageHandlerOptions { */ maxConcurrentCalls?: number; } + +/** + * Describes the options passed to the `createReceiver` method when using a Queue/Subscription that + * has sessions enabled. + */ +export interface GetSessionReceiverOptions extends OperationOptions { + /** + * @property The maximum duration in seconds + * until which, the lock on the session will be renewed automatically by the sdk. + * - **Default**: `300` seconds (5 minutes). + * - **To disable autolock renewal**, set this to `0`. + */ + maxSessionAutoRenewLockDurationInSeconds?: number; + + /** + * The session ID to open. If `undefined` we will connect to the next available + * unlocked session. + */ + sessionId?: string; +} diff --git a/sdk/servicebus/service-bus/src/receivers/receiver.ts b/sdk/servicebus/service-bus/src/receivers/receiver.ts new file mode 100644 index 000000000000..a222bfe7c910 --- /dev/null +++ b/sdk/servicebus/service-bus/src/receivers/receiver.ts @@ -0,0 +1,657 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +import { + MessageHandlers, + SubscribeOptions, + GetMessageIteratorOptions, + ReceiveBatchOptions, + ReceivedMessage, + MessageHandlerOptions +} from "../models"; +import { OperationOptions } from "@azure/core-auth"; +import { ServiceBusMessage, RuleDescription, CorrelationFilter } from ".."; +import { ClientEntityContext } from "../clientEntityContext"; +import { + throwErrorIfConnectionClosed, + getAlreadyReceivingErrorMsg, + getReceiverClosedErrorMsg, + throwTypeErrorIfParameterMissing, + getErrorMessageNotSupportedInReceiveAndDeleteMode, + throwTypeErrorIfParameterNotLong, + throwTypeErrorIfParameterNotLongArray, + throwErrorIfClientOrConnectionClosed +} from "../util/errors"; +import * as log from "../log"; +import { OnMessage, OnError, ReceiveOptions } from "../core/messageReceiver"; +import { StreamingReceiver } from "../core/streamingReceiver"; +import { BatchingReceiver } from "../core/batchingReceiver"; +import { + getSubscriptionRules, + removeSubscriptionRule, + addSubscriptionRule, + settlementContext, + assertValidMessageHandlers +} from "./shared"; +import { convertToInternalReceiveMode } from "../constructorHelpers"; +import Long from "long"; + +/** + * A receiver that does not handle sessions. + */ +export interface Receiver { + /** + * Streams messages to message handlers. + * @param handler A handler that gets called for messages and errors. + * @param options Options for subscribe. + */ + subscribe(handler: MessageHandlers, options?: SubscribeOptions): void; + + /** + * Returns an iterator that can be used to receive messages from Service Bus. + * @param options Options for getMessageIterator. + */ + getMessageIterator( + options?: GetMessageIteratorOptions + ): AsyncIterableIterator<{ message: ReceivedMessage; context: ContextT }>; + + /** + * Receives, at most, `maxMessages` worth of messages. + * @param maxMessages The maximum number of messages to accept. + * @param maxWaitTimeInSeconds The maximum time to wait, in seconds, for messages to arrive. + * @param options Options for receiveBatch. + */ + receiveBatch( + maxMessages: number, + maxWaitTimeInSeconds?: number, + options?: ReceiveBatchOptions + ): Promise<{ messages: ReceivedMessage[]; context: ContextT }>; + + // TODO: move to message object itself. + + /** + * Renews the lock on the message for the duration as specified during the Queue/Subscription + * creation. + * - Check the `lockedUntilUtc` property on the message for the time when the lock expires. + * - If a message is not settled (using either `complete()`, `defer()` or `deadletter()`, + * before its lock expires, then the message lands back in the Queue/Subscription for the next + * receive operation. + * + * @param lockTokenOrMessage - The `lockToken` property of the message or the message itself. + * @returns Promise - New lock token expiry date and time in UTC format. + * @throws Error if the underlying connection, client or receiver is closed. + * @throws MessagingError if the service returns an error while renewing message lock. + */ + renewMessageLock(lockTokenOrMessage: string | ReceivedMessage): Promise; + + /** + * Returns a promise that resolves to a deferred message identified by the given `sequenceNumber`. + * @param {Long} sequenceNumber The sequence number of the message that needs to be received. + * @returns {(Promise)} + * - Returns `Message` identified by sequence number. + * - Returns `undefined` if no such message is found. + * @throws Error if the underlying connection or receiver is closed. + * @throws MessagingError if the service returns an error while receiving deferred message. + */ + receiveDeferredMessage( + sequenceNumber: Long, + options?: OperationOptions + ): Promise; + + /** + * Returns a promise that resolves to an array of deferred messages identified by given `sequenceNumbers`. + * @param {Long[]} sequenceNumbers An array of sequence numbers for the messages that need to be received. + * @returns {Promise} + * - Returns a list of messages identified by the given sequenceNumbers. + * - Returns an empty list if no messages are found. + * @throws Error if the underlying connection or receiver is closed. + * @throws MessagingError if the service returns an error while receiving deferred messages. + * @memberof SessionReceiver + */ + receiveDeferredMessages( + sequenceNumbers: Long[], + options?: OperationOptions + ): Promise; + /** + * Indicates whether the receiver is currently receiving messages or not. + * When this returns true, new `registerMessageHandler()` or `receiveMessages()` calls cannot be made. + * @returns {boolean} + * @memberof SessionReceiver + */ + isReceivingMessages(): boolean; + /** + * Returns the corresponding dead letter queue path for the client entity - meant for both queue and subscription. + * @returns {string} + * @memberof SessionReceiver + */ + getDeadLetterPath(): string; + + // TODO: not sure these need to be on the interface + + /** + * Type of the entity with which the client is created. + * + * @type {("queue" | "subscription")} + * @memberof SessionReceiver + */ + entityType: "queue" | "subscription"; + /** + * Path for the client entity. + * + * @type {string} + * @memberof SessionReceiver + */ + entityPath: string; + /** + * ReceiveMode provided to the client. + * + * @type {("peekLock" | "receiveAndDelete")} + * @memberof SessionReceiver + */ + receiveMode: "peekLock" | "receiveAndDelete"; + + /** + * Closes the receiver. + */ + close(): Promise; + + /** + * Methods related to service bus diagnostics. + */ + diagnostics: { + /** + * Peek within a queue or subscription. + * NOTE: this method does not respect message locks or increment delivery count + * for messages. + * @param maxMessageCount The maximum number of messages to retrieve. + */ + peek(maxMessageCount?: number): Promise; + + /** + * Peek within a queue or subscription, starting with a specific sequence number. + * NOTE: this method does not respect message locks or increment delivery count + * for messages. + * @param fromSequenceNumber The sequence number to start peeking from (inclusive). + * @param maxMessageCount The maximum number of messages to retrieve. + */ + peekBySequenceNumber( + fromSequenceNumber: Long, + maxMessageCount?: number + ): Promise; + }; +} + +/** + * Methods to manage rules for subscriptions. More information about subscription rules + * can be found here: https://docs.microsoft.com/en-us/azure/service-bus-messaging/topic-filters + */ +export interface SubscriptionRuleManagement { + /** + * Gets all rules associated with the subscription + * @throws Error if the SubscriptionClient or the underlying connection is closed. + * @throws MessagingError if the service returns an error while retrieving rules. + */ + getRules(): Promise; + + /** + * Removes the rule on the subscription identified by the given rule name. + * + * **Caution**: If all rules on a subscription are removed, then the subscription will not receive + * any more messages. + * @param ruleName + * @throws Error if the SubscriptionClient or the underlying connection is closed. + * @throws MessagingError if the service returns an error while removing rules. + */ + + removeRule(ruleName: string): Promise; + /** + * Adds a rule on the subscription as defined by the given rule name, filter and action. + * + * **Note**: Remove the default true filter on the subscription before adding a rule. + * Otherwise, the added rule will have no affect as the true filter will always result in + * the subscription receiving all messages. + * @param ruleName Name of the rule + * @param filter A Boolean, SQL expression or a Correlation filter. For SQL Filter syntax, see + * {@link https://docs.microsoft.com/en-us/azure/service-bus-messaging/service-bus-messaging-sql-filter SQLFilter syntax}. + * @param sqlRuleActionExpression Action to perform if the message satisfies the filtering expression. For SQL Rule Action syntax, + * see {@link https://docs.microsoft.com/en-us/azure/service-bus-messaging/service-bus-messaging-sql-rule-action SQLRuleAction syntax}. + * @throws Error if the SubscriptionClient or the underlying connection is closed. + * @throws MessagingError if the service returns an error while adding rules. + */ + addRule( + ruleName: string, + filter: boolean | string | CorrelationFilter, + sqlRuleActionExpression?: string + ): Promise; + readonly defaultRuleName: string; +} + +export class ReceiverImpl implements Receiver, SubscriptionRuleManagement { + /** + * @property Describes the amqp connection context for the QueueClient. + */ + private _context: ClientEntityContext; + /** + * @property {boolean} [_isClosed] Denotes if close() was called on this receiver + */ + private _isClosed: boolean = false; + + public entityPath: string; + + public diagnostics: { + peek(maxMessageCount?: number): Promise; + peekBySequenceNumber( + fromSequenceNumber: Long, + maxMessageCount?: number + ): Promise; + }; + + /** + * @throws Error if the underlying connection is closed. + */ + constructor( + context: ClientEntityContext, + public receiveMode: "peekLock" | "receiveAndDelete", + public entityType: "queue" | "subscription" + ) { + throwErrorIfConnectionClosed(context.namespace); + this.entityPath = context.entityPath; + this._context = context; + this.diagnostics = { + peek: (maxMessageCount) => this._peek(maxMessageCount), + peekBySequenceNumber: (fromSequenceNumber, maxMessageCount) => + this._peekBySequenceNumber(fromSequenceNumber, maxMessageCount) + }; + } + + private _throwIfAlreadyReceiving(): void { + if (this.isReceivingMessages()) { + const errorMessage = getAlreadyReceivingErrorMsg(this._context.entityPath); + const error = new Error(errorMessage); + log.error(`[${this._context.namespace.connectionId}] %O`, error); + throw error; + } + } + + private _throwIfReceiverOrConnectionClosed(): void { + throwErrorIfConnectionClosed(this._context.namespace); + if (this.isClosed) { + const errorMessage = getReceiverClosedErrorMsg( + this._context.entityPath, + this._context.clientType, + this._context.isClosed + ); + const error = new Error(errorMessage); + log.error(`[${this._context.namespace.connectionId}] %O`, error); + throw error; + } + } + + getDeadLetterPath(): string { + return `${this.entityPath}/$DeadLetterQueue`; + } + + /** + * @property Returns `true` if the receiver is closed. This can happen either because the receiver + * itself has been closed or the client that created it has been closed. + * @readonly + */ + public get isClosed(): boolean { + return this._isClosed || this._context.isClosed; + } + + /** + * Registers handlers to deal with the incoming stream of messages over an AMQP receiver link + * from a Queue/Subscription. + * To stop receiving messages, call `close()` on the Receiver. + * + * Throws an error if there is another receive operation in progress on the same receiver. If you + * are not sure whether there is another receive operation running, check the `isReceivingMessages` + * property on the receiver. + * + * @param onMessage - Handler for processing each incoming message. + * @param onError - Handler for any error that occurs while receiving or processing messages. + * @param options - Options to control if messages should be automatically completed, and/or have + * their locks automatically renewed. You can control the maximum number of messages that should + * be concurrently processed. You can also provide a timeout in seconds to denote the + * amount of time to wait for a new message before closing the receiver. + * + * @returns void + * @throws Error if the underlying connection or receiver is closed. + * @throws Error if current receiver is already in state of receiving messages. + * @throws MessagingError if the service returns an error while receiving messages. These are bubbled up to be handled by user provided `onError` handler. + */ + private _registerMessageHandler( + onMessage: OnMessage, + onError: OnError, + options?: MessageHandlerOptions + ): void { + this._throwIfReceiverOrConnectionClosed(); + this._throwIfAlreadyReceiving(); + const connId = this._context.namespace.connectionId; + throwTypeErrorIfParameterMissing(connId, "onMessage", onMessage); + throwTypeErrorIfParameterMissing(connId, "onError", onError); + if (typeof onMessage !== "function") { + throw new TypeError("The parameter 'onMessage' must be of type 'function'."); + } + if (typeof onError !== "function") { + throw new TypeError("The parameter 'onError' must be of type 'function'."); + } + + StreamingReceiver.create(this._context, { + ...options, + receiveMode: convertToInternalReceiveMode(this.receiveMode) + }) + .then(async (sReceiver) => { + if (!sReceiver) { + return; + } + if (!this.isClosed) { + sReceiver.receive(onMessage, onError); + } else { + await sReceiver.close(); + } + return; + }) + .catch((err) => { + onError(err); + }); + } + + /** + * Returns a promise that resolves to an array of messages based on given count and timeout over + * an AMQP receiver link from a Queue/Subscription. + * + * Throws an error if there is another receive operation in progress on the same receiver. If you + * are not sure whether there is another receive operation running, check the `isReceivingMessages` + * property on the receiver. + * + * @param maxMessageCount The maximum number of messages to receive from Queue/Subscription. + * @param maxWaitTimeInSeconds The total wait time in seconds until which the receiver will attempt to receive specified number of messages. + * If this time elapses before the `maxMessageCount` is reached, then messages collected till then will be returned to the user. + * - **Default**: `60` seconds. + * @returns Promise A promise that resolves with an array of Message objects. + * @throws Error if the underlying connection, client or receiver is closed. + * @throws Error if current receiver is already in state of receiving messages. + * @throws MessagingError if the service returns an error while receiving messages. + */ + async receiveBatch( + maxMessageCount: number, + maxWaitTimeInSeconds?: number, + options?: ReceiveBatchOptions + ): Promise<{ messages: ReceivedMessage[]; context: ContextT }> { + this._throwIfReceiverOrConnectionClosed(); + this._throwIfAlreadyReceiving(); + + if (!this._context.batchingReceiver || !this._context.batchingReceiver.isOpen()) { + const options: ReceiveOptions = { + maxConcurrentCalls: 0, + receiveMode: convertToInternalReceiveMode(this.receiveMode) + }; + this._context.batchingReceiver = BatchingReceiver.create(this._context, options); + } + + const messages = await this._context.batchingReceiver.receive( + maxMessageCount, + maxWaitTimeInSeconds + ); + + return { + messages, + context: this.getContext() + }; + } + + /** + * Gets an async iterator over messages from the receiver. + * + * Throws an error if there is another receive operation in progress on the same receiver. If you + * are not sure whether there is another receive operation running, check the `isReceivingMessages` + * property on the receiver. + * + * If the iterator is not able to fetch a new message in over a minute, `undefined` will be returned. + * @throws Error if the underlying connection, client or receiver is closed. + * @throws Error if current receiver is already in state of receiving messages. + * @throws MessagingError if the service returns an error while receiving messages. + */ + async *getMessageIterator( + options?: GetMessageIteratorOptions + ): AsyncIterableIterator<{ + message: ReceivedMessage; + context: ContextT; + }> { + while (true) { + const { messages, context } = await this.receiveBatch(1); + + yield { + message: messages[0], + context + }; + } + } + + /** + * Renews the lock on the message for the duration as specified during the Queue/Subscription + * creation. + * - Check the `lockedUntilUtc` property on the message for the time when the lock expires. + * - If a message is not settled (using either `complete()`, `defer()` or `deadletter()`, + * before its lock expires, then the message lands back in the Queue/Subscription for the next + * receive operation. + * + * @param lockTokenOrMessage - The `lockToken` property of the message or the message itself. + * @returns Promise - New lock token expiry date and time in UTC format. + * @throws Error if the underlying connection, client or receiver is closed. + * @throws MessagingError if the service returns an error while renewing message lock. + */ + async renewMessageLock(lockTokenOrMessage: string | ReceivedMessage): Promise { + this._throwIfReceiverOrConnectionClosed(); + if (this.receiveMode !== "peekLock") { + throw new Error(getErrorMessageNotSupportedInReceiveAndDeleteMode("renew the message lock")); + } + throwTypeErrorIfParameterMissing( + this._context.namespace.connectionId, + "lockTokenOrMessage", + lockTokenOrMessage + ); + + const lockToken = + lockTokenOrMessage instanceof ServiceBusMessage + ? String(lockTokenOrMessage.lockToken) + : String(lockTokenOrMessage); + + const lockedUntilUtc = await this._context.managementClient!.renewLock(lockToken); + + return lockedUntilUtc; + } + + /** + * Returns a promise that resolves to a deferred message identified by the given `sequenceNumber`. + * @param sequenceNumber The sequence number of the message that needs to be received. + * @returns Promise + * - Returns `Message` identified by sequence number. + * - Returns `undefined` if no such message is found. + * @throws Error if the underlying connection, client or receiver is closed. + * @throws MessagingError if the service returns an error while receiving deferred message. + */ + async receiveDeferredMessage(sequenceNumber: Long): Promise { + this._throwIfReceiverOrConnectionClosed(); + throwTypeErrorIfParameterMissing( + this._context.namespace.connectionId, + "sequenceNumber", + sequenceNumber + ); + throwTypeErrorIfParameterNotLong( + this._context.namespace.connectionId, + "sequenceNumber", + sequenceNumber + ); + + const messages = await this._context.managementClient!.receiveDeferredMessages( + [sequenceNumber], + convertToInternalReceiveMode(this.receiveMode) + ); + return messages[0]; + } + + /** + * Returns a promise that resolves to an array of deferred messages identified by given `sequenceNumbers`. + * @param sequenceNumbers An array of sequence numbers for the messages that need to be received. + * @returns Promise + * - Returns a list of messages identified by the given sequenceNumbers. + * - Returns an empty list if no messages are found. + * @throws Error if the underlying connection, client or receiver is closed. + * @throws MessagingError if the service returns an error while receiving deferred messages. + */ + async receiveDeferredMessages(sequenceNumbers: Long[]): Promise { + this._throwIfReceiverOrConnectionClosed(); + throwTypeErrorIfParameterMissing( + this._context.namespace.connectionId, + "sequenceNumbers", + sequenceNumbers + ); + if (!Array.isArray(sequenceNumbers)) { + sequenceNumbers = [sequenceNumbers]; + } + throwTypeErrorIfParameterNotLongArray( + this._context.namespace.connectionId, + "sequenceNumbers", + sequenceNumbers + ); + + return this._context.managementClient!.receiveDeferredMessages( + sequenceNumbers, + convertToInternalReceiveMode(this.receiveMode) + ); + } + + // ManagementClient methods # Begin + + private async _peek(maxMessageCount?: number): Promise { + throwErrorIfClientOrConnectionClosed( + this._context.namespace, + this._context.entityPath, + this._context.isClosed + ); + + const internalMessages = await this._context.managementClient!.peek(maxMessageCount); + return internalMessages.map((m) => m as ReceivedMessage); + } + + private async _peekBySequenceNumber( + fromSequenceNumber: Long, + maxMessageCount?: number + ): Promise { + throwErrorIfClientOrConnectionClosed( + this._context.namespace, + this._context.entityPath, + this._context.isClosed + ); + + const internalMessages = await this._context.managementClient!.peekBySequenceNumber( + fromSequenceNumber, + maxMessageCount + ); + return internalMessages.map((m) => m as ReceivedMessage); + } + + subscribe(handlers: MessageHandlers, options?: SubscribeOptions): void { + assertValidMessageHandlers(handlers); + + this._registerMessageHandler( + async (message: ServiceBusMessage) => { + return handlers.processMessage(message, this.getContext()); + }, + (err: Error) => { + // TODO: not async internally yet. + handlers.processError(err); + }, + options + ); + } + + // #region topic-filters + + getRules(): Promise { + return getSubscriptionRules(this._context); + } + + removeRule(ruleName: string): Promise { + return removeSubscriptionRule(this._context, ruleName); + } + + addRule( + ruleName: string, + filter: boolean | string | CorrelationFilter, + sqlRuleActionExpression?: string + ): Promise { + return addSubscriptionRule(this._context, ruleName, filter, sqlRuleActionExpression); + } + + /** + * @readonly + * @property The name of the default rule on the subscription. + */ + readonly defaultRuleName: string = "$Default"; + + // #endregion + + /** + * Closes the underlying AMQP receiver link. + * Once closed, the receiver cannot be used for any further operations. + * Use the `createReceiver` function on the QueueClient or SubscriptionClient to instantiate + * a new Receiver + * + * @returns {Promise} + */ + async close(): Promise { + try { + this._isClosed = true; + if (this._context.namespace.connection && this._context.namespace.connection.isOpen()) { + // Close the streaming receiver. + if (this._context.streamingReceiver) { + await this._context.streamingReceiver.close(); + } + + // Close the batching receiver. + if (this._context.batchingReceiver) { + await this._context.batchingReceiver.close(); + } + + // Make sure that we clear the map of deferred messages + this._context.requestResponseLockedMessages.clear(); + } + } catch (err) { + log.error( + "[%s] An error occurred while closing the Receiver for %s: %O", + this._context.namespace.connectionId, + this._context.entityPath, + err + ); + throw err; + } + } + + /** + * Indicates whether the receiver is currently receiving messages or not. + * When this returns true, new `registerMessageHandler()` or `receiveMessages()` calls cannot be made. + */ + isReceivingMessages(): boolean { + if (this._context.streamingReceiver && this._context.streamingReceiver.isOpen()) { + return true; + } + if ( + this._context.batchingReceiver && + this._context.batchingReceiver.isOpen() && + this._context.batchingReceiver.isReceivingMessages + ) { + return true; + } + return false; + } + + private getContext(): ContextT { + return this.receiveMode === "peekLock" + ? ((settlementContext as any) as ContextT) + : ({} as ContextT); + } +} diff --git a/sdk/servicebus/service-bus/src/receivers/sessionReceiver.ts b/sdk/servicebus/service-bus/src/receivers/sessionReceiver.ts new file mode 100644 index 000000000000..419df9799295 --- /dev/null +++ b/sdk/servicebus/service-bus/src/receivers/sessionReceiver.ts @@ -0,0 +1,624 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +import { ClientEntityContext } from "../clientEntityContext"; +import { + SessionReceiverOptions, + ServiceBusMessage, + SessionMessageHandlerOptions, + RuleDescription, + CorrelationFilter, + ContextWithSettlement, + MessageHandlers, + SubscribeOptions, + ReceiveBatchOptions, + ReceivedMessage +} from ".."; + +import { GetMessageIteratorOptions } from "../models"; +import { MessageSession } from "../session/messageSession"; +import { + throwErrorIfConnectionClosed, + getOpenReceiverErrorMsg, + getReceiverClosedErrorMsg, + getAlreadyReceivingErrorMsg, + throwTypeErrorIfParameterMissing, + throwTypeErrorIfParameterNotLong, + throwTypeErrorIfParameterNotLongArray +} from "../util/errors"; +import * as log from "../log"; +import { OnMessage, OnError } from "../core/messageReceiver"; +import { + getSubscriptionRules, + removeSubscriptionRule, + addSubscriptionRule, + settlementContext, + assertValidMessageHandlers +} from "./shared"; +import { convertToInternalReceiveMode } from "../constructorHelpers"; +import { Receiver } from "./receiver"; +import Long from "long"; + +/** + *A receiver that handles sessions, including renewing the session lock. + */ +export interface SessionReceiver + extends Receiver { + /** + * The session ID. + * Can be undefined until a AMQP receiver link has been successfully set up for the session + */ + sessionId: string | undefined; + + /** + * Methods related to service bus diagnostics. + */ + diagnostics: { + /** + * Peek within a queue or subscription. + * @param maxMessageCount The maximum number of messages to retrieve. + */ + peek(maxMessageCount?: number): Promise; + /** + * Peek within a queue or subscription, starting with a specific sequence number. + * NOTE: this method does not respect message locks or increment delivery count + * for messages. + * @param fromSequenceNumber The sequence number to start peeking from (inclusive). + * @param maxMessageCount The maximum number of messages to retrieve. + */ + peekBySequenceNumber( + fromSequenceNumber: Long, + maxMessageCount?: number + ): Promise; + }; + + /** + * @property The time in UTC until which the session is locked. + * Everytime `renewSessionLock()` is called, this time gets updated to current time plus the lock + * duration as specified during the Queue/Subscription creation. + * + * Will return undefined until a AMQP receiver link has been successfully set up for the session. + * + * @readonly + * @memberof SessionReceiver + */ + sessionLockedUntilUtc: Date | undefined; + + /** + * Renews the lock on the session. + */ + renewSessionLock(): Promise; + + /** + * Gets the state of the Session. For more on session states, see + * {@link https://docs.microsoft.com/en-us/azure/service-bus-messaging/message-sessions#message-session-state Session State} + * @returns {Promise} The state of that session + * @throws Error if the underlying connection or receiver is closed. + * @throws MessagingError if the service returns an error while retrieving session state. + */ + getState(): Promise; + + /** + * Sets the state on the Session. For more on session states, see + * {@link https://docs.microsoft.com/en-us/azure/service-bus-messaging/message-sessions#message-session-state Session State} + * @param state The state that needs to be set. + * @throws Error if the underlying connection or receiver is closed. + * @throws MessagingError if the service returns an error while setting the session state. + * + * @param {*} state + * @returns {Promise} + */ + setState(state: any): Promise; +} + +export class SessionReceiverImpl + implements SessionReceiver { + public entityPath: string; + public sessionId: string | undefined; + + public diagnostics: { + peek(maxMessageCount?: number): Promise; + peekBySequenceNumber( + fromSequenceNumber: Long, + maxMessageCount?: number + ): Promise; + }; + + /** + * @property {ClientEntityContext} _context Describes the amqp connection context for the QueueClient. + */ + + private _context: ClientEntityContext; + private _messageSession: MessageSession | undefined; + /** + * @property {boolean} [_isClosed] Denotes if close() was called on this receiver + */ + private _isClosed: boolean = false; + + /** + * @internal + * @throws Error if the underlying connection is closed. + * @throws Error if an open receiver is already existing for given sessionId. + */ + constructor( + context: ClientEntityContext, + public receiveMode: "peekLock" | "receiveAndDelete", + public entityType: "queue" | "subscription", + private _sessionOptions: SessionReceiverOptions + ) { + throwErrorIfConnectionClosed(context.namespace); + this._context = context; + this.entityPath = this._context.entityPath; + + this.diagnostics = { + peek: (maxMessageCount) => this._peek(maxMessageCount), + peekBySequenceNumber: (fromSequenceNumber, maxMessageCount) => + this._peekBySequenceNumber(fromSequenceNumber, maxMessageCount) + }; + + if (this._sessionOptions.sessionId) { + this._sessionOptions.sessionId = String(this._sessionOptions.sessionId); + + // Check if receiver for given session already exists + if ( + this._context.messageSessions[this._sessionOptions.sessionId] && + this._context.messageSessions[this._sessionOptions.sessionId].isOpen() + ) { + const errorMessage = getOpenReceiverErrorMsg( + this._context.clientType, + this._context.entityPath, + this._sessionOptions.sessionId + ); + const error = new Error(errorMessage); + log.error(`[${this._context.namespace.connectionId}] %O`, error); + throw error; + } + } + } + + private _throwIfReceiverOrConnectionClosed(): void { + throwErrorIfConnectionClosed(this._context.namespace); + if (this.isClosed) { + const errorMessage = getReceiverClosedErrorMsg( + this._context.entityPath, + this._context.clientType, + this._context.isClosed, + this.sessionId! + ); + const error = new Error(errorMessage); + log.error(`[${this._context.namespace.connectionId}] %O`, error); + throw error; + } + } + + private async _createMessageSessionIfDoesntExist(): Promise { + if (this._messageSession) { + return; + } + this._context.isSessionEnabled = true; + this._messageSession = await MessageSession.create(this._context, { + sessionId: this._sessionOptions.sessionId, + maxSessionAutoRenewLockDurationInSeconds: this._sessionOptions + .maxSessionAutoRenewLockDurationInSeconds, + receiveMode: convertToInternalReceiveMode(this.receiveMode) + }); + // By this point, we should have a valid sessionId on the messageSession + // If not, the receiver cannot be used, so throw error. + if (this._messageSession.sessionId == null) { + const error = new Error("Something went wrong. Cannot lock a session."); + log.error(`[${this._context.namespace.connectionId}] %O`, error); + throw error; + } + this.sessionId = this._messageSession.sessionId; + delete this._context.expiredMessageSessions[this._messageSession.sessionId]; + } + + private _throwIfAlreadyReceiving(): void { + if (this.isReceivingMessages()) { + const errorMessage = getAlreadyReceivingErrorMsg(this._context.entityPath, this.sessionId); + const error = new Error(errorMessage); + log.error(`[${this._context.namespace.connectionId}] %O`, error); + throw error; + } + } + + getDeadLetterPath(): string { + return `${this.entityPath}/$DeadLetterQueue`; + } + + /** + * @property Returns `true` if the receiver is closed. This can happen either because the receiver + * itself has been closed or the client that created it has been closed. + * @readonly + */ + public get isClosed(): boolean { + return ( + this._isClosed || (this.sessionId ? !this._context.messageSessions[this.sessionId] : false) + ); + } + + /** + * @property The time in UTC until which the session is locked. + * Everytime `renewSessionLock()` is called, this time gets updated to current time plus the lock + * duration as specified during the Queue/Subscription creation. + * + * Will return undefined until a AMQP receiver link has been successfully set up for the session. + * + * @readonly + */ + public get sessionLockedUntilUtc(): Date | undefined { + return this._messageSession ? this._messageSession.sessionLockedUntilUtc : undefined; + } + + /** + * Renews the lock on the session for the duration as specified during the Queue/Subscription + * creation. + * - Check the `sessionLockedUntilUtc` property on the SessionReceiver for the time when the lock expires. + * - When the lock on the session expires + * - No more messages can be received using this receiver + * - If a message is not settled (using either `complete()`, `defer()` or `deadletter()`, + * before the session lock expires, then the message lands back in the Queue/Subscription for the next + * receive operation. + * + * @returns Promise - New lock token expiry date and time in UTC format. + * @throws Error if the underlying connection or receiver is closed. + * @throws MessagingError if the service returns an error while renewing session lock. + */ + async renewSessionLock(): Promise { + this._throwIfReceiverOrConnectionClosed(); + await this._createMessageSessionIfDoesntExist(); + + this._messageSession!.sessionLockedUntilUtc = await this._context.managementClient!.renewSessionLock( + this.sessionId! + ); + return this._messageSession!.sessionLockedUntilUtc!; + } + + /** + * Sets the state on the Session. For more on session states, see + * {@link https://docs.microsoft.com/en-us/azure/service-bus-messaging/message-sessions#message-session-state Session State} + * @param state The state that needs to be set. + * @throws Error if the underlying connection or receiver is closed. + * @throws MessagingError if the service returns an error while setting the session state. + */ + async setState(state: any): Promise { + this._throwIfReceiverOrConnectionClosed(); + await this._createMessageSessionIfDoesntExist(); + return this._context.managementClient!.setSessionState(this.sessionId!, state); + } + + /** + * Gets the state of the Session. For more on session states, see + * {@link https://docs.microsoft.com/en-us/azure/service-bus-messaging/message-sessions#message-session-state Session State} + * @returns Promise The state of that session + * @throws Error if the underlying connection or receiver is closed. + * @throws MessagingError if the service returns an error while retrieving session state. + */ + async getState(): Promise { + this._throwIfReceiverOrConnectionClosed(); + await this._createMessageSessionIfDoesntExist(); + return this._context.managementClient!.getSessionState(this.sessionId!); + } + + /** + * Fetches the next batch of active messages (including deferred but not deadlettered messages) in + * the current session. + * - The first call to `peek()` fetches the first active message. Each subsequent call fetches the + * subsequent message. + * - Unlike a `received` message, `peeked` message is a read-only version of the message. + * It cannot be `Completed/Abandoned/Deferred/Deadlettered`. The lock on it cannot be renewed. + * + * @param maxMessageCount The maximum number of messages to peek. Default value `1`. + * @returns Promise + * @throws Error if the underlying connection or receiver is closed. + * @throws MessagingError if the service returns an error while peeking for messages. + */ + private async _peek(maxMessageCount?: number): Promise { + this._throwIfReceiverOrConnectionClosed(); + await this._createMessageSessionIfDoesntExist(); + const internalMessages = await this._context.managementClient!.peekMessagesBySession( + this.sessionId!, + maxMessageCount + ); + return internalMessages.map((m) => m as ReceivedMessage); + } + + /** + * Peeks the desired number of active messages (including deferred but not deadlettered messages) + * from the specified sequence number in the current session. + * - Unlike a `received` message, `peeked` message is a read-only version of the message. + * It cannot be `Completed/Abandoned/Deferred/Deadlettered`. The lock on it cannot be renewed. + * + * @param fromSequenceNumber The sequence number from where to read the message. + * @param [maxMessageCount] The maximum number of messages to peek. Default value `1`. + * @returns Promise + * @throws Error if the underlying connection or receiver is closed. + * @throws MessagingError if the service returns an error while peeking for messages. + */ + private async _peekBySequenceNumber( + fromSequenceNumber: Long, + maxMessageCount?: number + ): Promise { + this._throwIfReceiverOrConnectionClosed(); + await this._createMessageSessionIfDoesntExist(); + const internalMessages = await this._context.managementClient!.peekBySequenceNumber( + fromSequenceNumber, + maxMessageCount, + this.sessionId + ); + + return internalMessages.map((m) => m as ReceivedMessage); + } + + /** + * Returns a promise that resolves to a deferred message identified by the given `sequenceNumber`. + * @param sequenceNumber The sequence number of the message that needs to be received. + * @returns Promise + * - Returns `Message` identified by sequence number. + * - Returns `undefined` if no such message is found. + * @throws Error if the underlying connection or receiver is closed. + * @throws MessagingError if the service returns an error while receiving deferred message. + */ + async receiveDeferredMessage(sequenceNumber: Long): Promise { + this._throwIfReceiverOrConnectionClosed(); + throwTypeErrorIfParameterMissing( + this._context.namespace.connectionId, + "sequenceNumber", + sequenceNumber + ); + throwTypeErrorIfParameterNotLong( + this._context.namespace.connectionId, + "sequenceNumber", + sequenceNumber + ); + + await this._createMessageSessionIfDoesntExist(); + const messages = await this._context.managementClient!.receiveDeferredMessages( + [sequenceNumber], + convertToInternalReceiveMode(this.receiveMode), + this.sessionId + ); + return messages[0]; + } + + /** + * Returns a promise that resolves to an array of deferred messages identified by given `sequenceNumbers`. + * @param sequenceNumbers An array of sequence numbers for the messages that need to be received. + * @returns Promise + * - Returns a list of messages identified by the given sequenceNumbers. + * - Returns an empty list if no messages are found. + * @throws Error if the underlying connection or receiver is closed. + * @throws MessagingError if the service returns an error while receiving deferred messages. + */ + async receiveDeferredMessages(sequenceNumbers: Long[]): Promise { + this._throwIfReceiverOrConnectionClosed(); + throwTypeErrorIfParameterMissing( + this._context.namespace.connectionId, + "sequenceNumbers", + sequenceNumbers + ); + if (!Array.isArray(sequenceNumbers)) { + sequenceNumbers = [sequenceNumbers]; + } + throwTypeErrorIfParameterNotLongArray( + this._context.namespace.connectionId, + "sequenceNumbers", + sequenceNumbers + ); + + await this._createMessageSessionIfDoesntExist(); + return this._context.managementClient!.receiveDeferredMessages( + sequenceNumbers, + convertToInternalReceiveMode(this.receiveMode), + this.sessionId + ); + } + + /** + * Returns a promise that resolves to an array of messages based on given count and timeout over + * an AMQP receiver link from a Queue/Subscription. + * + * Throws an error if there is another receive operation in progress on the same receiver. If you + * are not sure whether there is another receive operation running, check the `isReceivingMessages` + * property on the receiver. + * + * @param maxMessageCount The maximum number of messages to receive from Queue/Subscription. + * @param maxWaitTimeInSeconds The total wait time in seconds until which the receiver will attempt to receive specified number of messages. + * If this time elapses before the `maxMessageCount` is reached, then messages collected till then will be returned to the user. + * - **Default**: `60` seconds. + * @returns Promise A promise that resolves with an array of Message objects. + * @throws Error if the underlying connection or receiver is closed. + * @throws Error if the receiver is already in state of receiving messages. + * @throws MessagingError if the service returns an error while receiving messages. + */ + async receiveBatch( + maxMessageCount: number, + maxWaitTimeInSeconds?: number, + // TODO: use the options + options?: ReceiveBatchOptions + ): Promise<{ messages: ReceivedMessage[]; context: ContextT }> { + this._throwIfReceiverOrConnectionClosed(); + this._throwIfAlreadyReceiving(); + await this._createMessageSessionIfDoesntExist(); + const messages = await this._messageSession!.receiveMessages( + maxMessageCount, + maxWaitTimeInSeconds + ); + + return { + messages, + context: this.getContext() + }; + } + + subscribe(handlers: MessageHandlers, options?: SubscribeOptions): void { + assertValidMessageHandlers(handlers); + + this._registerMessageHandler( + async (message: ServiceBusMessage) => { + return handlers.processMessage(message, this.getContext()); + }, + (err: Error) => { + // TODO: not async internally yet. + handlers.processError(err); + }, + options + ); + } + + /** + * Registers handlers to deal with the incoming stream of messages over an AMQP receiver link + * from a Queue/Subscription. + * To stop receiving messages, call `close()` on the SessionReceiver. + * + * Throws an error if there is another receive operation in progress on the same receiver. If you + * are not sure whether there is another receive operation running, check the `isReceivingMessages` + * property on the receiver. + * + * @param onMessage - Handler for processing each incoming message. + * @param onError - Handler for any error that occurs while receiving or processing messages. + * @param options - Options to control whether messages should be automatically completed + * or if the lock on the session should be automatically renewed. You can control the + * maximum number of messages that should be concurrently processed. You can + * also provide a timeout in seconds to denote the amount of time to wait for a new message + * before closing the receiver. + * + * @returns void + * @throws Error if the underlying connection or receiver is closed. + * @throws Error if the receiver is already in state of receiving messages. + * @throws MessagingErrormif the service returns an error while receiving messages. These are bubbled up to be handled by user provided `onError` handler. + */ + private _registerMessageHandler( + onMessage: OnMessage, + onError: OnError, + options?: SessionMessageHandlerOptions + ): void { + this._throwIfReceiverOrConnectionClosed(); + this._throwIfAlreadyReceiving(); + const connId = this._context.namespace.connectionId; + throwTypeErrorIfParameterMissing(connId, "onMessage", onMessage); + throwTypeErrorIfParameterMissing(connId, "onError", onError); + if (typeof onMessage !== "function") { + throw new TypeError("The parameter 'onMessage' must be of type 'function'."); + } + if (typeof onError !== "function") { + throw new TypeError("The parameter 'onError' must be of type 'function'."); + } + + this._createMessageSessionIfDoesntExist() + .then(async () => { + if (!this._messageSession) { + return; + } + if (!this._isClosed) { + this._messageSession.receive(onMessage, onError, options); + } else { + await this._messageSession.close(); + } + return; + }) + .catch((err) => { + onError(err); + }); + } + + /** + * Gets an async iterator over messages from the receiver. + * + * Throws an error if there is another receive operation in progress on the same receiver. If you + * are not sure whether there is another receive operation running, check the `isReceivingMessages` + * property on the receiver. + * + * If the iterator is not able to fetch a new message in over a minute, `undefined` will be returned. + * @throws Error if the underlying connection, client or receiver is closed. + * @throws Error if current receiver is already in state of receiving messages. + * @throws MessagingError if the service returns an error while receiving messages. + */ + async *getMessageIterator( + options?: GetMessageIteratorOptions + ): AsyncIterableIterator<{ + message: ReceivedMessage; + context: ContextT; + }> { + while (true) { + const { messages, context } = await this.receiveBatch(1); + + yield { + message: messages[0], + context + }; + } + } + + // #region topic-filters + + getRules(): Promise { + return getSubscriptionRules(this._context); + } + + removeRule(ruleName: string): Promise { + return removeSubscriptionRule(this._context, ruleName); + } + + addRule( + ruleName: string, + filter: boolean | string | CorrelationFilter, + sqlRuleActionExpression?: string + ): Promise { + return addSubscriptionRule(this._context, ruleName, filter, sqlRuleActionExpression); + } + + /** + * @readonly + * @property The name of the default rule on the subscription. + */ + readonly defaultRuleName: string = "$Default"; + + // #endregion + + /** + * Closes the underlying AMQP receiver link. + * Once closed, the receiver cannot be used for any further operations. + * Use the `createReceiver` function on the QueueClient or SubscriptionClient to instantiate + * a new Receiver + * + * @returns {Promise} + */ + async close(): Promise { + try { + if (this._messageSession) { + await this._messageSession.close(); + this._messageSession = undefined; + } + } catch (err) { + log.error( + "[%s] An error occurred while closing the SessionReceiver for session %s in %s: %O", + this._context.namespace.connectionId, + this.sessionId, + this._context.entityPath, + err + ); + throw err; + } finally { + this._isClosed = true; + } + } + + /** + * Indicates whether the receiver is currently receiving messages or not. + * When this returns true, new `registerMessageHandler()` or `receiveMessages()` calls cannot be made. + */ + isReceivingMessages(): boolean { + return this._messageSession ? this._messageSession.isReceivingMessages : false; + } + + private getContext(): ContextT { + return this.receiveMode === "peekLock" + ? ((settlementContext as any) as ContextT) + : ({} as ContextT); + } + + async renewMessageLock(lockTokenOrMessage: string | ReceivedMessage): Promise { + throw new Error("Move to the context"); + } +} diff --git a/sdk/servicebus/service-bus/src/receivers/shared.ts b/sdk/servicebus/service-bus/src/receivers/shared.ts new file mode 100644 index 000000000000..e389f0885f11 --- /dev/null +++ b/sdk/servicebus/service-bus/src/receivers/shared.ts @@ -0,0 +1,88 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +import { ContextWithSettlement } from "../models"; +import { ServiceBusMessage } from "../serviceBusMessage"; +import { throwErrorIfClientOrConnectionClosed } from "../util/errors"; +import { ClientEntityContext } from "../clientEntityContext"; +import { RuleDescription, CorrelationFilter } from "../core/managementClient"; + +/** + * @internal + * @ignore + */ +export function getSubscriptionRules(context: ClientEntityContext): Promise { + if (context.entityPath.includes("/Subscriptions/")) { + throwErrorIfClientOrConnectionClosed(context.namespace, context.entityPath, context.isClosed); + return context.managementClient!.getRules(); + } else { + throw new Error("Only for a subscription"); + } +} + +/** + * @internal + * @ignore + */ +export function removeSubscriptionRule( + context: ClientEntityContext, + ruleName: string +): Promise { + if (context.entityPath.includes("/Subscriptions/")) { + throwErrorIfClientOrConnectionClosed(context.namespace, context.entityPath, context.isClosed); + return context.managementClient!.removeRule(ruleName); + } else { + throw new Error("Only for a subscription"); + } +} + +/** + * @internal + * @ignore + */ +export function addSubscriptionRule( + context: ClientEntityContext, + ruleName: string, + filter: boolean | string | CorrelationFilter, + sqlRuleActionExpression?: string +): Promise { + if (context.entityPath.includes("/Subscriptions/")) { + throwErrorIfClientOrConnectionClosed(context.namespace, context.entityPath, context.isClosed); + return context.managementClient!.addRule(ruleName, filter, sqlRuleActionExpression); + } else { + throw new Error("Only for a subscription"); + } +} + +// #endregion + +/** + * @internal + * @ignore + */ +export const settlementContext: ContextWithSettlement = { + // TODO: need to move the settlement methods out of sb message - + // we don't need to have this runtime dependency. + abandon: (message, propertiesToModify) => + ((message as unknown) as ServiceBusMessage).abandon(propertiesToModify), + complete: (message) => ((message as unknown) as ServiceBusMessage).complete(), + defer: (message, propertiesToModify) => + ((message as unknown) as ServiceBusMessage).defer(propertiesToModify), + deadLetter: (message, options) => ((message as unknown) as ServiceBusMessage).deadLetter(options) +}; + +/** + * @internal + * @ignore + */ +export function assertValidMessageHandlers(handlers: any) { + if ( + handlers && + handlers.processMessage instanceof Function && + handlers.processError instanceof Function + ) { + return; + } + + throw new TypeError('Invalid "MessageHandlers" provided.'); +} diff --git a/sdk/servicebus/service-bus/src/serviceBusClient.ts b/sdk/servicebus/service-bus/src/serviceBusClient.ts new file mode 100644 index 000000000000..8e88c84a7875 --- /dev/null +++ b/sdk/servicebus/service-bus/src/serviceBusClient.ts @@ -0,0 +1,269 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +import { generate_uuid } from "rhea-promise"; +import { isTokenCredential, TokenCredential } from "@azure/core-auth"; +import { + ServiceBusClientOptions, + createConnectionContextForTokenCredential, + createConnectionContextForConnectionString +} from "./constructorHelpers"; +import { ConnectionContext } from "./connectionContext"; +import { ClientEntityContext } from "./clientEntityContext"; +import { ClientType } from "./client"; +import { Sender } from "./sender"; +import { GetSessionReceiverOptions, ContextWithSettlement } from "./models"; +import { Receiver, ReceiverImpl, SubscriptionRuleManagement } from "./receivers/receiver"; +import { SessionReceiver, SessionReceiverImpl } from "./receivers/sessionReceiver"; + +export class ServiceBusClient { + private _connectionContext: ConnectionContext; + + /** + * + * @param connectionString A connection string for Azure Service Bus. + * NOTE: this connection string can contain an EntityPath, which is ignored. + * @param options Options for the service bus client. + */ + constructor(connectionString: string, options?: ServiceBusClientOptions); + /** + * + * @param host The hostname of your Azure Service Bus. + * @param tokenCredential A valid TokenCredential for Service Bus or a + * Service Bus entity. + * @param options Options for the service bus client. + */ + constructor( + hostName: string, + tokenCredential: TokenCredential, + options?: ServiceBusClientOptions + ); + constructor( + connectionStringOrHostName1: string, + tokenCredentialOrServiceBusOptions2?: TokenCredential | ServiceBusClientOptions, + options3?: ServiceBusClientOptions + ) { + if (isTokenCredential(tokenCredentialOrServiceBusOptions2)) { + const hostName: string = connectionStringOrHostName1; + const tokenCredential: TokenCredential = tokenCredentialOrServiceBusOptions2; + const options: ServiceBusClientOptions | undefined = options3; + + this._connectionContext = createConnectionContextForTokenCredential( + tokenCredential, + hostName, + options + ); + } else { + const connectionString: string = connectionStringOrHostName1; + const options: ServiceBusClientOptions | undefined = tokenCredentialOrServiceBusOptions2; + + this._connectionContext = createConnectionContextForConnectionString( + connectionString, + options + ); + } + } + + /** + * Creates a receiver for an Azure Service Bus queue. + * + * @param queueName The name of the queue to receive from. + * @param receiveMode The receive mode to use (defaults to PeekLock) + * @param options Options for the receiver itself. + */ + getReceiver(queueName: string, receiveMode: "peekLock"): Receiver; + /** + * Creates a receiver for an Azure Service Bus queue. + * + * @param queueName The name of the queue to receive from. + * @param receiveMode The receive mode to use (defaults to PeekLock) + * @param options Options for the receiver itself. + */ + getReceiver(queueName: string, receiveMode: "receiveAndDelete"): Receiver<{}>; + /** + * Creates a receiver for an Azure Service Bus subscription. + * + * @param topicName Name of the topic for the subscription we want to receive from. + * @param subscriptionName Name of the subscription (under the `topic`) that we want to receive from. + * @param receiveMode The receive mode to use (defaults to PeekLock) + * @param options Options for the receiver itself. + */ + getReceiver( + topicName: string, + subscriptionName: string, + receiveMode: "peekLock" + ): Receiver & SubscriptionRuleManagement; + /** + * Creates a receiver for an Azure Service Bus subscription. + * + * @param topicName Name of the topic for the subscription we want to receive from. + * @param subscriptionName Name of the subscription (under the `topic`) that we want to receive from. + * @param receiveMode The receive mode to use (defaults to PeekLock) + * @param options Options for the receiver itself. + */ + getReceiver( + topicName: string, + subscriptionName: string, + receiveMode: "receiveAndDelete" + ): Receiver<{}> & SubscriptionRuleManagement; + getReceiver( + queueOrTopicName1: string, + receiveModeOrSubscriptionName2: "peekLock" | "receiveAndDelete" | string, + receiveMode3?: "peekLock" | "receiveAndDelete" + ): + | Receiver + | Receiver<{}> + | (Receiver & SubscriptionRuleManagement) + | (Receiver<{}> & SubscriptionRuleManagement) { + let entityPath: string; + let receiveMode: "peekLock" | "receiveAndDelete"; + let entityType: "queue" | "subscription"; + + if (isReceiveMode(receiveMode3)) { + entityType = "subscription"; + const topic = queueOrTopicName1; + const subscription = receiveModeOrSubscriptionName2; + entityPath = `${topic}/Subscriptions/${subscription}`; + receiveMode = receiveMode3; + } else if (isReceiveMode(receiveModeOrSubscriptionName2)) { + entityType = "queue"; + entityPath = queueOrTopicName1; + receiveMode = receiveModeOrSubscriptionName2; + } else { + throw new TypeError("Invalid receiveMode provided"); + } + + const clientEntityContext = ClientEntityContext.create( + entityPath, + ClientType.ServiceBusReceiverClient, + this._connectionContext, + `${entityPath}/${generate_uuid()}` + ); + + if (receiveMode === "peekLock") { + return new ReceiverImpl(clientEntityContext, receiveMode, entityType); + } else { + return new ReceiverImpl<{}>(clientEntityContext, receiveMode, entityType); + } + } + + /** + * Creates a receiver for an Azure Service Bus queue. + * + * @param queueName The name of the queue to receive from. + * @param receiveMode The receive mode to use (defaults to PeekLock) + * @param options Options for the receiver itself. + */ + getSessionReceiver( + queueName: string, + receiveMode: "peekLock", + options?: GetSessionReceiverOptions + ): SessionReceiver; + /** + * Creates a receiver for an Azure Service Bus queue. + * + * @param queueName The name of the queue to receive from. + * @param receiveMode The receive mode to use (defaults to PeekLock) + * @param options Options for the receiver itself. + */ + getSessionReceiver( + queueName: string, + receiveMode: "receiveAndDelete", + options?: GetSessionReceiverOptions + ): SessionReceiver<{}>; + /** + * Creates a receiver for an Azure Service Bus subscription. + * + * @param topicName Name of the topic for the subscription we want to receive from. + * @param subscriptionName Name of the subscription (under the `topic`) that we want to receive from. + * @param receiveMode The receive mode to use (defaults to PeekLock) + * @param options Options for the receiver itself. + */ + getSessionReceiver( + topicName: string, + subscriptionName: string, + receiveMode: "peekLock", + options?: GetSessionReceiverOptions + ): SessionReceiver & SubscriptionRuleManagement; + /** + * Creates a receiver for an Azure Service Bus subscription. + * + * @param topicName Name of the topic for the subscription we want to receive from. + * @param subscriptionName Name of the subscription (under the `topic`) that we want to receive from. + * @param receiveMode The receive mode to use (defaults to PeekLock) + * @param options Options for the receiver itself. + */ + getSessionReceiver( + topicName: string, + subscriptionName: string, + receiveMode: "receiveAndDelete", + options?: GetSessionReceiverOptions + ): SessionReceiver<"receiveAndDelete"> & SubscriptionRuleManagement; + getSessionReceiver( + queueOrTopicName1: string, + receiveModeOrSubscriptionName2: "peekLock" | "receiveAndDelete" | string, + receiveModeOrOptions3?: "peekLock" | "receiveAndDelete" | GetSessionReceiverOptions, + options4?: GetSessionReceiverOptions + ): + | SessionReceiver + | SessionReceiver<{}> + | (SessionReceiver<{}> & SubscriptionRuleManagement) + | (SessionReceiver & SubscriptionRuleManagement) { + let entityPath: string; + let receiveMode: "peekLock" | "receiveAndDelete"; + let entityType: "queue" | "subscription"; + let options: GetSessionReceiverOptions | undefined; + + if (isReceiveMode(receiveModeOrOptions3)) { + entityType = "subscription"; + const topic = queueOrTopicName1; + const subscription = receiveModeOrSubscriptionName2; + entityPath = `${topic}/Subscriptions/${subscription}`; + receiveMode = receiveModeOrOptions3; + options = options4; + } else if (isReceiveMode(receiveModeOrSubscriptionName2)) { + entityType = "queue"; + entityPath = queueOrTopicName1; + receiveMode = receiveModeOrSubscriptionName2; + options = receiveModeOrOptions3 as GetSessionReceiverOptions | undefined; + } else { + throw new TypeError("Invalid receiveMode provided"); + } + + const clientEntityContext = ClientEntityContext.create( + entityPath, + ClientType.ServiceBusReceiverClient, + this._connectionContext, + `${entityPath}/${generate_uuid()}` + ); + + // TODO: .NET actually tries to open the session here so we'd need to be async for that. + return new SessionReceiverImpl(clientEntityContext, receiveMode, entityType, { + sessionId: options?.sessionId, + maxSessionAutoRenewLockDurationInSeconds: options?.maxSessionAutoRenewLockDurationInSeconds + }); + } + + /** + * Creates a Sender which can be used to send messages, schedule messages to be sent at a later time + * and cancel such scheduled messages. + */ + getSender(queueOrTopicName: string): Sender { + const clientEntityContext = ClientEntityContext.create( + queueOrTopicName, + ClientType.ServiceBusReceiverClient, + this._connectionContext, + `${queueOrTopicName}/${generate_uuid()}` + ); + + return new Sender(clientEntityContext); + } + + close(): Promise { + return ConnectionContext.close(this._connectionContext); + } +} + +function isReceiveMode(mode: any): mode is "peekLock" | "receiveAndDelete" { + return mode && typeof mode === "string" && (mode === "peekLock" || mode === "receiveAndDelete"); +} diff --git a/sdk/servicebus/service-bus/src/serviceBusReceiverClient.ts b/sdk/servicebus/service-bus/src/serviceBusReceiverClient.ts deleted file mode 100644 index a694ffb269ad..000000000000 --- a/sdk/servicebus/service-bus/src/serviceBusReceiverClient.ts +++ /dev/null @@ -1,897 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -import Long from "long"; -import { ServiceBusMessage, ReceiveMode } from "./serviceBusMessage"; -import { ClientEntityContext } from "./clientEntityContext"; -import { generate_uuid } from "rhea-promise"; -import { ClientType } from "./client"; -import { InternalSessionReceiver, InternalReceiver } from "./internalReceivers"; -import { - MessageHandlers, - MessageIterator, - Session, - ContextType, - ContextWithSettlement, - QueueAuth, - SubscriptionAuth, - isSession, - MessageAndContext, - ReceivedMessage, - ReceiveBatchOptions, - IterateMessagesOptions, - SubscribeOptions, - isQueueAuth -} from "./models"; -import { - createConnectionContext, - convertToInternalReceiveMode, - ServiceBusClientOptions -} from "./constructorHelpers"; -import { RuleDescription, CorrelationFilter } from "./core/managementClient"; -import { ConnectionContext } from "./connectionContext"; - -/** - *A receiver client that handles sessions, including renewing the session lock. - * @export - * @interface SessionReceiver - * @template LockModeT - */ -export interface SessionReceiver { - /** - * Streams messages to message handlers. - * - * @param {MessageHandlers>} handlers A handler that gets called for messages and errors. - * @param {SubscribeOptions} [options] Options for subscribe. - * @memberof SessionReceiver - */ - subscribe(handlers: MessageHandlers>, options?: SubscribeOptions): void; - /** - * Returns an iterator that can be used to receive messages from Service Bus. - * @param {IterateMessagesOptions} [options] Options for iterateMessages. - * @returns {MessageIterator>} - * @memberof SessionReceiver - */ - iterateMessages(options?: IterateMessagesOptions): MessageIterator>; - /** - * Receives, at most, `maxMessages` worth of messages. - * @param {number} maxMessages The maximum number of messages to accept. - * @param {number} [maxWaitTimeInSeconds] The maximum time to wait, in seconds, for messages to arrive. - * @param {ReceiveBatchOptions} [options] Options for receiveBatch. - * @returns {Promise<{ messages: ReceivedMessage[]; context: ContextType }>} Message batch object with an array of `ReceivedMessage`s along with the context to settle the messages in "peekLock" mode, context would be empty in "receiveAndDelete" mode - * @memberof SessionReceiver - */ - receiveBatch( - maxMessages: number, - maxWaitTimeInSeconds?: number, - options?: ReceiveBatchOptions - ): Promise<{ messages: ReceivedMessage[]; context: ContextType }>; - /** - * Returns a promise that resolves to a deferred message identified by the given `sequenceNumber`. - * @param {Long} sequenceNumber The sequence number of the message that needs to be received. - * @returns {(Promise)} - * - Returns `Message` identified by sequence number. - * - Returns `undefined` if no such message is found. - * @throws Error if the underlying connection or receiver is closed. - * @throws MessagingError if the service returns an error while receiving deferred message. - * @memberof SessionReceiver - */ - receiveDeferredMessage(sequenceNumber: Long): Promise; - /** - * Returns a promise that resolves to an array of deferred messages identified by given `sequenceNumbers`. - * @param {Long[]} sequenceNumbers An array of sequence numbers for the messages that need to be received. - * @returns {Promise} - * - Returns a list of messages identified by the given sequenceNumbers. - * - Returns an empty list if no messages are found. - * @throws Error if the underlying connection or receiver is closed. - * @throws MessagingError if the service returns an error while receiving deferred messages. - * @memberof SessionReceiver - */ - receiveDeferredMessages(sequenceNumbers: Long[]): Promise; - /** - * Indicates whether the receiver is currently receiving messages or not. - * When this returns true, new `registerMessageHandler()` or `receiveMessages()` calls cannot be made. - * @returns {boolean} - * @memberof SessionReceiver - */ - isReceivingMessages(): boolean; - /** - * Renews the lock on the session for the duration as specified during the Queue/Subscription - * creation. - * - Check the `sessionLockedUntilUtc` property on the SessionReceiver for the time when the lock expires. - * - When the lock on the session expires - * - No more messages can be received using this receiver - * - If a message is not settled (using either `complete()`, `defer()` or `deadletter()`, - * before the session lock expires, then the message lands back in the Queue/Subscription for the next - * receive operation. - * - * @returns {Promise} - New lock token expiry date and time in UTC format. - * @throws Error if the underlying connection or receiver is closed. - * @throws MessagingError if the service returns an error while renewing session lock. - * @memberof SessionReceiver - */ - renewSessionLock(): Promise; - /** - * Sets the state on the Session. For more on session states, see - * {@link https://docs.microsoft.com/en-us/azure/service-bus-messaging/message-sessions#message-session-state Session State} - * @param state The state that needs to be set. - * @throws Error if the underlying connection or receiver is closed. - * @throws MessagingError if the service returns an error while setting the session state. - * - * @param {*} state - * @returns {Promise} - * @memberof SessionReceiver - */ - setState(state: any): Promise; - /** - * Gets the state of the Session. For more on session states, see - * {@link https://docs.microsoft.com/en-us/azure/service-bus-messaging/message-sessions#message-session-state Session State} - * @returns {Promise} The state of that session - * @throws Error if the underlying connection or receiver is closed. - * @throws MessagingError if the service returns an error while retrieving session state. - * @memberof SessionReceiver - */ - getState(): Promise; - /** - * @property The id of the session from which this receiver will receive messages. - * Will return undefined until a AMQP receiver link has been successfully set up for the session. - * @readonly - * @type {(string | undefined)} - * @memberof SessionReceiver - */ - sessionId: string | undefined; - /** - * @property The time in UTC until which the session is locked. - * Everytime `renewSessionLock()` is called, this time gets updated to current time plus the lock - * duration as specified during the Queue/Subscription creation. - * - * Will return undefined until a AMQP receiver link has been successfully set up for the session. - * - * @readonly - * @memberof SessionReceiver - */ - sessionLockedUntilUtc: Date | undefined; - /** - * Closes the client. - * - * @returns {Promise} - * @memberof SessionReceiver - */ - close(): Promise; - /** - * Returns the corresponding dead letter queue path for the client entity - meant for both queue and subscription. - * @returns {string} - * @memberof SessionReceiver - */ - getDeadLetterPath(): string; - /** - * Methods related to service bus diagnostics. - */ - diagnostics: { - /** - * Peek within a queue or subscription. - * @param maxMessageCount The maximum number of messages to retrieve. - */ - peek(maxMessageCount?: number): Promise; - /** - * Peek within a queue or subscription, starting with a specific sequence number. - * NOTE: this method does not respect message locks or increment delivery count - * for messages. - * @param fromSequenceNumber The sequence number to start peeking from (inclusive). - * @param maxMessageCount The maximum number of messages to retrieve. - */ - peekBySequenceNumber( - fromSequenceNumber: Long, - maxMessageCount?: number - ): Promise; - }; - /** - * Type of the entity with which the client is created. - * - * @type {("queue" | "subscription")} - * @memberof SessionReceiver - */ - entityType: "queue" | "subscription"; - /** - * Path for the client entity. - * - * @type {string} - * @memberof SessionReceiver - */ - entityPath: string; - /** - * ReceiveMode provided to the client. - * - * @type {("peekLock" | "receiveAndDelete")} - * @memberof SessionReceiver - */ - receiveMode: "peekLock" | "receiveAndDelete"; -} - -/** - * A receiver client that does not handle sessions. - * - * @export - * @interface NonSessionReceiver - * @template LockModeT - */ -export interface NonSessionReceiver { - /** - * Streams messages to message handlers. - * - * @param {MessageHandlers>} handlers A handler that gets called for messages and errors. - * @param {SubscribeOptions} [options] Options for subscribe. - * @memberof NonSessionReceiver - */ - subscribe(handler: MessageHandlers>, options?: SubscribeOptions): void; - /** - * Returns an iterator that can be used to receive messages from Service Bus. - * @param {IterateMessagesOptions} [options] Options for iterateMessages. - * @returns {MessageIterator>} - * @memberof NonSessionReceiver - */ - iterateMessages(options?: IterateMessagesOptions): MessageIterator>; - /** - * Receives, at most, `maxMessages` worth of messages. - * @param {number} maxMessages The maximum number of messages to accept. - * @param {number} [maxWaitTimeInSeconds] The maximum time to wait, in seconds, for messages to arrive. - * @param {ReceiveBatchOptions} [options] Options for receiveBatch. - * @returns {Promise<{ messages: ReceivedMessage[]; context: ContextType }>} Message batch object with an array of `ReceivedMessage`s along with the context to settle the messages in "peekLock" mode, context would be empty in "receiveAndDelete" mode - * @memberof NonSessionReceiver - */ - receiveBatch( - maxMessages: number, - maxWaitTimeInSeconds?: number, - options?: ReceiveBatchOptions - ): Promise<{ messages: ReceivedMessage[]; context: ContextType }>; - /** - * Renews the lock on the message for the duration as specified during the Queue/Subscription - * creation. - * - Check the `lockedUntilUtc` property on the message for the time when the lock expires. - * - If a message is not settled (using either `complete()`, `defer()` or `deadletter()`, - * before its lock expires, then the message lands back in the Queue/Subscription for the next - * receive operation. - * - * @param {(string | ReceivedMessage)} lockTokenOrMessage - The `lockToken` property of the message or the message itself. - * @returns {Promise} - New lock token expiry date and time in UTC format. - * @throws Error if the underlying connection, client or receiver is closed. - * @throws MessagingError if the service returns an error while renewing message lock. - * @memberof NonSessionReceiver - */ - renewMessageLock(lockTokenOrMessage: string | ReceivedMessage): Promise; - /** - * Returns a promise that resolves to a deferred message identified by the given `sequenceNumber`. - * @param {Long} sequenceNumber The sequence number of the message that needs to be received. - * @returns {(Promise)} - * - Returns `Message` identified by sequence number. - * - Returns `undefined` if no such message is found. - * @throws Error if the underlying connection or receiver is closed. - * @throws MessagingError if the service returns an error while receiving deferred message. - * @memberof NonSessionReceiver - */ - receiveDeferredMessage(sequenceNumber: Long): Promise; - /** - * Returns a promise that resolves to an array of deferred messages identified by given `sequenceNumbers`. - * @param {Long[]} sequenceNumbers An array of sequence numbers for the messages that need to be received. - * @returns {Promise} - * - Returns a list of messages identified by the given sequenceNumbers. - * - Returns an empty list if no messages are found. - * @throws Error if the underlying connection or receiver is closed. - * @throws MessagingError if the service returns an error while receiving deferred messages. - * @memberof NonSessionReceiver - */ - receiveDeferredMessages(sequenceNumbers: Long[]): Promise; - /** - * Indicates whether the receiver is currently receiving messages or not. - * When this returns true, new `registerMessageHandler()` or `receiveMessages()` calls cannot be made. - * @returns {boolean} - * @memberof NonSessionReceiver - */ - isReceivingMessages(): boolean; - /** - * Closes the client. - * - * @returns {Promise} - * @memberof NonSessionReceiver - */ - close(): Promise; - /** - * Returns the corresponding dead letter queue path for the client entity - meant for both queue and subscription. - * @returns {string} - * @memberof NonSessionReceiver - */ - getDeadLetterPath(): string; - - /** - * Methods related to service bus diagnostics. - */ - diagnostics: { - /** - * Peek within a queue or subscription. - * NOTE: this method does not respect message locks or increment delivery count - * for messages. - * @param maxMessageCount The maximum number of messages to retrieve. - */ - peek(maxMessageCount?: number): Promise; - - /** - * Peek within a queue or subscription, starting with a specific sequence number. - * NOTE: this method does not respect message locks or increment delivery count - * for messages. - * @param fromSequenceNumber The sequence number to start peeking from (inclusive). - * @param maxMessageCount The maximum number of messages to retrieve. - */ - peekBySequenceNumber( - fromSequenceNumber: Long, - maxMessageCount?: number - ): Promise; - }; - /** - * Type of the entity with which the client is created. - * - * @type {("queue" | "subscription")} - * @memberof NonSessionReceiver - */ - entityType: "queue" | "subscription"; - /** - * Path for the client entity. - * - * @type {string} - * @memberof NonSessionReceiver - */ - entityPath: string; - /** - * ReceiveMode provided to the client. - * - * @type {("peekLock" | "receiveAndDelete")} - * @memberof NonSessionReceiver - */ - receiveMode: "peekLock" | "receiveAndDelete"; -} - -/** - * Methods to manage rules for subscriptions. More information about subscription rules - * can be found here: https://docs.microsoft.com/en-us/azure/service-bus-messaging/topic-filters - */ -export interface SubscriptionRuleManagement { - /** - * Gets all rules associated with the subscription - * @throws Error if the SubscriptionClient or the underlying connection is closed. - * @throws MessagingError if the service returns an error while retrieving rules. - */ - getRules(): Promise; - - /** - * Removes the rule on the subscription identified by the given rule name. - * - * **Caution**: If all rules on a subscription are removed, then the subscription will not receive - * any more messages. - * @param ruleName - * @throws Error if the SubscriptionClient or the underlying connection is closed. - * @throws MessagingError if the service returns an error while removing rules. - */ - - removeRule(ruleName: string): Promise; - /** - * Adds a rule on the subscription as defined by the given rule name, filter and action. - * - * **Note**: Remove the default true filter on the subscription before adding a rule. - * Otherwise, the added rule will have no affect as the true filter will always result in - * the subscription receiving all messages. - * @param ruleName Name of the rule - * @param filter A Boolean, SQL expression or a Correlation filter. For SQL Filter syntax, see - * {@link https://docs.microsoft.com/en-us/azure/service-bus-messaging/service-bus-messaging-sql-filter SQLFilter syntax}. - * @param sqlRuleActionExpression Action to perform if the message satisfies the filtering expression. For SQL Rule Action syntax, - * see {@link https://docs.microsoft.com/en-us/azure/service-bus-messaging/service-bus-messaging-sql-rule-action SQLRuleAction syntax}. - * @throws Error if the SubscriptionClient or the underlying connection is closed. - * @throws MessagingError if the service returns an error while adding rules. - */ - addRule( - ruleName: string, - filter: boolean | string | CorrelationFilter, - sqlRuleActionExpression?: string - ): Promise; - readonly defaultRuleName: string; -} - -/** - * Dynamic type that represents the proper receiver based on: - * - The lock mode (peekLock or receiveAndDelete). - * - Whether sessions are enabled or not on this particular receiver. - * - The entity type (queue or subscription). - */ -export type ClientTypeT< - ReceiveModeT extends "peekLock" | "receiveAndDelete", - EntityTypeT extends "queue" | "subscription", - SessionsEnabledT extends "sessions" | "nosessions" -> = SessionsEnabledT extends "nosessions" - ? EntityTypeT extends "queue" - ? NonSessionReceiver - : NonSessionReceiver & SubscriptionRuleManagement - : EntityTypeT extends "queue" - ? SessionReceiver - : SessionReceiver & SubscriptionRuleManagement; - -/** - * A client that can send to queues or topics. - */ -export interface ServiceBusReceiverClient { - /** - * Creates a client for an Azure Service Bus queue. - * - * @param queueAuth Data needed to connect to a queue. - * @param receiveMode The receive mode to use (defaults to PeekLock) - * @param options Options for the client itself. - */ - new ( - queueAuth: QueueAuth, - receiveMode: "peekLock", - options?: ServiceBusClientOptions - ): ClientTypeT<"peekLock", "queue", "nosessions">; - - /** - * Creates a client for an Azure Service Bus queue. - * - * @param queueAuth Data needed to connect to a queue. - * @param receiveMode The receive mode to use (defaults to PeekLock) - * @param options Options for the client itself. - */ - new ( - queueAuth: QueueAuth, - receiveMode: "receiveAndDelete", - options?: ServiceBusClientOptions - ): ClientTypeT<"receiveAndDelete", "queue", "nosessions">; - - /** - * Creates a client for an Azure Service Bus queue. - * - * @param queueAuth Data needed to connect to a queue. - * @param receiveMode The receive mode to use (defaults to PeekLock) - * @param options Options for the client itself. - */ - new ( - queueAuth: QueueAuth, - receiveMode: "peekLock", - session: Session, - options?: ServiceBusClientOptions - ): ClientTypeT<"peekLock", "queue", "sessions">; - - /** - * Creates a client for an Azure Service Bus queue. - * - * @param queueAuth Data needed to connect to a queue. - * @param receiveMode The receive mode to use (defaults to PeekLock) - * @param options Options for the client itself. - */ - new ( - queueAuth: QueueAuth, - receiveMode: "receiveAndDelete", - session: Session, - options?: ServiceBusClientOptions - ): ClientTypeT<"receiveAndDelete", "queue", "sessions">; - - /** - * Creates a client for an Azure Service Bus queue. - * - * @param subscriptionAuth Data needed to connect to a subscription. - * @param receiveMode The receive mode to use (defaults to PeekLock) - * @param options Options for the client itself. - */ - new ( - subscriptionAuth: SubscriptionAuth, - receiveMode: "peekLock", - options?: ServiceBusClientOptions - ): ClientTypeT<"peekLock", "subscription", "nosessions">; - - /** - * Creates a client for an Azure Service Bus queue. - * - * @param subscriptionAuth Data needed to connect to a subscription. - * @param receiveMode The receive mode to use (defaults to PeekLock) - * @param options Options for the client itself. - */ - new ( - subscriptionAuth: SubscriptionAuth, - receiveMode: "receiveAndDelete", - options?: ServiceBusClientOptions - ): ClientTypeT<"receiveAndDelete", "subscription", "nosessions">; - - /** - * Creates a client for an Azure Service Bus queue. - * - * @param subscriptionAuth Data needed to connect to a subscription. - * @param receiveMode The receive mode to use (defaults to PeekLock) - * @param options Options for the client itself. - */ - new ( - subscriptionAuth: SubscriptionAuth, - receiveMode: "peekLock", - session: Session, - options?: ServiceBusClientOptions - ): ClientTypeT<"peekLock", "subscription", "sessions">; - - /** - * Creates a client for an Azure Service Bus queue. - * - * @param subscriptionAuth Data needed to connect to a subscription. - * @param receiveMode The receive mode to use (defaults to PeekLock) - * @param options Options for the client itself. - */ - new ( - subscriptionAuth: SubscriptionAuth, - receiveMode: "receiveAndDelete", - session: Session, - options?: ServiceBusClientOptions - ): ClientTypeT<"receiveAndDelete", "subscription", "sessions">; -} - -/** - * Implementation class for receivers. - * @internal - * @ignore - */ -export class ReceiverClientImplementation { - constructor( - auth1: QueueAuth | SubscriptionAuth, - receiveMode2: "peekLock" | "receiveAndDelete", - sessionOrOptions3?: Session | ServiceBusClientOptions, - options4?: ServiceBusClientOptions - ) { - let options: ServiceBusClientOptions; - let session: Session | undefined; - - if (isSession(sessionOrOptions3)) { - session = sessionOrOptions3; - options = options4 || {}; - } else { - options = sessionOrOptions3 || {}; - } - this.entityType = isQueueAuth(auth1) ? "queue" : "subscription"; - const { context, entityPath } = createConnectionContext(auth1, options); - this.entityPath = entityPath; - this._context = context; - - if (receiveMode2 === "peekLock" || receiveMode2 === "receiveAndDelete") { - this.receiveMode = receiveMode2; - } else { - throw new Error("Invalid receiveMode provided"); - } - this._internalReceiveMode = convertToInternalReceiveMode(receiveMode2); - - const clientEntityContext = ClientEntityContext.create( - entityPath, - ClientType.ServiceBusReceiverClient, - context, - `${entityPath}/${generate_uuid()}` - ); - - // TODO: use the session connections object to "cache" the client entity context - if (session != null) { - const receiver = new InternalSessionReceiver(clientEntityContext, this._internalReceiveMode, { - sessionId: session.id, - maxSessionAutoRenewLockDurationInSeconds: session.maxSessionAutoRenewLockDurationInSeconds - }); - this._receiver = receiver; - - this.diagnostics = { - async peek(maxMessageCount?: number): Promise { - return (await receiver.peek(maxMessageCount)).map((m) => m as ReceivedMessage); - }, - async peekBySequenceNumber( - fromSequenceNumber: Long, - maxMessageCount?: number - ): Promise { - return (await receiver.peekBySequenceNumber(fromSequenceNumber, maxMessageCount)).map( - (m) => m as ReceivedMessage - ); - } - }; - } else { - const receiver = new InternalReceiver(clientEntityContext, this._internalReceiveMode); - this._receiver = receiver; - - this.diagnostics = { - async peek(maxMessageCount?: number): Promise { - return (await receiver.peek(entityPath, maxMessageCount)).map( - (m) => m as ReceivedMessage - ); - }, - async peekBySequenceNumber( - fromSequenceNumber: Long, - maxMessageCount?: number - ): Promise { - return ( - await receiver.peekBySequenceNumber(entityPath, fromSequenceNumber, maxMessageCount) - ).map((m) => m as ReceivedMessage); - } - }; - } - } - - public get isClosed(): boolean { - return this._receiver.isClosed; - } - - isReceivingMessages(): boolean { - return this._receiver.isReceivingMessages(); - } - - /** - * Streams messages to the passed in handlers. - * @param handlers message handlers that receive events as well as errors. - */ - subscribe(handlers: MessageHandlers, options?: SubscribeOptions): void; - /** - * Streams messages to the passed in handlers. - * @param handlers message handlers that receive events as well as errors. - */ - subscribe(handlers: MessageHandlers<{}>, options?: SubscribeOptions): void; - subscribe( - handlers: MessageHandlers<{}> | MessageHandlers, - options?: SubscribeOptions - ): void { - // TODO: use options - if ( - !handlers || - !(handlers.processMessage instanceof Function && handlers.processError instanceof Function) - ) { - throw new TypeError('Invalid "MessageHandlers" provided.'); - } - - if (this.receiveMode === "peekLock") { - const onMessage = async (sbMessage: ServiceBusMessage) => { - return handlers.processMessage(sbMessage, settlementContext); - }; - - this._receiver.registerMessageHandler( - onMessage, - (err) => { - // TODO: this isn't right - the receiver's onError is not async and needs to be fixed. - handlers.processError(err); - }, - options - ); - } else if (this.receiveMode === "receiveAndDelete") { - const actualHandlers = handlers as MessageHandlers<{}>; - - this._receiver.registerMessageHandler( - (message) => { - return actualHandlers.processMessage(message, {}); - }, - (err) => { - // TODO: this isn't right - the receiver's onError is not async and needs to be fixed. - handlers.processError(err); - }, - options - ); - } else { - throw new Error("Invalid receive mode"); - } - } - - async renewMessageLock(lockTokenOrMessage: string | ReceivedMessage): Promise { - if (!(this._receiver instanceof InternalSessionReceiver)) { - return this._receiver.renewMessageLock(lockTokenOrMessage); - } else { - throw new Error("'renewMessageLock' does not exist on 'SessionReceiver'"); - } - } - - /** - * Gets an iterator of messages that also contains a context that can be used to - * settle messages. - */ - iterateMessages(options?: IterateMessagesOptions): MessageIterator>; - /** - * Gets an iterator of messages - */ - iterateMessages( - options?: IterateMessagesOptions - ): MessageIterator>; - iterateMessages( - options?: IterateMessagesOptions - ): MessageIterator> | MessageIterator> { - // TODO: this needs to be more configurable - at least with timeouts, etc... - // TODO: use the options - const messageIterator = this._receiver.getMessageIterator(); - - if (this.receiveMode === "peekLock") { - const f = async function*( - originalMessageIterator: AsyncIterableIterator - ): AsyncIterableIterator>> { - for await (const message of originalMessageIterator) { - yield { message, context: settlementContext }; - } - }; - - return f(messageIterator); - } else if (this.receiveMode === "receiveAndDelete") { - const f = async function*( - originalMessageIterator: AsyncIterableIterator - ): AsyncIterableIterator>> { - for await (const message of originalMessageIterator) { - yield { message, context: {} }; - } - }; - return f(messageIterator); - } else { - throw new Error("Unknown receive mode"); - } - } - - receiveDeferredMessage(sequenceNumber: Long): Promise { - return this._receiver.receiveDeferredMessage(sequenceNumber); - } - - receiveDeferredMessages(sequenceNumbers: Long[]): Promise { - return this._receiver.receiveDeferredMessages(sequenceNumbers); - } - - // TODO: should probably be milliseconds - async receiveBatch( - maxMessages: number, - maxWaitTimeInSeconds?: number, - options?: ReceiveBatchOptions - ): Promise<{ messages: ReceivedMessage[]; context: ContextType<"peekLock"> }>; - // TODO: should probably be milliseconds - async receiveBatch( - maxMessages: number, - maxWaitTimeInSeconds?: number, - options?: ReceiveBatchOptions - ): Promise<{ messages: ReceivedMessage[]; context: ContextType<"receiveAndDelete"> }>; - // TODO: should probably be milliseconds - async receiveBatch( - maxMessages: number, - maxWaitTimeInSeconds?: number, - options?: ReceiveBatchOptions - ): Promise<{ - messages: ReceivedMessage[]; - context: ContextType<"receiveAndDelete"> | ContextType<"peekLock">; - }> { - // TODO: use the options (it contains things like AbortSignal) - const messages = await this._receiver.receiveMessages(maxMessages, maxWaitTimeInSeconds); - - if (this.receiveMode === "peekLock") { - return { - messages, - context: settlementContext - }; - } else if (this.receiveMode === "receiveAndDelete") { - return { - messages, - context: {} - }; - } else { - throw new Error("Unhandled receive mode"); - } - } - - getRules(): Promise { - return this._receiver.getRules(this.entityPath); - } - removeRule(ruleName: string): Promise { - return this._receiver.removeRule(this.entityPath, ruleName); - } - addRule( - ruleName: string, - filter: boolean | string | CorrelationFilter, - sqlRuleActionExpression?: string - ): Promise { - return this._receiver.addRule(this.entityPath, ruleName, filter, sqlRuleActionExpression); - } - // ManagementClient methods # Begin - // peek & peekBySequenceNumber are kept under `diagnostics` - // /** - // * Lists the ids of the sessions on the ServiceBus Queue. - // * @param maxNumberOfSessions Maximum number of sessions. - // * @param lastUpdateTime Filter to include only sessions updated after a given time. Default - // * value is 3 days before the current time. - // */ - // async listMessageSessions( - // maxNumberOfSessions: number, - // lastUpdatedTime?: Date - // ): Promise { - // TODO: Parameter validation if required - // this.throwErrorIfClientOrConnectionClosed(); - // return this._context.managementClient!.listMessageSessions( - // 0, - // maxNumberOfSessions, - // lastUpdatedTime - // ); - // } - - // ManagementClient methods # End - - // Session methods # Begin - get sessionId(): string | undefined { - if (this._receiver instanceof InternalSessionReceiver) { - return this._receiver.sessionId; - } else { - throw new Error("Only available on sessionful Receiver"); - } - } - - get sessionLockedUntilUtc(): Date | undefined { - if (this._receiver instanceof InternalSessionReceiver) { - return this._receiver.sessionLockedUntilUtc; - } else { - throw new Error("Only available on sessionful Receiver"); - } - } - - async renewSessionLock(): Promise { - if (this._receiver instanceof InternalSessionReceiver) { - return this._receiver.renewSessionLock(); - } else { - throw new Error("Only available on sessionful Receiver"); - } - } - - async setState(state: any): Promise { - if (this._receiver instanceof InternalSessionReceiver) { - return this._receiver.setState(state); - } else { - throw new Error("Only available on sessionful Receiver"); - } - } - - async getState(): Promise { - if (this._receiver instanceof InternalSessionReceiver) { - return this._receiver.getState(); - } else { - throw new Error("Only available on sessionful Receiver"); - } - } - // Session methods # End - async close(): Promise { - await this._receiver.close(); - // TODO: don't close the entire connection here if we're doing a shared connection - await ConnectionContext.close(this._context); - } - - /** - * Returns the corresponding dead letter queue path for the client entity - meant for both queue and subscription. - */ - public getDeadLetterPath(): string { - return `${this.entityPath}/$DeadLetterQueue`; - } - - public diagnostics: { - peek(maxMessageCount?: number): Promise; - peekBySequenceNumber( - fromSequenceNumber: Long, - maxMessageCount?: number - ): Promise; - }; - - /** - * @readonly - * @property The name of the default rule on the subscription. - */ - readonly defaultRuleName: string = "$Default"; - public entityPath: string; - public entityType: "queue" | "subscription"; - public receiveMode: "peekLock" | "receiveAndDelete"; - private _context: ConnectionContext; - private _internalReceiveMode: ReceiveMode; - private _receiver: InternalSessionReceiver | InternalReceiver; -} - -/** - * A client that can receive messages from Service Bus Queues or Service Bus Subscriptions. - */ -export const ServiceBusReceiverClient: ServiceBusReceiverClient = ReceiverClientImplementation; - -/** - * @internal - * @ignore - */ -const settlementContext: ContextWithSettlement = { - // TODO: need to move the settlement methods out of sb message - - // we don't need to have this runtime dependency. - abandon: (message, propertiesToModify) => - ((message as unknown) as ServiceBusMessage).abandon(propertiesToModify), - complete: (message) => ((message as unknown) as ServiceBusMessage).complete(), - defer: (message, propertiesToModify) => - ((message as unknown) as ServiceBusMessage).defer(propertiesToModify), - deadLetter: (message, options) => ((message as unknown) as ServiceBusMessage).deadLetter(options) -}; diff --git a/sdk/servicebus/service-bus/src/serviceBusSenderClient.ts b/sdk/servicebus/service-bus/src/serviceBusSenderClient.ts deleted file mode 100644 index d35ddc3b2d3e..000000000000 --- a/sdk/servicebus/service-bus/src/serviceBusSenderClient.ts +++ /dev/null @@ -1,204 +0,0 @@ -import Long from "long"; -import { Sender } from "./sender"; -import { ClientEntityContext } from "./clientEntityContext"; -import { - ServiceBusClientOptions, - createConnectionContextForTokenCredential, - createConnectionContextForConnectionString -} from "./constructorHelpers"; -import { TokenCredential, SendableMessageInfo } from "."; -import { isTokenCredential } from "@azure/core-amqp"; -import { ClientType } from "./client"; -import { generate_uuid } from "rhea-promise"; -import { ConnectionContext } from "./connectionContext"; -import { getEntityNameFromConnectionString } from "./constructorHelpers"; - -/** - * Client for sending messages to a topic or queue. - */ -export class ServiceBusSenderClient { - private _entityPath: string; - private _currentSender: Sender; - private _context: ConnectionContext; - - /** - * Creates a ServiceBusClient for the Service Bus Namespace represented in the given connection - * string. - * @param entityConnectionString - Connection string of the form - * 'Endpoint=sb://my-servicebus-namespace.servicebus.windows.net/;SharedAccessKeyName=my-SA-name;SharedAccessKey=my-SA-key;EntityPath=new-queue' - * @param options Options to control ways to interact with the - * Service Bus Namespace. - * @returns ServiceBusClient - */ - constructor(entityConnectionString: string, options?: ServiceBusClientOptions); - - /** - * Creates a ServiceBusClient for the Service Bus Namespace represented in the given connection - * string. - * @param serviceBusConnectionString - Connection string of the form - * 'Endpoint=sb://my-servicebus-namespace.servicebus.windows.net/;SharedAccessKeyName=my-SA-name;SharedAccessKey=my-SA-key' - * @param options Options to control ways to interact with the - * Service Bus Namespace. - * @returns ServiceBusClient - */ - constructor( - serviceBusConnectionString: string, - entityName: string, - options?: ServiceBusClientOptions - ); - - /** - * Instantiates a ServiceBusClient to interact with a Service Bus Namespace. - * - * @constructor - * @param host - The host name for the Service Bus namespace. This is likely to be similar to - * .servicebus.windows.net - * @param credential - credential that implements the TokenCredential interface. - * @param options - Options to control ways to interact with the Service Bus - * Namespace. - */ - constructor( - host: string, - entityName: string, - credential: TokenCredential, - options?: ServiceBusClientOptions - ); - - constructor( - hostOrConnectionString: string, - entityNameOrOptions?: string | ServiceBusClientOptions, - credentialOrServiceBusClientOptions?: TokenCredential | ServiceBusClientOptions, - options?: ServiceBusClientOptions - ) { - if (typeof entityNameOrOptions !== "string") { - // (entityConnectionString: string, options?: ServiceBusClientOptions) - const entityConnectionString = hostOrConnectionString; - const options = entityNameOrOptions; - - this._entityPath = getEntityNameFromConnectionString(entityConnectionString); - this._context = createConnectionContextForConnectionString(entityConnectionString, options); - } else if (!isTokenCredential(credentialOrServiceBusClientOptions)) { - // (serviceBusConnectionString: string, entityName: string, options?: ServiceBusClientOptions) - this._entityPath = String(entityNameOrOptions); - this._context = createConnectionContextForConnectionString(hostOrConnectionString, options); - } else { - // (host: string, entityName: string, credential: TokenCredential, options?: ServiceBusClientOptions) - const entityName = entityNameOrOptions; - this._entityPath = String(entityName); - this._context = createConnectionContextForTokenCredential( - credentialOrServiceBusClientOptions, - hostOrConnectionString, - options - ); - } - const clientEntityContext = ClientEntityContext.create( - this._entityPath, - ClientType.ServiceBusSenderClient, - this._context, - `${this._entityPath}/${generate_uuid()}` - ); - this._currentSender = new Sender(clientEntityContext); - } - - /** - * Sends the given message after creating an AMQP Sender link if it doesnt already exists. - * - * To send a message to a `session` and/or `partition` enabled Queue/Topic, set the `sessionId` - * and/or `partitionKey` properties respectively on the message. - * - * @param message - Message to send. - * @returns Promise - * @throws Error if the underlying connection, client or sender is closed. - * @throws MessagingError if the service returns an error while sending messages to the service. - */ - async send(message: SendableMessageInfo): Promise { - return this._currentSender.send(message); - } - - /** - * Sends the given messages in a single batch i.e. in a single AMQP message after creating an AMQP - * Sender link if it doesnt already exists. - * - * - To send messages to a `session` and/or `partition` enabled Queue/Topic, set the `sessionId` - * and/or `partitionKey` properties respectively on the messages. - * - When doing so, all - * messages in the batch should have the same `sessionId` (if using sessions) and the same - * `parititionKey` (if using paritions). - * - * @param messages - An array of SendableMessageInfo objects to be sent in a Batch message. - * @return Promise - * @throws Error if the underlying connection, client or sender is closed. - * @throws MessagingError if the service returns an error while sending messages to the service. - */ - async sendBatch(messages: SendableMessageInfo[]): Promise { - return this._currentSender.sendBatch(messages); - } - - /** - * Schedules given message to appear on Service Bus Queue/Subscription at a later time. - * - * @param scheduledEnqueueTimeUtc - The UTC time at which the message should be enqueued. - * @param message - The message that needs to be scheduled. - * @returns Promise - The sequence number of the message that was scheduled. - * You will need the sequence number if you intend to cancel the scheduling of the message. - * Save the `Long` type as-is in your application without converting to number. Since JavaScript - * only supports 53 bit numbers, converting the `Long` to number will cause loss in precision. - * @throws Error if the underlying connection, client or sender is closed. - * @throws MessagingError if the service returns an error while scheduling a message. - */ - async scheduleMessage( - scheduledEnqueueTimeUtc: Date, - message: SendableMessageInfo - ): Promise { - return this._currentSender.scheduleMessage(scheduledEnqueueTimeUtc, message); - } - - /** - * Schedules given messages to appear on Service Bus Queue/Subscription at a later time. - * - * @param scheduledEnqueueTimeUtc - The UTC time at which the messages should be enqueued. - * @param messages - Array of Messages that need to be scheduled. - * @returns Promise - The sequence numbers of messages that were scheduled. - * You will need the sequence number if you intend to cancel the scheduling of the messages. - * Save the `Long` type as-is in your application without converting to number. Since JavaScript - * only supports 53 bit numbers, converting the `Long` to number will cause loss in precision. - * @throws Error if the underlying connection, client or sender is closed. - * @throws MessagingError if the service returns an error while scheduling messages. - */ - async scheduleMessages( - scheduledEnqueueTimeUtc: Date, - messages: SendableMessageInfo[] - ): Promise { - return this._currentSender.scheduleMessages(scheduledEnqueueTimeUtc, messages); - } - - /** - * Cancels a message that was scheduled to appear on a ServiceBus Queue/Subscription. - * @param sequenceNumber - The sequence number of the message to be cancelled. - * @returns Promise - * @throws Error if the underlying connection, client or sender is closed. - * @throws MessagingError if the service returns an error while canceling a scheduled message. - */ - async cancelScheduledMessage(sequenceNumber: Long): Promise { - return this._currentSender.cancelScheduledMessage(sequenceNumber); - } - - /** - * Cancels multiple messages that were scheduled to appear on a ServiceBus Queue/Subscription. - * @param sequenceNumbers - An Array of sequence numbers of the messages to be cancelled. - * @returns Promise - * @throws Error if the underlying connection, client or sender is closed. - * @throws MessagingError if the service returns an error while canceling scheduled messages. - */ - async cancelScheduledMessages(sequenceNumbers: Long[]): Promise { - return this._currentSender.cancelScheduledMessages(sequenceNumbers); - } - - /** - * Closes this sender. - */ - async close(): Promise { - await this._currentSender.close(); - await ConnectionContext.close(this._context); - } -} diff --git a/sdk/servicebus/service-bus/test/batchReceiver.spec.ts b/sdk/servicebus/service-bus/test/batchReceiver.spec.ts index e0e697ce13d7..bc49b55205c1 100644 --- a/sdk/servicebus/service-bus/test/batchReceiver.spec.ts +++ b/sdk/servicebus/service-bus/test/batchReceiver.spec.ts @@ -3,1024 +3,993 @@ import chai from "chai"; import chaiAsPromised from "chai-as-promised"; -import { - delay, - SendableMessageInfo, - ServiceBusSenderClient, - ContextWithSettlement, - ReceivedMessage -} from "../src"; +import { delay, SendableMessageInfo, ContextWithSettlement, ReceivedMessage } from "../src"; import { getAlreadyReceivingErrorMsg } from "../src/util/errors"; +import { TestClientType, TestMessage } from "./utils/testUtils"; +import { Receiver } from "../src/receivers/receiver"; +import { Sender } from "../src/sender"; import { - TestClientType, - getSenderReceiverClients, - purge, - TestMessage, - isSessionfulEntity, - ReceiverClientTypeForUser, - ReceiverClientTypeForUserT -} from "./utils/testUtils"; -import { ServiceBusReceiverClient } from "../src/serviceBusReceiverClient"; -import { getEnvVars } from "./utils/envVarUtils"; + createServiceBusClientForTests, + ServiceBusClientForTests, + testPeekMsgsLength +} from "./utils/testutils2"; const should = chai.should(); chai.use(chaiAsPromised); -async function testPeekMsgsLength( - client: ReceiverClientTypeForUser, - expectedPeekLength: number -): Promise { - const peekedMsgs = await client.diagnostics.peek(expectedPeekLength + 1); - should.equal( - peekedMsgs.length, - expectedPeekLength, - "Unexpected number of msgs found when peeking" - ); -} - -let errorWasThrown: boolean; - -let senderClient: ServiceBusSenderClient; -let receiverClient: ReceiverClientTypeForUserT<"peekLock">; -let deadLetterClient: ReceiverClientTypeForUserT<"peekLock">; -const maxDeliveryCount = 10; - -async function beforeEachTest(entityType: TestClientType): Promise { - let clients; - if (isSessionfulEntity(entityType)) { - clients = await getSenderReceiverClients(entityType, "peekLock", undefined, { - id: TestMessage.sessionId - }); - } else { - clients = await getSenderReceiverClients(entityType, "peekLock"); - } +describe("batchReceiver", () => { + let serviceBusClient: ServiceBusClientForTests; + let errorWasThrown: boolean; - senderClient = clients.senderClient; - receiverClient = clients.receiverClient; - deadLetterClient = new ServiceBusReceiverClient( - { - connectionString: getEnvVars().SERVICEBUS_CONNECTION_STRING, - queueName: receiverClient.getDeadLetterPath() - }, - "peekLock" - ); - - await purge(receiverClient); - await purge(deadLetterClient); - const peekedMsgs = await receiverClient.diagnostics.peek(); - const receiverEntityType = receiverClient.entityType; - if (peekedMsgs.length) { - chai.assert.fail(`Please use an empty ${receiverEntityType} for integration testing`); - } - const peekedDeadMsgs = await deadLetterClient.diagnostics.peek(); - if (peekedDeadMsgs.length) { - chai.assert.fail( - `Please use an empty dead letter ${receiverEntityType} for integration testing` - ); - } -} + let senderClient: Sender; + let receiverClient: Receiver; + let deadLetterClient: Receiver; + const maxDeliveryCount = 10; -async function afterEachTest(): Promise { - await senderClient.close(); - await receiverClient.close(); -} + before(() => { + serviceBusClient = createServiceBusClientForTests(); + }); -describe("Batch Receiver - Settle message", function(): void { - afterEach(async () => { - await afterEachTest(); + after(() => { + return serviceBusClient.test.after(); }); - async function sendReceiveMsg( - testMessages: SendableMessageInfo - ): Promise<{ message: ReceivedMessage; context: ContextWithSettlement }> { - await senderClient.send(testMessages); - const msgs = await receiverClient.receiveBatch(1); - - should.equal(Array.isArray(msgs.messages), true, "`ReceivedMessages` is not an array"); - should.equal(msgs.messages.length, 1, "Unexpected number of messages"); - should.equal( - msgs.messages[0].body, - testMessages.body, - "MessageBody is different than expected" + async function beforeEachTest(entityType: TestClientType): Promise { + const entityNames = await serviceBusClient.test.createTestEntities(entityType); + receiverClient = serviceBusClient.test.getPeekLockReceiver(entityNames); + + senderClient = serviceBusClient.test.addToCleanup( + serviceBusClient.getSender(entityNames.queue ?? entityNames.topic!) ); - should.equal( - msgs.messages[0].messageId, - testMessages.messageId, - "MessageId is different than expected" + + deadLetterClient = serviceBusClient.test.addToCleanup( + serviceBusClient.getReceiver(receiverClient.getDeadLetterPath(), "peekLock") ); - should.equal(msgs.messages[0].deliveryCount, 0, "DeliveryCount is different than expected"); + } - return { message: msgs.messages[0], context: msgs.context as ContextWithSettlement }; + function afterEachTest(): Promise { + return serviceBusClient.test.afterEach(); } - async function testComplete(useSessions?: boolean): Promise { - const testMessages = useSessions ? TestMessage.getSessionSample() : TestMessage.getSample(); - const msg = await sendReceiveMsg(testMessages); + describe("Batch Receiver - Settle message", function(): void { + afterEach(async () => { + await afterEachTest(); + }); - await msg.context.complete(msg.message); + async function sendReceiveMsg( + testMessages: SendableMessageInfo + ): Promise<{ message: ReceivedMessage; context: ContextWithSettlement }> { + await senderClient.send(testMessages); + const msgs = await receiverClient.receiveBatch(1); - await testPeekMsgsLength(receiverClient, 0); - } + should.equal(Array.isArray(msgs.messages), true, "`ReceivedMessages` is not an array"); + should.equal(msgs.messages.length, 1, "Unexpected number of messages"); + should.equal( + msgs.messages[0].body, + testMessages.body, + "MessageBody is different than expected" + ); + should.equal( + msgs.messages[0].messageId, + testMessages.messageId, + "MessageId is different than expected" + ); + should.equal(msgs.messages[0].deliveryCount, 0, "DeliveryCount is different than expected"); - it("Partitioned Queue: complete() removes message", async function(): Promise { - await beforeEachTest(TestClientType.PartitionedQueue); - await testComplete(); - }); + return { message: msgs.messages[0], context: msgs.context as ContextWithSettlement }; + } - it("Partitioned Subscription: complete() removes message", async function(): Promise { - await beforeEachTest(TestClientType.PartitionedSubscription); - await testComplete(); - }); + async function testComplete(useSessions?: boolean): Promise { + const testMessages = useSessions ? TestMessage.getSessionSample() : TestMessage.getSample(); + const msg = await sendReceiveMsg(testMessages); - it("Unpartitioned Queue: complete() removes message #RunInBrowser ", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedQueue); - await testComplete(); - }); + await msg.context.complete(msg.message); - it("Unpartitioned Subscription: complete() removes message", async function(): Promise { - await beforeEachTest(TestClientType.UnpartitionedSubscription); - await testComplete(); - }); + await testPeekMsgsLength(receiverClient, 0); + } - it("Partitioned Queue with Sessions: complete() removes message", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedQueueWithSessions); - await testComplete(true); - }); + it("Partitioned Queue: complete() removes message", async function(): Promise { + await beforeEachTest(TestClientType.PartitionedQueue); + await testComplete(); + }); - it("Partitioned Subscription with Sessions: complete() removes message", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); - await testComplete(true); - }); + it("Partitioned Subscription: complete() removes message", async function(): Promise { + await beforeEachTest(TestClientType.PartitionedSubscription); + await testComplete(); + }); - it("Unpartitioned Queue with Sessions: complete() removes message #RunInBrowser", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); - await testComplete(true); - }); + it("Unpartitioned Queue: complete() removes message #RunInBrowser ", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedQueue); + await testComplete(); + }); - it("Unpartitioned Subscription with Sessions: complete() removes message", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedSubscriptionWithSessions); - await testComplete(true); - }); + it("Unpartitioned Subscription: complete() removes message", async function(): Promise { + await beforeEachTest(TestClientType.UnpartitionedSubscription); + await testComplete(); + }); - async function testAbandon(useSessions?: boolean): Promise { - const testMessages = useSessions ? TestMessage.getSessionSample() : TestMessage.getSample(); - const msg = await sendReceiveMsg(testMessages); - await msg.context.abandon(msg.message); + it("Partitioned Queue with Sessions: complete() removes message", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedQueueWithSessions); + await testComplete(true); + }); - await testPeekMsgsLength(receiverClient, 1); + it("Partitioned Subscription with Sessions: complete() removes message", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); + await testComplete(true); + }); - const messageBatch = await receiverClient.receiveBatch(1); + it("Unpartitioned Queue with Sessions: complete() removes message #RunInBrowser", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); + await testComplete(true); + }); - should.equal(messageBatch.messages.length, 1, "Unexpected number of messages"); - should.equal( - messageBatch.messages[0].deliveryCount, - 1, - "DeliveryCount is different than expected" - ); - should.equal( - messageBatch.messages[0].messageId, - testMessages.messageId, - "MessageId is different than expected" - ); + it("Unpartitioned Subscription with Sessions: complete() removes message", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedSubscriptionWithSessions); + await testComplete(true); + }); - await (messageBatch.context as ContextWithSettlement).complete(messageBatch.messages[0]); + async function testAbandon(useSessions?: boolean): Promise { + const testMessages = useSessions ? TestMessage.getSessionSample() : TestMessage.getSample(); + const msg = await sendReceiveMsg(testMessages); + await msg.context.abandon(msg.message); - await testPeekMsgsLength(receiverClient, 0); - } + await testPeekMsgsLength(receiverClient, 1); - it("Partitioned Queue: abandon() retains message with incremented deliveryCount", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedQueue); - await testAbandon(); - }); + const messageBatch = await receiverClient.receiveBatch(1); - it("Partitioned Subscription: abandon() retains message with incremented deliveryCount", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedSubscription); - await testAbandon(); - }); + should.equal(messageBatch.messages.length, 1, "Unexpected number of messages"); + should.equal( + messageBatch.messages[0].deliveryCount, + 1, + "DeliveryCount is different than expected" + ); + should.equal( + messageBatch.messages[0].messageId, + testMessages.messageId, + "MessageId is different than expected" + ); - it("Unpartitioned Queue: abandon() retains message with incremented deliveryCount #RunInBrowser", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedQueue); - await testAbandon(); - }); + await (messageBatch.context as ContextWithSettlement).complete(messageBatch.messages[0]); - it("Unpartitioned Subscription: abandon() retains message with incremented deliveryCount", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedSubscription); - await testAbandon(); - }); + await testPeekMsgsLength(receiverClient, 0); + } - it("Partitioned Queue with Sessions: abandon() retains message with incremented deliveryCount", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedQueueWithSessions); - await testAbandon(true); - }); + it("Partitioned Queue: abandon() retains message with incremented deliveryCount", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedQueue); + await testAbandon(); + }); - it("Partitioned Subscription with Sessions: abandon() retains message with incremented deliveryCount", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); - await testAbandon(true); - }); + it("Partitioned Subscription: abandon() retains message with incremented deliveryCount", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedSubscription); + await testAbandon(); + }); - it("Unpartitioned Queue with Sessions: abandon() retains message with incremented deliveryCount #RunInBrowser", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); - await testAbandon(true); - }); + it("Unpartitioned Queue: abandon() retains message with incremented deliveryCount #RunInBrowser", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedQueue); + await testAbandon(); + }); - it("Unpartitioned Subscription with Sessions: abandon() retains message with incremented deliveryCount", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedSubscriptionWithSessions); - await testAbandon(true); - }); + it("Unpartitioned Subscription: abandon() retains message with incremented deliveryCount", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedSubscription); + await testAbandon(); + }); - async function testAbandonMsgsTillMaxDeliveryCount(useSessions?: boolean): Promise { - const testMessages = useSessions ? TestMessage.getSessionSample() : TestMessage.getSample(); - await senderClient.send(testMessages); - let abandonMsgCount = 0; + it("Partitioned Queue with Sessions: abandon() retains message with incremented deliveryCount", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedQueueWithSessions); + await testAbandon(true); + }); - while (abandonMsgCount < maxDeliveryCount) { - const batch = await receiverClient.receiveBatch(1); + it("Partitioned Subscription with Sessions: abandon() retains message with incremented deliveryCount", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); + await testAbandon(true); + }); + + it("Unpartitioned Queue with Sessions: abandon() retains message with incremented deliveryCount #RunInBrowser", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); + await testAbandon(true); + }); + + it("Unpartitioned Subscription with Sessions: abandon() retains message with incremented deliveryCount", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedSubscriptionWithSessions); + await testAbandon(true); + }); + + async function testAbandonMsgsTillMaxDeliveryCount(useSessions?: boolean): Promise { + const testMessages = useSessions ? TestMessage.getSessionSample() : TestMessage.getSample(); + await senderClient.send(testMessages); + let abandonMsgCount = 0; + + while (abandonMsgCount < maxDeliveryCount) { + const batch = await receiverClient.receiveBatch(1); + + should.equal(batch.messages.length, 1, "Unexpected number of messages"); + should.equal( + batch.messages[0].messageId, + testMessages.messageId, + "MessageId is different than expected" + ); + should.equal( + batch.messages[0].deliveryCount, + abandonMsgCount, + "DeliveryCount is different than expected" + ); + abandonMsgCount++; + + await batch.context.abandon(batch.messages[0]); + } + + await testPeekMsgsLength(receiverClient, 0); + + const deadLetterMsgsBatch = await deadLetterClient.receiveBatch(1); - should.equal(batch.messages.length, 1, "Unexpected number of messages"); should.equal( - batch.messages[0].messageId, - testMessages.messageId, - "MessageId is different than expected" + Array.isArray(deadLetterMsgsBatch.messages), + true, + "`ReceivedMessages` from Deadletter is not an array" ); + should.equal(deadLetterMsgsBatch.messages.length, 1, "Unexpected number of messages"); should.equal( - batch.messages[0].deliveryCount, - abandonMsgCount, - "DeliveryCount is different than expected" + deadLetterMsgsBatch.messages[0].body, + testMessages.body, + "MessageBody is different than expected" + ); + should.equal( + deadLetterMsgsBatch.messages[0].messageId, + testMessages.messageId, + "MessageId is different than expected" ); - abandonMsgCount++; - - await batch.context.abandon(batch.messages[0]); - } - await testPeekMsgsLength(receiverClient, 0); + await deadLetterMsgsBatch.context.complete(deadLetterMsgsBatch.messages[0]); - const deadLetterMsgsBatch = await deadLetterClient.receiveBatch(1); + await testPeekMsgsLength(deadLetterClient, 0); + } - should.equal( - Array.isArray(deadLetterMsgsBatch.messages), - true, - "`ReceivedMessages` from Deadletter is not an array" - ); - should.equal(deadLetterMsgsBatch.messages.length, 1, "Unexpected number of messages"); - should.equal( - deadLetterMsgsBatch.messages[0].body, - testMessages.body, - "MessageBody is different than expected" - ); - should.equal( - deadLetterMsgsBatch.messages[0].messageId, - testMessages.messageId, - "MessageId is different than expected" - ); + it("Partitioned Queue: Multiple abandons until maxDeliveryCount.", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedQueue); + await testAbandonMsgsTillMaxDeliveryCount(); + }); - await deadLetterMsgsBatch.context.complete(deadLetterMsgsBatch.messages[0]); + it("Partitioned Subscription: Multiple abandons until maxDeliveryCount.", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedSubscription); + await testAbandonMsgsTillMaxDeliveryCount(); + }); - await testPeekMsgsLength(deadLetterClient, 0); - } + it("Unpartitioned Queue: Multiple abandons until maxDeliveryCount.", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedQueue); + await testAbandonMsgsTillMaxDeliveryCount(); + }); - it("Partitioned Queue: Multiple abandons until maxDeliveryCount.", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedQueue); - await testAbandonMsgsTillMaxDeliveryCount(); - }); + it("Unpartitioned Subscription: Multiple abandons until maxDeliveryCount.", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedSubscription); + await testAbandonMsgsTillMaxDeliveryCount(); + }); - it("Partitioned Subscription: Multiple abandons until maxDeliveryCount.", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedSubscription); - await testAbandonMsgsTillMaxDeliveryCount(); - }); + it("Partitioned Queue with Sessions: Multiple abandons until maxDeliveryCount.", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedQueue); + await testAbandonMsgsTillMaxDeliveryCount(true); + }); - it("Unpartitioned Queue: Multiple abandons until maxDeliveryCount.", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedQueue); - await testAbandonMsgsTillMaxDeliveryCount(); - }); + it("Partitioned Subscription with Sessions: Multiple abandons until maxDeliveryCount.", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedSubscription); + await testAbandonMsgsTillMaxDeliveryCount(true); + }); - it("Unpartitioned Subscription: Multiple abandons until maxDeliveryCount.", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedSubscription); - await testAbandonMsgsTillMaxDeliveryCount(); - }); + it("Unpartitioned Queue with Sessions: Multiple abandons until maxDeliveryCount.", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedQueue); + await testAbandonMsgsTillMaxDeliveryCount(true); + }); - it("Partitioned Queue with Sessions: Multiple abandons until maxDeliveryCount.", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedQueue); - await testAbandonMsgsTillMaxDeliveryCount(true); - }); + it("Unpartitioned Subscription with Sessions: Multiple abandons until maxDeliveryCount.", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedSubscription); + await testAbandonMsgsTillMaxDeliveryCount(true); + }); - it("Partitioned Subscription with Sessions: Multiple abandons until maxDeliveryCount.", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedSubscription); - await testAbandonMsgsTillMaxDeliveryCount(true); - }); + async function testDefer(useSessions?: boolean): Promise { + const testMessages = useSessions ? TestMessage.getSessionSample() : TestMessage.getSample(); + const msg = await sendReceiveMsg(testMessages); + + if (!msg.message.sequenceNumber) { + throw "Sequence Number can not be null"; + } + const sequenceNumber = msg.message.sequenceNumber; + await msg.context.defer(msg.message); + + const deferredMsgs = await receiverClient.receiveDeferredMessage(sequenceNumber); + if (!deferredMsgs) { + throw "No message received for sequence number"; + } + should.equal(deferredMsgs.body, testMessages.body, "MessageBody is different than expected"); + should.equal( + deferredMsgs.messageId, + testMessages.messageId, + "MessageId is different than expected" + ); + should.equal(deferredMsgs.deliveryCount, 1, "DeliveryCount is different than expected"); - it("Unpartitioned Queue with Sessions: Multiple abandons until maxDeliveryCount.", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedQueue); - await testAbandonMsgsTillMaxDeliveryCount(true); - }); + await deferredMsgs.complete(); - it("Unpartitioned Subscription with Sessions: Multiple abandons until maxDeliveryCount.", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedSubscription); - await testAbandonMsgsTillMaxDeliveryCount(true); - }); + await testPeekMsgsLength(receiverClient, 0); + } - async function testDefer(useSessions?: boolean): Promise { - const testMessages = useSessions ? TestMessage.getSessionSample() : TestMessage.getSample(); - const msg = await sendReceiveMsg(testMessages); + it("Partitioned Queue: defer() moves message to deferred queue", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedQueue); + await testDefer(); + }); - if (!msg.message.sequenceNumber) { - throw "Sequence Number can not be null"; - } - const sequenceNumber = msg.message.sequenceNumber; - await msg.context.defer(msg.message); + it("Partitioned Subscription: defer() moves message to deferred queue", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedSubscription); + await testDefer(); + }); - const deferredMsgs = await receiverClient.receiveDeferredMessage(sequenceNumber); - if (!deferredMsgs) { - throw "No message received for sequence number"; - } - should.equal(deferredMsgs.body, testMessages.body, "MessageBody is different than expected"); - should.equal( - deferredMsgs.messageId, - testMessages.messageId, - "MessageId is different than expected" - ); - should.equal(deferredMsgs.deliveryCount, 1, "DeliveryCount is different than expected"); + it("Partitioned Queue with Sessions: defer() moves message to deferred queue", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedQueueWithSessions); + await testDefer(true); + }); - await deferredMsgs.complete(); + it("Partitioned Subscription with Sessions: defer() moves message to deferred queue", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); + await testDefer(true); + }); - await testPeekMsgsLength(receiverClient, 0); - } + it("Unpartitioned Queue: defer() moves message to deferred queue #RunInBrowser", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedQueue); + await testDefer(); + }); - it("Partitioned Queue: defer() moves message to deferred queue", async function(): Promise { - await beforeEachTest(TestClientType.PartitionedQueue); - await testDefer(); - }); + it("Unpartitioned Subscription: defer() moves message to deferred queue", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedSubscription); + await testDefer(); + }); - it("Partitioned Subscription: defer() moves message to deferred queue", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedSubscription); - await testDefer(); - }); + it("Unpartitioned Queue with Sessions: defer() moves message to deferred queue #RunInBrowser", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); + await testDefer(true); + }); - it("Partitioned Queue with Sessions: defer() moves message to deferred queue", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedQueueWithSessions); - await testDefer(true); - }); + it("Unpartitioned Subscription with Sessions: defer() moves message to deferred queue", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedSubscriptionWithSessions); + await testDefer(true); + }); - it("Partitioned Subscription with Sessions: defer() moves message to deferred queue", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); - await testDefer(true); - }); + async function testDeadletter(useSessions?: boolean): Promise { + const testMessages = useSessions ? TestMessage.getSessionSample() : TestMessage.getSample(); + const msg = await sendReceiveMsg(testMessages); + await msg.context.deadLetter(msg.message); - it("Unpartitioned Queue: defer() moves message to deferred queue #RunInBrowser", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedQueue); - await testDefer(); - }); + await testPeekMsgsLength(receiverClient, 0); - it("Unpartitioned Subscription: defer() moves message to deferred queue", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedSubscription); - await testDefer(); - }); + const deadLetterMsgsBatch = await deadLetterClient.receiveBatch(1); - it("Unpartitioned Queue with Sessions: defer() moves message to deferred queue #RunInBrowser", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); - await testDefer(true); - }); + should.equal( + Array.isArray(deadLetterMsgsBatch.messages), + true, + "`ReceivedMessages` from Deadletter is not an array" + ); + should.equal(deadLetterMsgsBatch.messages.length, 1, "Unexpected number of messages"); + should.equal( + deadLetterMsgsBatch.messages[0].body, + testMessages.body, + "MessageBody is different than expected" + ); + should.equal( + deadLetterMsgsBatch.messages[0].messageId, + testMessages.messageId, + "MessageId is different than expected" + ); - it("Unpartitioned Subscription with Sessions: defer() moves message to deferred queue", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedSubscriptionWithSessions); - await testDefer(true); - }); + await deadLetterMsgsBatch.context.complete(deadLetterMsgsBatch.messages[0]); - async function testDeadletter(useSessions?: boolean): Promise { - const testMessages = useSessions ? TestMessage.getSessionSample() : TestMessage.getSample(); - const msg = await sendReceiveMsg(testMessages); - await msg.context.deadLetter(msg.message); + await testPeekMsgsLength(deadLetterClient, 0); + } - await testPeekMsgsLength(receiverClient, 0); + it("Partitioned Queue: deadLetter() moves message to deadletter queue", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedQueue); + await testDeadletter(); + }); - const deadLetterMsgsBatch = await deadLetterClient.receiveBatch(1); + it("Partitioned Subscription: deadLetter() moves message to deadletter queue", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedSubscription); + await testDeadletter(); + }); - should.equal( - Array.isArray(deadLetterMsgsBatch.messages), - true, - "`ReceivedMessages` from Deadletter is not an array" - ); - should.equal(deadLetterMsgsBatch.messages.length, 1, "Unexpected number of messages"); - should.equal( - deadLetterMsgsBatch.messages[0].body, - testMessages.body, - "MessageBody is different than expected" - ); - should.equal( - deadLetterMsgsBatch.messages[0].messageId, - testMessages.messageId, - "MessageId is different than expected" - ); + it("Unpartitioned Queue: deadLetter() moves message to deadletter queue #RunInBrowser", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedQueue); + await testDeadletter(); + }); - await deadLetterMsgsBatch.context.complete(deadLetterMsgsBatch.messages[0]); + it("Unpartitioned Subscription: deadLetter() moves message to deadletter queue", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedSubscription); + await testDeadletter(); + }); - await testPeekMsgsLength(deadLetterClient, 0); - } + it("Partitioned Queue with Sessions: deadLetter() moves message to deadletter queue", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedQueueWithSessions); + await testDeadletter(true); + }); - it("Partitioned Queue: deadLetter() moves message to deadletter queue", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedQueue); - await testDeadletter(); - }); + it("Partitioned Subscription with Sessions: deadLetter() moves message to deadletter queue", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); + await testDeadletter(true); + }); - it("Partitioned Subscription: deadLetter() moves message to deadletter queue", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedSubscription); - await testDeadletter(); - }); + it("Unpartitioned Queue with Sessions: deadLetter() moves message to deadletter queue #RunInBrowser", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); + await testDeadletter(true); + }); - it("Unpartitioned Queue: deadLetter() moves message to deadletter queue #RunInBrowser", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedQueue); - await testDeadletter(); + it("Unpartitioned Subscription with Sessions: deadLetter() moves message to deadletter queue", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedSubscriptionWithSessions); + await testDeadletter(true); + }); }); - it("Unpartitioned Subscription: deadLetter() moves message to deadletter queue", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedSubscription); - await testDeadletter(); - }); + describe("Batch Receiver - Settle deadlettered message", function(): void { + afterEach(async () => { + await afterEachTest(); + }); - it("Partitioned Queue with Sessions: deadLetter() moves message to deadletter queue", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedQueueWithSessions); - await testDeadletter(true); - }); + async function deadLetterMessage( + testMessage: SendableMessageInfo + ): Promise<{ message: ReceivedMessage; context: ContextWithSettlement }> { + await senderClient.send(testMessage); + const batch = await receiverClient.receiveBatch(1); - it("Partitioned Subscription with Sessions: deadLetter() moves message to deadletter queue", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); - await testDeadletter(true); - }); + should.equal(batch.messages.length, 1, "Unexpected number of messages"); + should.equal( + batch.messages[0].body, + testMessage.body, + "MessageBody is different than expected" + ); + should.equal( + batch.messages[0].messageId, + testMessage.messageId, + "MessageId is different than expected" + ); + should.equal(batch.messages[0].deliveryCount, 0, "DeliveryCount is different than expected"); - it("Unpartitioned Queue with Sessions: deadLetter() moves message to deadletter queue #RunInBrowser", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); - await testDeadletter(true); - }); + await batch.context.deadLetter(batch.messages[0]); - it("Unpartitioned Subscription with Sessions: deadLetter() moves message to deadletter queue", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedSubscriptionWithSessions); - await testDeadletter(true); - }); -}); + await testPeekMsgsLength(receiverClient, 0); -describe("Batch Receiver - Settle deadlettered message", function(): void { - afterEach(async () => { - await afterEachTest(); - }); + const deadLetterMsgsBatch = await deadLetterClient.receiveBatch(1); - async function deadLetterMessage( - testMessage: SendableMessageInfo - ): Promise<{ message: ReceivedMessage; context: ContextWithSettlement }> { - await senderClient.send(testMessage); - const batch = await receiverClient.receiveBatch(1); - - should.equal(batch.messages.length, 1, "Unexpected number of messages"); - should.equal( - batch.messages[0].body, - testMessage.body, - "MessageBody is different than expected" - ); - should.equal( - batch.messages[0].messageId, - testMessage.messageId, - "MessageId is different than expected" - ); - should.equal(batch.messages[0].deliveryCount, 0, "DeliveryCount is different than expected"); + should.equal(deadLetterMsgsBatch.messages.length, 1, "Unexpected number of messages"); + should.equal( + deadLetterMsgsBatch.messages[0].body, + testMessage.body, + "MessageBody is different than expected" + ); + should.equal( + deadLetterMsgsBatch.messages[0].messageId, + testMessage.messageId, + "MessageId is different than expected" + ); + should.equal( + deadLetterMsgsBatch.messages[0].deliveryCount, + 0, + "DeliveryCount is different than expected" + ); - await batch.context.deadLetter(batch.messages[0]); + return { message: deadLetterMsgsBatch.messages[0], context: deadLetterMsgsBatch.context }; + } - await testPeekMsgsLength(receiverClient, 0); + async function completeDeadLetteredMessage( + testMessage: SendableMessageInfo, + deadletterClient: Receiver, + expectedDeliverCount: number + ): Promise { + const deadLetterMsgsBatch = await deadLetterClient.receiveBatch(1); - const deadLetterMsgsBatch = await deadLetterClient.receiveBatch(1); + should.equal(deadLetterMsgsBatch.messages.length, 1, "Unexpected number of messages"); + should.equal( + deadLetterMsgsBatch.messages[0].body, + testMessage.body, + "MessageBody is different than expected" + ); + should.equal( + deadLetterMsgsBatch.messages[0].messageId, + testMessage.messageId, + "MessageId is different than expected" + ); + should.equal( + deadLetterMsgsBatch.messages[0].deliveryCount, + expectedDeliverCount, + "DeliveryCount is different than expected" + ); - should.equal(deadLetterMsgsBatch.messages.length, 1, "Unexpected number of messages"); - should.equal( - deadLetterMsgsBatch.messages[0].body, - testMessage.body, - "MessageBody is different than expected" - ); - should.equal( - deadLetterMsgsBatch.messages[0].messageId, - testMessage.messageId, - "MessageId is different than expected" - ); - should.equal( - deadLetterMsgsBatch.messages[0].deliveryCount, - 0, - "DeliveryCount is different than expected" - ); + await deadLetterMsgsBatch.context.complete(deadLetterMsgsBatch.messages[0]); + await testPeekMsgsLength(deadletterClient, 0); + } - return { message: deadLetterMsgsBatch.messages[0], context: deadLetterMsgsBatch.context }; - } + async function testDeadletter(testMessage: SendableMessageInfo): Promise { + const deadLetterMsg = await deadLetterMessage(testMessage); - async function completeDeadLetteredMessage( - testMessage: SendableMessageInfo, - deadletterClient: ReceiverClientTypeForUserT<"peekLock">, - expectedDeliverCount: number - ): Promise { - const deadLetterMsgsBatch = await deadLetterClient.receiveBatch(1); - - should.equal(deadLetterMsgsBatch.messages.length, 1, "Unexpected number of messages"); - should.equal( - deadLetterMsgsBatch.messages[0].body, - testMessage.body, - "MessageBody is different than expected" - ); - should.equal( - deadLetterMsgsBatch.messages[0].messageId, - testMessage.messageId, - "MessageId is different than expected" - ); - should.equal( - deadLetterMsgsBatch.messages[0].deliveryCount, - expectedDeliverCount, - "DeliveryCount is different than expected" - ); + await deadLetterMsg.context.deadLetter(deadLetterMsg.message).catch((err) => { + should.equal(err.code, "InvalidOperationError", "Error code is different than expected"); + errorWasThrown = true; + }); - await deadLetterMsgsBatch.context.complete(deadLetterMsgsBatch.messages[0]); - await testPeekMsgsLength(deadletterClient, 0); - } + should.equal(errorWasThrown, true, "Error thrown flag must be true"); - async function testDeadletter(testMessage: SendableMessageInfo): Promise { - const deadLetterMsg = await deadLetterMessage(testMessage); + await completeDeadLetteredMessage(testMessage, deadLetterClient, 0); + } - await deadLetterMsg.context.deadLetter(deadLetterMsg.message).catch((err) => { - should.equal(err.code, "InvalidOperationError", "Error code is different than expected"); - errorWasThrown = true; + it("Partitioned Queue: Throws error when dead lettering a dead lettered message", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedQueue); + await testDeadletter(TestMessage.getSample()); }); - should.equal(errorWasThrown, true, "Error thrown flag must be true"); + it("Partitioned Subscription: Throws error when dead lettering a dead lettered message", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedSubscription); + await testDeadletter(TestMessage.getSample()); + }); - await completeDeadLetteredMessage(testMessage, deadLetterClient, 0); - } + it("Unpartitioned Queue: Throws error when dead lettering a dead lettered message", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedQueue); + await testDeadletter(TestMessage.getSample()); + }); - it("Partitioned Queue: Throws error when dead lettering a dead lettered message", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedQueue); - await testDeadletter(TestMessage.getSample()); - }); + it("Unpartitioned Subscription: Throws error when dead lettering a dead lettered message", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedSubscription); + await testDeadletter(TestMessage.getSample()); + }); - it("Partitioned Subscription: Throws error when dead lettering a dead lettered message", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedSubscription); - await testDeadletter(TestMessage.getSample()); - }); + async function testAbandon(testMessage: SendableMessageInfo): Promise { + const deadLetterMsg = await deadLetterMessage(testMessage); - it("Unpartitioned Queue: Throws error when dead lettering a dead lettered message", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedQueue); - await testDeadletter(TestMessage.getSample()); - }); + await deadLetterMsg.context.abandon(deadLetterMsg.message); - it("Unpartitioned Subscription: Throws error when dead lettering a dead lettered message", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedSubscription); - await testDeadletter(TestMessage.getSample()); - }); + await completeDeadLetteredMessage(testMessage, deadLetterClient, 0); + } - async function testAbandon(testMessage: SendableMessageInfo): Promise { - const deadLetterMsg = await deadLetterMessage(testMessage); + it("Partitioned Queue: Abandon a message received from dead letter queue", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedQueue); + await testAbandon(TestMessage.getSample()); + }); - await deadLetterMsg.context.abandon(deadLetterMsg.message); + it("Partitioned Subscription: Abandon a message received from dead letter queue", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedSubscription); + await testAbandon(TestMessage.getSample()); + }); - await completeDeadLetteredMessage(testMessage, deadLetterClient, 0); - } + it("Unpartitioned Queue: Abandon a message received from dead letter queue", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedQueue); + await testAbandon(TestMessage.getSample()); + }); - it("Partitioned Queue: Abandon a message received from dead letter queue", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedQueue); - await testAbandon(TestMessage.getSample()); - }); + it("Unpartitioned Subscription: Abandon a message received from dead letter queue", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedSubscription); + await testAbandon(TestMessage.getSample()); + }); - it("Partitioned Subscription: Abandon a message received from dead letter queue", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedSubscription); - await testAbandon(TestMessage.getSample()); - }); + async function testDefer(testMessage: SendableMessageInfo): Promise { + const deadLetterMsg = await deadLetterMessage(testMessage); - it("Unpartitioned Queue: Abandon a message received from dead letter queue", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedQueue); - await testAbandon(TestMessage.getSample()); - }); + if (!deadLetterMsg.message.sequenceNumber) { + throw "Sequence Number can not be null"; + } - it("Unpartitioned Subscription: Abandon a message received from dead letter queue", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedSubscription); - await testAbandon(TestMessage.getSample()); - }); + const sequenceNumber = deadLetterMsg.message.sequenceNumber; + await deadLetterMsg.context.defer(deadLetterMsg.message); - async function testDefer(testMessage: SendableMessageInfo): Promise { - const deadLetterMsg = await deadLetterMessage(testMessage); + const deferredMsgs = await deadLetterClient.receiveDeferredMessage(sequenceNumber); + if (!deferredMsgs) { + throw "No message received for sequence number"; + } + should.equal(deferredMsgs.body, testMessage.body, "MessageBody is different than expected"); + should.equal( + deferredMsgs.messageId, + testMessage.messageId, + "MessageId is different than expected" + ); - if (!deadLetterMsg.message.sequenceNumber) { - throw "Sequence Number can not be null"; - } + await deferredMsgs.complete(); - const sequenceNumber = deadLetterMsg.message.sequenceNumber; - await deadLetterMsg.context.defer(deadLetterMsg.message); + await testPeekMsgsLength(receiverClient, 0); - const deferredMsgs = await deadLetterClient.receiveDeferredMessage(sequenceNumber); - if (!deferredMsgs) { - throw "No message received for sequence number"; + await testPeekMsgsLength(deadLetterClient, 0); } - should.equal(deferredMsgs.body, testMessage.body, "MessageBody is different than expected"); - should.equal( - deferredMsgs.messageId, - testMessage.messageId, - "MessageId is different than expected" - ); - - await deferredMsgs.complete(); - await testPeekMsgsLength(receiverClient, 0); + it("Partitioned Queue: Defer a message received from dead letter queue", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedQueue); + await testDefer(TestMessage.getSample()); + }); - await testPeekMsgsLength(deadLetterClient, 0); - } + it("Partitioned Subscription: Defer a message received from dead letter queue", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedSubscription); + await testDefer(TestMessage.getSample()); + }); - it("Partitioned Queue: Defer a message received from dead letter queue", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedQueue); - await testDefer(TestMessage.getSample()); - }); + it("Unpartitioned Queue: Defer a message received from dead letter queue", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedQueue); + await testDefer(TestMessage.getSample()); + }); - it("Partitioned Subscription: Defer a message received from dead letter queue", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedSubscription); - await testDefer(TestMessage.getSample()); + it("Unpartitioned Subscription: Defer a message received from dead letter queue", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedSubscription); + await testDefer(TestMessage.getSample()); + }); }); - it("Unpartitioned Queue: Defer a message received from dead letter queue", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedQueue); - await testDefer(TestMessage.getSample()); - }); + describe("Batch Receiver - Multiple Receiver Operations", function(): void { + afterEach(async () => { + await afterEachTest(); + }); - it("Unpartitioned Subscription: Defer a message received from dead letter queue", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedSubscription); - await testDefer(TestMessage.getSample()); - }); -}); + // We use an empty queue/topic here so that the first receiveMessages call takes time to return + async function testParallelReceiveCalls(useSessions?: boolean): Promise { + const firstBatchPromise = receiverClient.receiveBatch(1, 10); + await delay(5000); -describe("Batch Receiver - Multiple Receiver Operations", function(): void { - afterEach(async () => { - await afterEachTest(); - }); + let errorMessage; + const expectedErrorMessage = getAlreadyReceivingErrorMsg( + receiverClient.entityPath, + useSessions ? TestMessage.sessionId : undefined + ); - // We use an empty queue/topic here so that the first receiveMessages call takes time to return - async function testParallelReceiveCalls(useSessions?: boolean): Promise { - const firstBatchPromise = receiverClient.receiveBatch(1, 10); - await delay(5000); + try { + await receiverClient.receiveBatch(1); + } catch (err) { + errorMessage = err && err.message; + } + should.equal( + errorMessage, + expectedErrorMessage, + "Unexpected error message for receiveMessages" + ); - let errorMessage; - const expectedErrorMessage = getAlreadyReceivingErrorMsg( - receiverClient.entityPath, - useSessions ? TestMessage.sessionId : undefined - ); + let unexpectedError; + try { + receiverClient.subscribe({ + async processMessage(): Promise { + // process message here - it's basically a ServiceBusMessage minus any settlement related methods + }, + async processError(err: Error): Promise { + unexpectedError = err; + } + }); + } catch (err) { + errorMessage = err && err.message; + } + should.equal( + errorMessage, + expectedErrorMessage, + "Unexpected error message for registerMessageHandler" + ); + should.equal( + unexpectedError, + undefined, + "Unexpected error found in errorHandler for registerMessageHandler" + ); - try { - await receiverClient.receiveBatch(1); - } catch (err) { - errorMessage = err && err.message; + await firstBatchPromise; } - should.equal( - errorMessage, - expectedErrorMessage, - "Unexpected error message for receiveMessages" - ); - let unexpectedError; - try { - receiverClient.subscribe({ - async processMessage(message: ReceivedMessage): Promise { - // process message here - it's basically a ServiceBusMessage minus any settlement related methods - }, - async processError(err: Error): Promise { - unexpectedError = err; - } - }); - } catch (err) { - errorMessage = err && err.message; - } - should.equal( - errorMessage, - expectedErrorMessage, - "Unexpected error message for registerMessageHandler" - ); - should.equal( - unexpectedError, - undefined, - "Unexpected error found in errorHandler for registerMessageHandler" - ); + it("Unpartitioned Queue: Throws error when ReceiveBatch is called while the previous call is not done #RunInBrowser", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedQueue); + await testParallelReceiveCalls(); + }); - await firstBatchPromise; - } + it("Unpartitioned Queue with Sessions: Throws error when ReceiveBatch is called while the previous call is not done #RunInBrowser", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); + await testParallelReceiveCalls(true); + }); - it("Unpartitioned Queue: Throws error when ReceiveBatch is called while the previous call is not done #RunInBrowser", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedQueue); - await testParallelReceiveCalls(); - }); + const messages: SendableMessageInfo[] = [ + { + body: "hello1", + messageId: `test message ${Math.random()}`, + partitionKey: "dummy" // partitionKey is only for partitioned queue/subscrption, Unpartitioned queue/subscrption do not care about partitionKey. + }, + { + body: "hello2", + messageId: `test message ${Math.random()}`, + partitionKey: "dummy" // partitionKey is only for partitioned queue/subscrption, Unpartitioned queue/subscrption do not care about partitionKey. + } + ]; + const messageWithSessions: SendableMessageInfo[] = [ + { + body: "hello1", + messageId: `test message ${Math.random()}`, + sessionId: TestMessage.sessionId + }, + { + body: "hello2", + messageId: `test message ${Math.random()}`, + sessionId: TestMessage.sessionId + } + ]; + + // We test for mutilple receiveMessages specifically to ensure that batchingRecevier on a client is reused + // See https://github.com/Azure/azure-service-bus-node/issues/31 + async function testSequentialReceiveBatchCalls(useSessions?: boolean): Promise { + const testMessages = useSessions ? messageWithSessions : messages; + await senderClient.sendBatch(testMessages); + const msgs1 = await receiverClient.receiveBatch(1); + const msgs2 = await receiverClient.receiveBatch(1); + + // Results are checked after both receiveMessages are done to ensure that the second call doesnt + // affect the result from the first one. + should.equal(Array.isArray(msgs1.messages), true, "`ReceivedMessages` is not an array"); + should.equal(msgs1.messages.length, 1, "Unexpected number of messages"); + + should.equal(Array.isArray(msgs2.messages), true, "`ReceivedMessages` is not an array"); + should.equal(msgs2.messages.length, 1, "Unexpected number of messages"); - it("Unpartitioned Queue with Sessions: Throws error when ReceiveBatch is called while the previous call is not done #RunInBrowser", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); - await testParallelReceiveCalls(true); - }); + should.equal( + testMessages.some((x) => x.messageId === msgs1.messages[0].messageId), + true, + "MessageId is different than expected" + ); + should.equal( + testMessages.some((x) => x.messageId === msgs2.messages[0].messageId), + true, + "MessageId is different than expected" + ); - const messages: SendableMessageInfo[] = [ - { - body: "hello1", - messageId: `test message ${Math.random()}`, - partitionKey: "dummy" // partitionKey is only for partitioned queue/subscrption, Unpartitioned queue/subscrption do not care about partitionKey. - }, - { - body: "hello2", - messageId: `test message ${Math.random()}`, - partitionKey: "dummy" // partitionKey is only for partitioned queue/subscrption, Unpartitioned queue/subscrption do not care about partitionKey. - } - ]; - const messageWithSessions: SendableMessageInfo[] = [ - { - body: "hello1", - messageId: `test message ${Math.random()}`, - sessionId: TestMessage.sessionId - }, - { - body: "hello2", - messageId: `test message ${Math.random()}`, - sessionId: TestMessage.sessionId + await msgs1.context.complete(msgs1.messages[0]); + await msgs2.context.complete(msgs2.messages[0]); } - ]; - - // We test for mutilple receiveMessages specifically to ensure that batchingRecevier on a client is reused - // See https://github.com/Azure/azure-service-bus-node/issues/31 - async function testSequentialReceiveBatchCalls(useSessions?: boolean): Promise { - const testMessages = useSessions ? messageWithSessions : messages; - await senderClient.sendBatch(testMessages); - const msgs1 = await receiverClient.receiveBatch(1); - const msgs2 = await receiverClient.receiveBatch(1); - - // Results are checked after both receiveMessages are done to ensure that the second call doesnt - // affect the result from the first one. - should.equal(Array.isArray(msgs1.messages), true, "`ReceivedMessages` is not an array"); - should.equal(msgs1.messages.length, 1, "Unexpected number of messages"); - - should.equal(Array.isArray(msgs2.messages), true, "`ReceivedMessages` is not an array"); - should.equal(msgs2.messages.length, 1, "Unexpected number of messages"); - - should.equal( - testMessages.some((x) => x.messageId === msgs1.messages[0].messageId), - true, - "MessageId is different than expected" - ); - should.equal( - testMessages.some((x) => x.messageId === msgs2.messages[0].messageId), - true, - "MessageId is different than expected" - ); - await msgs1.context.complete(msgs1.messages[0]); - await msgs2.context.complete(msgs2.messages[0]); - } - - it("Unpartitioned Queue: Multiple sequential receiveMessages calls #RunInBrowser", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedQueue); - await testSequentialReceiveBatchCalls(); - }); + it("Unpartitioned Queue: Multiple sequential receiveMessages calls #RunInBrowser", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedQueue); + await testSequentialReceiveBatchCalls(); + }); - it("Unpartitioned Queue with Sessions: Multiple sequential receiveMessages calls #RunInBrowser", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); - await testSequentialReceiveBatchCalls(true); + it("Unpartitioned Queue with Sessions: Multiple sequential receiveMessages calls #RunInBrowser", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); + await testSequentialReceiveBatchCalls(true); + }); }); -}); -describe("Batch Receiver - Others", function(): void { - afterEach(async () => { - await afterEachTest(); - }); + describe("Batch Receiver - Others", function(): void { + afterEach(async () => { + await afterEachTest(); + }); - async function testNoSettlement(useSessions?: boolean): Promise { - const testMessages = useSessions ? TestMessage.getSessionSample() : TestMessage.getSample(); - await senderClient.send(testMessages); + async function testNoSettlement(useSessions?: boolean): Promise { + const testMessages = useSessions ? TestMessage.getSessionSample() : TestMessage.getSample(); + await senderClient.send(testMessages); - let batch = await receiverClient.receiveBatch(1); + let batch = await receiverClient.receiveBatch(1); - should.equal(batch.messages.length, 1, "Unexpected number of messages"); - should.equal(batch.messages[0].deliveryCount, 0, "DeliveryCount is different than expected"); - should.equal( - batch.messages[0].messageId, - testMessages.messageId, - "MessageId is different than expected" - ); + should.equal(batch.messages.length, 1, "Unexpected number of messages"); + should.equal(batch.messages[0].deliveryCount, 0, "DeliveryCount is different than expected"); + should.equal( + batch.messages[0].messageId, + testMessages.messageId, + "MessageId is different than expected" + ); - await testPeekMsgsLength(receiverClient, 1); + await testPeekMsgsLength(receiverClient, 1); - batch = await receiverClient.receiveBatch(1); + batch = await receiverClient.receiveBatch(1); - should.equal(batch.messages.length, 1, "Unexpected number of messages"); - should.equal(batch.messages[0].deliveryCount, 1, "DeliveryCount is different than expected"); - should.equal( - batch.messages[0].messageId, - testMessages.messageId, - "MessageId is different than expected" - ); + should.equal(batch.messages.length, 1, "Unexpected number of messages"); + should.equal(batch.messages[0].deliveryCount, 1, "DeliveryCount is different than expected"); + should.equal( + batch.messages[0].messageId, + testMessages.messageId, + "MessageId is different than expected" + ); - await batch.context.complete(batch.messages[0]); - } + await batch.context.complete(batch.messages[0]); + } - it("Partitioned Queue: No settlement of the message is retained with incremented deliveryCount", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedQueue); - await testNoSettlement(); - }); + it("Partitioned Queue: No settlement of the message is retained with incremented deliveryCount", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedQueue); + await testNoSettlement(); + }); - it("Partitioned Subscription: No settlement of the message is retained with incremented deliveryCount", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedSubscription); - await testNoSettlement(); - }); + it("Partitioned Subscription: No settlement of the message is retained with incremented deliveryCount", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedSubscription); + await testNoSettlement(); + }); - it("Unpartitioned Queue: No settlement of the message is retained with incremented deliveryCount #RunInBrowser", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedQueue); - await testNoSettlement(); - }); + it("Unpartitioned Queue: No settlement of the message is retained with incremented deliveryCount #RunInBrowser", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedQueue); + await testNoSettlement(); + }); - it("Unpartitioned Subscription: No settlement of the message is retained with incremented deliveryCount", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedSubscription); - await testNoSettlement(); - }); + it("Unpartitioned Subscription: No settlement of the message is retained with incremented deliveryCount", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedSubscription); + await testNoSettlement(); + }); - async function testAskForMore(useSessions?: boolean): Promise { - const testMessages = useSessions ? TestMessage.getSessionSample() : TestMessage.getSample(); - await senderClient.send(testMessages); - const batch = await receiverClient.receiveBatch(2); + async function testAskForMore(useSessions?: boolean): Promise { + const testMessages = useSessions ? TestMessage.getSessionSample() : TestMessage.getSample(); + await senderClient.send(testMessages); + const batch = await receiverClient.receiveBatch(2); - should.equal(batch.messages.length, 1, "Unexpected number of messages"); - should.equal( - batch.messages[0].body, - testMessages.body, - "MessageBody is different than expected" - ); - should.equal( - batch.messages[0].messageId, - testMessages.messageId, - "MessageId is different than expected" - ); + should.equal(batch.messages.length, 1, "Unexpected number of messages"); + should.equal( + batch.messages[0].body, + testMessages.body, + "MessageBody is different than expected" + ); + should.equal( + batch.messages[0].messageId, + testMessages.messageId, + "MessageId is different than expected" + ); - await batch.context.complete(batch.messages[0]); + await batch.context.complete(batch.messages[0]); - await testPeekMsgsLength(receiverClient, 0); - } + await testPeekMsgsLength(receiverClient, 0); + } - it("Partitioned Queue: Receive n messages but queue only has m messages, where m < n", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedQueue); + it("Partitioned Queue: Receive n messages but queue only has m messages, where m < n", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedQueue); - await testAskForMore(); - }); + await testAskForMore(); + }); - it("Partitioned Subscription: Receive n messages but subscription only has m messages, where m < n", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedSubscription); + it("Partitioned Subscription: Receive n messages but subscription only has m messages, where m < n", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedSubscription); - await testAskForMore(); - }); + await testAskForMore(); + }); - it("Unpartitioned Queue: Receive n messages but queue only has m messages, where m < n #RunInBrowser", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedQueue); + it("Unpartitioned Queue: Receive n messages but queue only has m messages, where m < n #RunInBrowser", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedQueue); - await testAskForMore(); - }); + await testAskForMore(); + }); - it("Unpartitioned Subscription: Receive n messages but subscription only has m messages, where m < n", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedSubscription); + it("Unpartitioned Subscription: Receive n messages but subscription only has m messages, where m < n", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedSubscription); - await testAskForMore(); - }); + await testAskForMore(); + }); - it("Partitioned Queue with Sessions: Receive n messages but queue only has m messages, where m < n", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedQueueWithSessions); - await testAskForMore(true); - }); + it("Partitioned Queue with Sessions: Receive n messages but queue only has m messages, where m < n", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedQueueWithSessions); + await testAskForMore(true); + }); - it("Partitioned Subscription with Sessions: Receive n messages but subscription only has m messages, where m < n", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); - await testAskForMore(true); - }); + it("Partitioned Subscription with Sessions: Receive n messages but subscription only has m messages, where m < n", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); + await testAskForMore(true); + }); - it("Unpartitioned Queue with Sessions: Receive n messages but queue only has m messages, where m < n #RunInBrowser", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); - await testAskForMore(true); - }); + it("Unpartitioned Queue with Sessions: Receive n messages but queue only has m messages, where m < n #RunInBrowser", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); + await testAskForMore(true); + }); - it("Unpartitioned Subscription with Sessions: Receive n messages but subscription only has m messages, where m < n", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedSubscriptionWithSessions); - await testAskForMore(true); + it("Unpartitioned Subscription with Sessions: Receive n messages but subscription only has m messages, where m < n", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedSubscriptionWithSessions); + await testAskForMore(true); + }); }); }); diff --git a/sdk/servicebus/service-bus/test/deferredMessage.spec.ts b/sdk/servicebus/service-bus/test/deferredMessage.spec.ts index 948d1f00d0e9..95d7d2b26db8 100644 --- a/sdk/servicebus/service-bus/test/deferredMessage.spec.ts +++ b/sdk/servicebus/service-bus/test/deferredMessage.spec.ts @@ -5,379 +5,348 @@ import chai from "chai"; const should = chai.should(); import chaiAsPromised from "chai-as-promised"; chai.use(chaiAsPromised); -import { ServiceBusMessage, SendableMessageInfo, ServiceBusSenderClient } from "../src"; -import { - TestMessage, - getSenderReceiverClients, - TestClientType, - purge, - isSessionfulEntity, - ReceiverClientTypeForUserT, - ReceiverClientTypeForUser -} from "./utils/testUtils"; -import { ServiceBusReceiverClient } from "../src/serviceBusReceiverClient"; -import { getEnvVars } from "./utils/envVarUtils"; - -async function testPeekMsgsLength( - client: ReceiverClientTypeForUser, - expectedPeekLength: number -): Promise { - const peekedMsgs = await client.diagnostics.peek(expectedPeekLength + 1); - should.equal( - peekedMsgs.length, - expectedPeekLength, - "Unexpected number of msgs found when peeking" - ); -} - -let senderClient: ServiceBusSenderClient; -let receiverClient: ReceiverClientTypeForUserT<"peekLock">; -let deadLetterClient: ReceiverClientTypeForUserT<"peekLock">; - -async function beforeEachTest(entityType: TestClientType): Promise { - let clients; - if (isSessionfulEntity(entityType)) { - clients = await getSenderReceiverClients(entityType, "peekLock", undefined, { - id: TestMessage.sessionId - }); - } else { - clients = await getSenderReceiverClients(entityType, "peekLock"); - } +import { ServiceBusMessage, SendableMessageInfo, ContextWithSettlement } from "../src"; +import { TestMessage, TestClientType } from "./utils/testUtils"; +import { testPeekMsgsLength, createServiceBusClientForTests } from "./utils/testutils2"; +import { Receiver } from "../src/receivers/receiver"; +import { Sender } from "../src/sender"; + +describe("deferred messages", () => { + let serviceBusClient: ReturnType; + let senderClient: Sender; + let receiverClient: Receiver; + let deadLetterClient: Receiver; + + before(() => { + serviceBusClient = createServiceBusClientForTests(); + }); - senderClient = clients.senderClient; - receiverClient = clients.receiverClient; - - deadLetterClient = new ServiceBusReceiverClient( - { - connectionString: getEnvVars().SERVICEBUS_CONNECTION_STRING, - queueName: receiverClient.getDeadLetterPath() - }, - "peekLock" - ); - - await purge(receiverClient); - await purge(deadLetterClient); - const peekedMsgs = await receiverClient.diagnostics.peek(); - const receiverEntityType = receiverClient.entityType; - if (peekedMsgs.length) { - chai.assert.fail(`Please use an empty ${receiverEntityType} for integration testing`); - } - const peekedDeadMsgs = await deadLetterClient.diagnostics.peek(); - if (peekedDeadMsgs.length) { - chai.assert.fail( - `Please use an empty dead letter ${receiverEntityType} for integration testing` + after(async () => { + await serviceBusClient.test.after(); + }); + + async function beforeEachTest(entityType: TestClientType): Promise { + const entityNames = await serviceBusClient.test.createTestEntities(entityType); + + receiverClient = serviceBusClient.test.getPeekLockReceiver(entityNames); + + senderClient = serviceBusClient.test.addToCleanup( + serviceBusClient.getSender(entityNames.queue ?? entityNames.topic!) + ); + + deadLetterClient = serviceBusClient.test.addToCleanup( + serviceBusClient.getReceiver(receiverClient.getDeadLetterPath(), "peekLock") ); } -} - -async function afterEachTest(): Promise { - await senderClient.close(); - await receiverClient.close(); -} - -/** - * Sends, defers, receives and then returns a test message - * @param testMessage Test message to send, defer, receive and then return - * @param useReceiveDeferredMessages Boolean to indicate whether to use `receiveDeferredMessage` or - * `receiveDeferredMessages` to ensure both get code coverage - */ -async function deferMessage( - testMessage: SendableMessageInfo, - useReceiveDeferredMessages: boolean -): Promise { - await senderClient.send(testMessage); - const batch = await receiverClient.receiveBatch(1); - const receivedMsgs = batch.messages; - - should.equal(receivedMsgs.length, 1, "Unexpected number of messages"); - should.equal(receivedMsgs[0].body, testMessage.body, "MessageBody is different than expected"); - should.equal(receivedMsgs[0].deliveryCount, 0, "DeliveryCount is different than expected"); - should.equal( - receivedMsgs[0].messageId, - testMessage.messageId, - "MessageId is different than expected" - ); - - if (!receivedMsgs[0].sequenceNumber) { - throw "Sequence Number can not be null"; + + async function afterEachTest(): Promise { + await serviceBusClient.test.afterEach(); } - const sequenceNumber = receivedMsgs[0].sequenceNumber; - await batch.context.defer(receivedMsgs[0]); - let deferredMsg: ServiceBusMessage | undefined; + /** + * Sends, defers, receives and then returns a test message + * @param testMessage Test message to send, defer, receive and then return + * @param useReceiveDeferredMessages Boolean to indicate whether to use `receiveDeferredMessage` or + * `receiveDeferredMessages` to ensure both get code coverage + */ + async function deferMessage( + testMessage: SendableMessageInfo, + useReceiveDeferredMessages: boolean + ): Promise { + await senderClient.send(testMessage); + const batch = await receiverClient.receiveBatch(1); + const receivedMsgs = batch.messages; + + should.equal(receivedMsgs.length, 1, "Unexpected number of messages"); + should.equal(receivedMsgs[0].body, testMessage.body, "MessageBody is different than expected"); + should.equal(receivedMsgs[0].deliveryCount, 0, "DeliveryCount is different than expected"); + should.equal( + receivedMsgs[0].messageId, + testMessage.messageId, + "MessageId is different than expected" + ); - // Randomly choose receiveDeferredMessage/receiveDeferredMessages as the latter is expected to - // convert single input to array and then use it - if (useReceiveDeferredMessages) { - [deferredMsg] = await receiverClient.receiveDeferredMessages(sequenceNumber as any); - } else { - deferredMsg = await receiverClient.receiveDeferredMessage(sequenceNumber); - } + if (!receivedMsgs[0].sequenceNumber) { + throw "Sequence Number can not be null"; + } + const sequenceNumber = receivedMsgs[0].sequenceNumber; + await batch.context.defer(receivedMsgs[0]); - if (!deferredMsg) { - throw "No message received for sequence number"; - } - should.equal(deferredMsg.body, testMessage.body, "MessageBody is different than expected"); - should.equal( - deferredMsg.messageId, - testMessage.messageId, - "MessageId is different than expected" - ); - should.equal(deferredMsg.deliveryCount, 1, "DeliveryCount is different than expected"); - - return deferredMsg; -} - -async function completeDeferredMessage( - sequenceNumber: Long, - expectedDeliverCount: number, - testMessages: SendableMessageInfo -): Promise { - await testPeekMsgsLength(receiverClient, 1); - - const deferredMsg = await receiverClient.receiveDeferredMessage(sequenceNumber); - if (!deferredMsg) { - throw "No message received for sequence number"; + let deferredMsg: ServiceBusMessage | undefined; + + // Randomly choose receiveDeferredMessage/receiveDeferredMessages as the latter is expected to + // convert single input to array and then use it + if (useReceiveDeferredMessages) { + [deferredMsg] = await receiverClient.receiveDeferredMessages(sequenceNumber as any); + } else { + deferredMsg = await receiverClient.receiveDeferredMessage(sequenceNumber); + } + + if (!deferredMsg) { + throw "No message received for sequence number"; + } + should.equal(deferredMsg.body, testMessage.body, "MessageBody is different than expected"); + should.equal( + deferredMsg.messageId, + testMessage.messageId, + "MessageId is different than expected" + ); + should.equal(deferredMsg.deliveryCount, 1, "DeliveryCount is different than expected"); + + return deferredMsg; } - should.equal(deferredMsg.body, testMessages.body, "MessageBody is different than expected"); - should.equal( - deferredMsg.deliveryCount, - expectedDeliverCount, - "DeliveryCount is different than expected" - ); - should.equal( - deferredMsg.messageId, - testMessages.messageId, - "MessageId is different than expected" - ); - - await deferredMsg.complete(); - - await testPeekMsgsLength(receiverClient, 0); -} - -describe("Abandon/Defer/Deadletter deferred message", function(): void { - afterEach(async () => { - await afterEachTest(); - }); + async function completeDeferredMessage( + sequenceNumber: Long, + expectedDeliverCount: number, + testMessages: SendableMessageInfo + ): Promise { + await testPeekMsgsLength(receiverClient, 1); - async function testAbandon(useSessions?: boolean): Promise { - const testMessages = useSessions ? TestMessage.getSessionSample() : TestMessage.getSample(); - const deferredMsg = await deferMessage(testMessages, true); - const sequenceNumber = deferredMsg.sequenceNumber; - if (!sequenceNumber) { - throw "Sequence Number can not be null"; + const deferredMsg = await receiverClient.receiveDeferredMessage(sequenceNumber); + if (!deferredMsg) { + throw "No message received for sequence number"; } - await deferredMsg.abandon(); - await completeDeferredMessage(sequenceNumber, 2, testMessages); + + should.equal(deferredMsg.body, testMessages.body, "MessageBody is different than expected"); + should.equal( + deferredMsg.deliveryCount, + expectedDeliverCount, + "DeliveryCount is different than expected" + ); + should.equal( + deferredMsg.messageId, + testMessages.messageId, + "MessageId is different than expected" + ); + + await deferredMsg.complete(); + + await testPeekMsgsLength(receiverClient, 0); } - it("Partitioned Queue: Abandoning a deferred message puts it back to the deferred queue.", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedQueue); - await testAbandon(); - }); + describe("Abandon/Defer/Deadletter deferred message", function(): void { + afterEach(async () => { + await afterEachTest(); + }); - it("Partitioned Subscription: Abandoning a deferred message puts it back to the deferred queue.", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedSubscription); - await testAbandon(); - }); + async function testAbandon(useSessions?: boolean): Promise { + const testMessages = useSessions ? TestMessage.getSessionSample() : TestMessage.getSample(); + const deferredMsg = await deferMessage(testMessages, true); + const sequenceNumber = deferredMsg.sequenceNumber; + if (!sequenceNumber) { + throw "Sequence Number can not be null"; + } + await deferredMsg.abandon(); + await completeDeferredMessage(sequenceNumber, 2, testMessages); + } - it("Partitioned Queue with Sessions: Abandoning a deferred message puts it back to the deferred queue.", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedQueueWithSessions); - await testAbandon(true); - }); + it("Partitioned Queue: Abandoning a deferred message puts it back to the deferred queue.", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedQueue); + await testAbandon(); + }); - it("Partitioned Subscription with Sessions: Abandoning a deferred message puts it back to the deferred queue.", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); - await testAbandon(true); - }); + it("Partitioned Subscription: Abandoning a deferred message puts it back to the deferred queue.", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedSubscription); + await testAbandon(); + }); - it("Unpartitioned Queue: Abandoning a deferred message puts it back to the deferred queue.", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedQueue); - await testAbandon(); - }); + it("Partitioned Queue with Sessions: Abandoning a deferred message puts it back to the deferred queue.", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedQueueWithSessions); + await testAbandon(true); + }); - it("Unpartitioned Subscription: Abandoning a deferred message puts it back to the deferred queue.", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedSubscription); - await testAbandon(); - }); + it("Partitioned Subscription with Sessions: Abandoning a deferred message puts it back to the deferred queue.", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); + await testAbandon(true); + }); - it("Unpartitioned Queue with Sessions:: Abandoning a deferred message puts it back to the deferred queue.", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); - await testAbandon(true); - }); + it("Unpartitioned Queue: Abandoning a deferred message puts it back to the deferred queue.", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedQueue); + await testAbandon(); + }); - it("Unpartitioned Subscription with Sessions:: Abandoning a deferred message puts it back to the deferred queue.", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedSubscriptionWithSessions); - await testAbandon(true); - }); + it("Unpartitioned Subscription: Abandoning a deferred message puts it back to the deferred queue.", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedSubscription); + await testAbandon(); + }); - async function testDefer(useSessions?: boolean): Promise { - const testMessages = useSessions ? TestMessage.getSessionSample() : TestMessage.getSample(); - const deferredMsg = await deferMessage(testMessages, false); - const sequenceNumber = deferredMsg.sequenceNumber; - if (!sequenceNumber) { - throw "Sequence Number can not be null"; + it("Unpartitioned Queue with Sessions:: Abandoning a deferred message puts it back to the deferred queue.", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); + await testAbandon(true); + }); + + it("Unpartitioned Subscription with Sessions:: Abandoning a deferred message puts it back to the deferred queue.", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedSubscriptionWithSessions); + await testAbandon(true); + }); + + async function testDefer(useSessions?: boolean): Promise { + const testMessages = useSessions ? TestMessage.getSessionSample() : TestMessage.getSample(); + const deferredMsg = await deferMessage(testMessages, false); + const sequenceNumber = deferredMsg.sequenceNumber; + if (!sequenceNumber) { + throw "Sequence Number can not be null"; + } + await deferredMsg.defer(); + await completeDeferredMessage(sequenceNumber, 2, testMessages); } - await deferredMsg.defer(); - await completeDeferredMessage(sequenceNumber, 2, testMessages); - } - it("Partitioned Queue: Deferring a deferred message puts it back to the deferred queue.", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedQueue); - await testDefer(); - }); + it("Partitioned Queue: Deferring a deferred message puts it back to the deferred queue.", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedQueue); + await testDefer(); + }); - it("Partitioned Subscription: Deferring a deferred message puts it back to the deferred queue.", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedSubscription); - await testDefer(); - }); + it("Partitioned Subscription: Deferring a deferred message puts it back to the deferred queue.", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedSubscription); + await testDefer(); + }); - it("Partitioned Queue with Sessions: Deferring a deferred message puts it back to the deferred queue.", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedQueueWithSessions); - await testDefer(true); - }); + it("Partitioned Queue with Sessions: Deferring a deferred message puts it back to the deferred queue.", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedQueueWithSessions); + await testDefer(true); + }); - it("Partitioned Subscription with Sessions: Deferring a deferred message puts it back to the deferred queue.", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); - await testDefer(true); - }); + it("Partitioned Subscription with Sessions: Deferring a deferred message puts it back to the deferred queue.", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); + await testDefer(true); + }); - it("Unpartitioned Queue: Deferring a deferred message puts it back to the deferred queue.", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedQueue); - await testDefer(); - }); + it("Unpartitioned Queue: Deferring a deferred message puts it back to the deferred queue.", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedQueue); + await testDefer(); + }); - it("Unpartitioned Subscription: Deferring a deferred message puts it back to the deferred queue.", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedSubscription); - await testDefer(); - }); + it("Unpartitioned Subscription: Deferring a deferred message puts it back to the deferred queue.", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedSubscription); + await testDefer(); + }); - it("Unpartitioned Queue with Sessions: Deferring a deferred message puts it back to the deferred queue.", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); - await testDefer(true); - }); + it("Unpartitioned Queue with Sessions: Deferring a deferred message puts it back to the deferred queue.", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); + await testDefer(true); + }); - it("Unpartitioned Subscription with Sessions: Deferring a deferred message puts it back to the deferred queue.", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedSubscriptionWithSessions); - await testDefer(true); - }); + it("Unpartitioned Subscription with Sessions: Deferring a deferred message puts it back to the deferred queue.", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedSubscriptionWithSessions); + await testDefer(true); + }); - async function testDeadletter(useSessions?: boolean): Promise { - const testMessages = useSessions ? TestMessage.getSessionSample() : TestMessage.getSample(); - const deferredMsg = await deferMessage(testMessages, true); + async function testDeadletter(useSessions?: boolean): Promise { + const testMessages = useSessions ? TestMessage.getSessionSample() : TestMessage.getSample(); + const deferredMsg = await deferMessage(testMessages, true); - await deferredMsg.deadLetter(); + await deferredMsg.deadLetter(); - await testPeekMsgsLength(receiverClient, 0); + await testPeekMsgsLength(receiverClient, 0); - const deadLetterMsgsBatch = await deadLetterClient.receiveBatch(1); - const deadLetterMsgs = deadLetterMsgsBatch.messages; + const deadLetterMsgsBatch = await deadLetterClient.receiveBatch(1); + const deadLetterMsgs = deadLetterMsgsBatch.messages; - should.equal(deadLetterMsgs.length, 1, "Unexpected number of messages"); - should.equal( - deadLetterMsgs[0].body, - testMessages.body, - "MessageBody is different than expected" - ); - should.equal(deadLetterMsgs[0].deliveryCount, 1, "DeliveryCount is different than expected"); - should.equal( - deadLetterMsgs[0].messageId, - testMessages.messageId, - "MessageId is different than expected" - ); + should.equal(deadLetterMsgs.length, 1, "Unexpected number of messages"); + should.equal( + deadLetterMsgs[0].body, + testMessages.body, + "MessageBody is different than expected" + ); + should.equal(deadLetterMsgs[0].deliveryCount, 1, "DeliveryCount is different than expected"); + should.equal( + deadLetterMsgs[0].messageId, + testMessages.messageId, + "MessageId is different than expected" + ); - await deadLetterMsgsBatch.context.complete(deadLetterMsgs[0]); + await deadLetterMsgsBatch.context.complete(deadLetterMsgs[0]); - await testPeekMsgsLength(deadLetterClient, 0); - } + await testPeekMsgsLength(deadLetterClient, 0); + } - it("Partitioned Queue: Deadlettering a deferred message moves it to dead letter queue.", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedQueue); - await testDeadletter(); - }); + it("Partitioned Queue: Deadlettering a deferred message moves it to dead letter queue.", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedQueue); + await testDeadletter(); + }); - it("Partitioned Subscription: Deadlettering a deferred message moves it to dead letter queue.", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedSubscription); - await testDeadletter(); - }); + it("Partitioned Subscription: Deadlettering a deferred message moves it to dead letter queue.", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedSubscription); + await testDeadletter(); + }); - it("Partitioned Queue with Sessions: Deadlettering a deferred message moves it to dead letter queue.", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedQueueWithSessions); - await testDeadletter(true); - }); + it("Partitioned Queue with Sessions: Deadlettering a deferred message moves it to dead letter queue.", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedQueueWithSessions); + await testDeadletter(true); + }); - it("Partitioned Subscription with Sessions: Deadlettering a deferred message moves it to dead letter queue.", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); - await testDeadletter(true); - }); + it("Partitioned Subscription with Sessions: Deadlettering a deferred message moves it to dead letter queue.", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); + await testDeadletter(true); + }); - it("Unpartitioned Queue: Deadlettering a deferred message moves it to dead letter queue.", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedQueue); - await testDeadletter(); - }); + it("Unpartitioned Queue: Deadlettering a deferred message moves it to dead letter queue.", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedQueue); + await testDeadletter(); + }); - it("Unpartitioned Subscription: Deadlettering a deferred message moves it to dead letter queue.", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedSubscription); - await testDeadletter(); - }); + it("Unpartitioned Subscription: Deadlettering a deferred message moves it to dead letter queue.", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedSubscription); + await testDeadletter(); + }); - it("Unpartitioned Queue with Sessions: Deadlettering a deferred message moves it to dead letter queue.", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); - await testDeadletter(true); - }); + it("Unpartitioned Queue with Sessions: Deadlettering a deferred message moves it to dead letter queue.", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); + await testDeadletter(true); + }); - it("Unpartitioned Subscription with Sessions: Deadlettering a deferred message moves it to dead letter queue.", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedSubscriptionWithSessions); - await testDeadletter(true); + it("Unpartitioned Subscription with Sessions: Deadlettering a deferred message moves it to dead letter queue.", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedSubscriptionWithSessions); + await testDeadletter(true); + }); }); }); diff --git a/sdk/servicebus/service-bus/test/invalidParameters.spec.ts b/sdk/servicebus/service-bus/test/invalidParameters.spec.ts index 162ed0e61181..cb76c390dd20 100644 --- a/sdk/servicebus/service-bus/test/invalidParameters.spec.ts +++ b/sdk/servicebus/service-bus/test/invalidParameters.spec.ts @@ -6,923 +6,932 @@ import Long from "long"; const should = chai.should(); import chaiAsPromised from "chai-as-promised"; chai.use(chaiAsPromised); -import { ServiceBusSenderClient } from "../src"; -import { - TestMessage, - getSenderReceiverClients, - TestClientType, - EntityNames, - ReceiverClientTypeForUserT -} from "./utils/testUtils"; -import { - NonSessionReceiver, - SubscriptionRuleManagement, - SessionReceiver, - ServiceBusReceiverClient -} from "../src/serviceBusReceiverClient"; -import { getEnvVars } from "./utils/envVarUtils"; - -describe("Invalid parameters in Sender/ReceiverClients for PartitionedQueue #RunInBrowser", function(): void { - let senderClient: ServiceBusSenderClient; - let queueReceiverClient: ReceiverClientTypeForUserT<"peekLock">; - - // Since, the below tests never actually make use of any AMQP links, there is no need to create - // new sender/receiver clients before each test. Doing it once for each describe block. - before(async () => { - const clients = await getSenderReceiverClients(TestClientType.PartitionedQueue, "peekLock"); - senderClient = clients.senderClient; - queueReceiverClient = clients.receiverClient; - }); - - after(async () => { - await senderClient.close(); - await queueReceiverClient.close(); - }); - - it("Peek: Invalid maxMessageCount for Queue", async function(): Promise { - const peekedResults = await queueReceiverClient.diagnostics.peek(-100); - should.equal(peekedResults.length, 0); - }); - - it("Peek: Wrong type maxMessageCount for Queue", async function(): Promise { - let caughtError: Error | undefined; - try { - await queueReceiverClient.diagnostics.peek("somestring" as any); - } catch (error) { - caughtError = error; - } - should.equal(caughtError && caughtError.name, "TypeError"); - should.equal( - caughtError && caughtError.message, - `The parameter "maxMessageCount" should be of type "number"` - ); - }); - - it("PeekBySequenceNumber: Invalid maxMessageCount for Queue", async function(): Promise { - const peekedResults = await queueReceiverClient.diagnostics.peekBySequenceNumber( - Long.ZERO, - -100 - ); - should.equal(peekedResults.length, 0); - }); - - it("PeekBySequenceNumber: Wrong type maxMessageCount for Queue", async function(): Promise { - let caughtError: Error | undefined; - try { - await queueReceiverClient.diagnostics.peekBySequenceNumber(Long.ZERO, "somestring" as any); - } catch (error) { - caughtError = error; - } - should.equal(caughtError && caughtError.name, "TypeError"); - should.equal( - caughtError && caughtError.message, - `The parameter "maxMessageCount" should be of type "number"` - ); - }); +import { ContextWithSettlement } from "../src"; +import { TestMessage, TestClientType } from "./utils/testUtils"; +import { ServiceBusClientForTests, createServiceBusClientForTests } from "./utils/testutils2"; +import { SubscriptionRuleManagement, Receiver } from "../src/receivers/receiver"; +import { Sender } from "../src/sender"; +import { SessionReceiver } from "../src/receivers/sessionReceiver"; - it("PeekBySequenceNumber: Wrong type fromSequenceNumber for Queue", async function(): Promise< - void - > { - let caughtError: Error | undefined; - try { - await queueReceiverClient.diagnostics.peekBySequenceNumber("somestring" as any); - } catch (error) { - caughtError = error; - } - should.equal(caughtError && caughtError.name, "TypeError"); - should.equal( - caughtError && caughtError.message, - `The parameter "fromSequenceNumber" should be of type "Long"` - ); - }); +describe("invalid parameters", () => { + let serviceBusClient: ServiceBusClientForTests; - it("PeekBySequenceNumber: Missing fromSequenceNumber for Queue", async function(): Promise { - let caughtError: Error | undefined; - try { - await queueReceiverClient.diagnostics.peekBySequenceNumber(undefined as any); - } catch (error) { - caughtError = error; - } - should.equal(caughtError && caughtError.name, "TypeError"); - should.equal(caughtError && caughtError.message, `Missing parameter "fromSequenceNumber"`); + before(() => { + serviceBusClient = createServiceBusClientForTests(); }); -}); -describe("Invalid parameters in Sender/ReceiverClients for PartitionedSubscription #RunInBrowser", function(): void { - let senderClient: ServiceBusSenderClient; - let subscriptionReceiverClient: NonSessionReceiver<"peekLock"> & SubscriptionRuleManagement; - - // Since, the below tests never actually make use of any AMQP links, there is no need to create - // new sender/receiver clients before each test. Doing it once for each describe block. - before(async () => { - const clients = await getSenderReceiverClients( - TestClientType.PartitionedSubscription, - "peekLock" - ); - senderClient = clients.senderClient; - subscriptionReceiverClient = clients.receiverClient as NonSessionReceiver<"peekLock"> & - SubscriptionRuleManagement; + after(() => { + return serviceBusClient.test.after(); }); - after(async () => { - await senderClient.close(); - await subscriptionReceiverClient.close(); - }); + describe("Invalid parameters in Sender/ReceiverClients for PartitionedQueue #RunInBrowser", function(): void { + let receiver: Receiver; - it("Peek: Invalid maxMessageCount for Subscription", async function(): Promise { - const peekedResults = await subscriptionReceiverClient.diagnostics.peek(-100); - should.equal(peekedResults.length, 0); - }); + // Since, the below tests never actually make use of any AMQP links, there is no need to create + // new sender/receiver clients before each test. Doing it once for each describe block. + before(async () => { + const entityNames = await serviceBusClient.test.createTestEntities( + TestClientType.PartitionedQueue + ); - it("Peek: Wrong type maxMessageCount for Subscription", async function(): Promise { - let caughtError: Error | undefined; - try { - await subscriptionReceiverClient.diagnostics.peek("somestring" as any); - } catch (error) { - caughtError = error; - } - should.equal(caughtError && caughtError.name, "TypeError"); - should.equal( - caughtError && caughtError.message, - `The parameter "maxMessageCount" should be of type "number"` - ); - }); + receiver = serviceBusClient.test.getPeekLockReceiver(entityNames); + }); + + after(async () => { + await serviceBusClient.test.afterEach(); + }); + + it("Peek: Invalid maxMessageCount for Queue", async function(): Promise { + const peekedResults = await receiver.diagnostics.peek(-100); + should.equal(peekedResults.length, 0); + }); + + it("Peek: Wrong type maxMessageCount for Queue", async function(): Promise { + let caughtError: Error | undefined; + try { + await receiver.diagnostics.peek("somestring" as any); + } catch (error) { + caughtError = error; + } + should.equal(caughtError && caughtError.name, "TypeError"); + should.equal( + caughtError && caughtError.message, + `The parameter "maxMessageCount" should be of type "number"` + ); + }); + + it("PeekBySequenceNumber: Invalid maxMessageCount for Queue", async function(): Promise { + const peekedResults = await receiver.diagnostics.peekBySequenceNumber(Long.ZERO, -100); + should.equal(peekedResults.length, 0); + }); + + it("PeekBySequenceNumber: Wrong type maxMessageCount for Queue", async function(): Promise< + void + > { + let caughtError: Error | undefined; + try { + await receiver.diagnostics.peekBySequenceNumber(Long.ZERO, "somestring" as any); + } catch (error) { + caughtError = error; + } + should.equal(caughtError && caughtError.name, "TypeError"); + should.equal( + caughtError && caughtError.message, + `The parameter "maxMessageCount" should be of type "number"` + ); + }); + + it("PeekBySequenceNumber: Wrong type fromSequenceNumber for Queue", async function(): Promise< + void + > { + let caughtError: Error | undefined; + try { + await receiver.diagnostics.peekBySequenceNumber("somestring" as any); + } catch (error) { + caughtError = error; + } + should.equal(caughtError && caughtError.name, "TypeError"); + should.equal( + caughtError && caughtError.message, + `The parameter "fromSequenceNumber" should be of type "Long"` + ); + }); + + it("PeekBySequenceNumber: Missing fromSequenceNumber for Queue", async function(): Promise< + void + > { + let caughtError: Error | undefined; + try { + await receiver.diagnostics.peekBySequenceNumber(undefined as any); + } catch (error) { + caughtError = error; + } + should.equal(caughtError && caughtError.name, "TypeError"); + should.equal(caughtError && caughtError.message, `Missing parameter "fromSequenceNumber"`); + }); + }); + + describe("Invalid parameters in Sender/ReceiverClients for PartitionedSubscription #RunInBrowser", function(): void { + let subscriptionReceiverClient: Receiver & SubscriptionRuleManagement; + + // Since, the below tests never actually make use of any AMQP links, there is no need to create + // new sender/receiver clients before each test. Doing it once for each describe block. + before(async () => { + const entityNames = await serviceBusClient.test.createTestEntities( + TestClientType.PartitionedSubscription + ); - it("PeekBySequenceNumber: Invalid maxMessageCount for Subscription", async function(): Promise< - void - > { - const peekedResults = await subscriptionReceiverClient.diagnostics.peekBySequenceNumber( - Long.ZERO, - -100 - ); - should.equal(peekedResults.length, 0); - }); + subscriptionReceiverClient = serviceBusClient.test.getSubscriptionPeekLockReceiver( + entityNames + ); + }); + + after(() => { + return serviceBusClient.test.afterEach(); + }); + + it("Peek: Invalid maxMessageCount for Subscription", async function(): Promise { + const peekedResults = await subscriptionReceiverClient.diagnostics.peek(-100); + should.equal(peekedResults.length, 0); + }); + + it("Peek: Wrong type maxMessageCount for Subscription", async function(): Promise { + let caughtError: Error | undefined; + try { + await subscriptionReceiverClient.diagnostics.peek("somestring" as any); + } catch (error) { + caughtError = error; + } + should.equal(caughtError && caughtError.name, "TypeError"); + should.equal( + caughtError && caughtError.message, + `The parameter "maxMessageCount" should be of type "number"` + ); + }); - it("PeekBySequenceNumber: Wrong type maxMessageCount for Subscription", async function(): Promise< - void - > { - let caughtError: Error | undefined; - try { - await subscriptionReceiverClient.diagnostics.peekBySequenceNumber( + it("PeekBySequenceNumber: Invalid maxMessageCount for Subscription", async function(): Promise< + void + > { + const peekedResults = await subscriptionReceiverClient.diagnostics.peekBySequenceNumber( Long.ZERO, - "somestring" as any + -100 + ); + should.equal(peekedResults.length, 0); + }); + + it("PeekBySequenceNumber: Wrong type maxMessageCount for Subscription", async function(): Promise< + void + > { + let caughtError: Error | undefined; + try { + await subscriptionReceiverClient.diagnostics.peekBySequenceNumber( + Long.ZERO, + "somestring" as any + ); + } catch (error) { + caughtError = error; + } + should.equal(caughtError && caughtError.name, "TypeError"); + should.equal( + caughtError && caughtError.message, + `The parameter "maxMessageCount" should be of type "number"` + ); + }); + + it("PeekBySequenceNumber: Wrong type fromSequenceNumber for Subscription", async function(): Promise< + void + > { + let caughtError: Error | undefined; + try { + await subscriptionReceiverClient.diagnostics.peekBySequenceNumber("somestring" as any); + } catch (error) { + caughtError = error; + } + should.equal(caughtError && caughtError.name, "TypeError"); + should.equal( + caughtError && caughtError.message, + `The parameter "fromSequenceNumber" should be of type "Long"` + ); + }); + + it("PeekBySequenceNumber: Missing fromSequenceNumber for Subscription", async function(): Promise< + void + > { + let caughtError: Error | undefined; + try { + await subscriptionReceiverClient.diagnostics.peekBySequenceNumber(undefined as any); + } catch (error) { + caughtError = error; + } + should.equal(caughtError && caughtError.name, "TypeError"); + should.equal(caughtError && caughtError.message, `Missing parameter "fromSequenceNumber"`); + }); + + it("AddRule: Missing ruleName", async function(): Promise { + let caughtError: Error | undefined; + try { + await subscriptionReceiverClient.addRule(undefined as any, undefined as any); + } catch (error) { + caughtError = error; + } + should.equal(caughtError && caughtError.name, "TypeError"); + should.equal(caughtError && caughtError.message, `Missing parameter "ruleName"`); + }); + + it("AddRule: Empty string as ruleName", async function(): Promise { + let caughtError: Error | undefined; + try { + await subscriptionReceiverClient.addRule("", false); + } catch (error) { + caughtError = error; + } + should.equal(caughtError && caughtError.name, "TypeError"); + should.equal( + caughtError && caughtError.message, + `Empty string not allowed in parameter "ruleName"` + ); + }); + + it("AddRule: Missing filter", async function(): Promise { + let caughtError: Error | undefined; + try { + await subscriptionReceiverClient.addRule("myrule", undefined as any); + } catch (error) { + caughtError = error; + } + should.equal(caughtError && caughtError.name, "TypeError"); + should.equal(caughtError && caughtError.message, `Missing parameter "filter"`); + }); + + it("AddRule: Invalid filter", async function(): Promise { + let caughtError: Error | undefined; + try { + await subscriptionReceiverClient.addRule("myrule", { random: "value" } as any); + } catch (error) { + caughtError = error; + } + should.equal(caughtError && caughtError.name, "TypeError"); + should.equal( + caughtError && caughtError.message, + `The parameter "filter" should be either a boolean, string or implement the CorrelationFilter interface.` + ); + }); + + it("RemoveRule: Missing ruleName", async function(): Promise { + let caughtError: Error | undefined; + try { + await subscriptionReceiverClient.removeRule(undefined as any); + } catch (error) { + caughtError = error; + } + should.equal(caughtError && caughtError.name, "TypeError"); + should.equal(caughtError && caughtError.message, `Missing parameter "ruleName"`); + }); + + it("RemoveRule: Empty string as ruleName", async function(): Promise { + let caughtError: Error | undefined; + try { + await subscriptionReceiverClient.removeRule(""); + } catch (error) { + caughtError = error; + } + should.equal(caughtError && caughtError.name, "TypeError"); + should.equal( + caughtError && caughtError.message, + `Empty string not allowed in parameter "ruleName"` + ); + }); + + it("Add and Remove Rule: Coerce RuleName into string", async function(): Promise { + // Clean up existing rules + let rules = await subscriptionReceiverClient.getRules(); + await Promise.all(rules.map((rule) => subscriptionReceiverClient.removeRule(rule.name))); + + // Add rule with number as name + await subscriptionReceiverClient.addRule(123 as any, true); + rules = await subscriptionReceiverClient.getRules(); + should.equal( + rules.some((rule) => rule.name === "123"), + true, + "Added rule not found" ); - } catch (error) { - caughtError = error; - } - should.equal(caughtError && caughtError.name, "TypeError"); - should.equal( - caughtError && caughtError.message, - `The parameter "maxMessageCount" should be of type "number"` - ); - }); - - it("PeekBySequenceNumber: Wrong type fromSequenceNumber for Subscription", async function(): Promise< - void - > { - let caughtError: Error | undefined; - try { - await subscriptionReceiverClient.diagnostics.peekBySequenceNumber("somestring" as any); - } catch (error) { - caughtError = error; - } - should.equal(caughtError && caughtError.name, "TypeError"); - should.equal( - caughtError && caughtError.message, - `The parameter "fromSequenceNumber" should be of type "Long"` - ); - }); - - it("PeekBySequenceNumber: Missing fromSequenceNumber for Subscription", async function(): Promise< - void - > { - let caughtError: Error | undefined; - try { - await subscriptionReceiverClient.diagnostics.peekBySequenceNumber(undefined as any); - } catch (error) { - caughtError = error; - } - should.equal(caughtError && caughtError.name, "TypeError"); - should.equal(caughtError && caughtError.message, `Missing parameter "fromSequenceNumber"`); - }); - - it("AddRule: Missing ruleName", async function(): Promise { - let caughtError: Error | undefined; - try { - await subscriptionReceiverClient.addRule(undefined as any, undefined as any); - } catch (error) { - caughtError = error; - } - should.equal(caughtError && caughtError.name, "TypeError"); - should.equal(caughtError && caughtError.message, `Missing parameter "ruleName"`); - }); - - it("AddRule: Empty string as ruleName", async function(): Promise { - let caughtError: Error | undefined; - try { - await subscriptionReceiverClient.addRule("", false); - } catch (error) { - caughtError = error; - } - should.equal(caughtError && caughtError.name, "TypeError"); - should.equal( - caughtError && caughtError.message, - `Empty string not allowed in parameter "ruleName"` - ); - }); - - it("AddRule: Missing filter", async function(): Promise { - let caughtError: Error | undefined; - try { - await subscriptionReceiverClient.addRule("myrule", undefined as any); - } catch (error) { - caughtError = error; - } - should.equal(caughtError && caughtError.name, "TypeError"); - should.equal(caughtError && caughtError.message, `Missing parameter "filter"`); - }); - - it("AddRule: Invalid filter", async function(): Promise { - let caughtError: Error | undefined; - try { - await subscriptionReceiverClient.addRule("myrule", { random: "value" } as any); - } catch (error) { - caughtError = error; - } - should.equal(caughtError && caughtError.name, "TypeError"); - should.equal( - caughtError && caughtError.message, - `The parameter "filter" should be either a boolean, string or implement the CorrelationFilter interface.` - ); - }); - - it("RemoveRule: Missing ruleName", async function(): Promise { - let caughtError: Error | undefined; - try { - await subscriptionReceiverClient.removeRule(undefined as any); - } catch (error) { - caughtError = error; - } - should.equal(caughtError && caughtError.name, "TypeError"); - should.equal(caughtError && caughtError.message, `Missing parameter "ruleName"`); - }); - it("RemoveRule: Empty string as ruleName", async function(): Promise { - let caughtError: Error | undefined; - try { - await subscriptionReceiverClient.removeRule(""); - } catch (error) { - caughtError = error; - } - should.equal(caughtError && caughtError.name, "TypeError"); - should.equal( - caughtError && caughtError.message, - `Empty string not allowed in parameter "ruleName"` - ); - }); + // Remove rule with number as name + await subscriptionReceiverClient.removeRule(123 as any); + rules = await subscriptionReceiverClient.getRules(); + should.equal( + rules.some((rule) => rule.name === "123"), + false, + "Removed rule still found" + ); - it("Add and Remove Rule: Coerce RuleName into string", async function(): Promise { - // Clean up existing rules - let rules = await subscriptionReceiverClient.getRules(); - await Promise.all(rules.map((rule) => subscriptionReceiverClient.removeRule(rule.name))); - - // Add rule with number as name - await subscriptionReceiverClient.addRule(123 as any, true); - rules = await subscriptionReceiverClient.getRules(); - should.equal( - rules.some((rule) => rule.name === "123"), - true, - "Added rule not found" - ); - - // Remove rule with number as name - await subscriptionReceiverClient.removeRule(123 as any); - rules = await subscriptionReceiverClient.getRules(); - should.equal( - rules.some((rule) => rule.name === "123"), - false, - "Removed rule still found" - ); - - // Add default rule so that other tests are not affected - await subscriptionReceiverClient.addRule(subscriptionReceiverClient.defaultRuleName, true); + // Add default rule so that other tests are not affected + await subscriptionReceiverClient.addRule(subscriptionReceiverClient.defaultRuleName, true); + }); }); -}); -describe("Invalid parameters in SessionReceiver #RunInBrowser", function(): void { - let senderClient: ServiceBusSenderClient; - let receiverClient: SessionReceiver<"peekLock">; + describe("Invalid parameters in SessionReceiver #RunInBrowser", function(): void { + let sender: Sender; + let receiver: SessionReceiver; - // Since, the below tests never actually make use of any AMQP links, there is no need to create - // new sender/receiver clients before each test. Doing it once for each describe block. - before(async () => { - const clients = await getSenderReceiverClients( - TestClientType.PartitionedQueueWithSessions, - "peekLock", - undefined, - { id: TestMessage.sessionId } - ); - - senderClient = clients.senderClient; - await senderClient.send(TestMessage.getSessionSample()); - - receiverClient = clients.receiverClient as SessionReceiver<"peekLock">; - }); + // Since, the below tests never actually make use of any AMQP links, there is no need to create + // new sender/receiver clients before each test. Doing it once for each describe block. + before(async () => { + const entityNames = await serviceBusClient.test.createTestEntities( + TestClientType.PartitionedQueueWithSessions + ); - after(async () => { - await senderClient.close(); - await receiverClient.close(); + sender = serviceBusClient.test.addToCleanup(serviceBusClient.getSender(entityNames.queue!)); + + receiver = serviceBusClient.test.getSessionPeekLockReceiver(entityNames, { + sessionId: TestMessage.sessionId + }); + + await sender.send(TestMessage.getSessionSample()); + }); + + after(() => { + return serviceBusClient.test.afterEach(); + }); + + // #RevisitCommentedTestsAfterTheSingleClientAPI + // Reason for commenting the following 2 tests + // `ReceiveMode` is now being passed in the Client - and this test is covered in the newly added `SessionReceiver: Throws error if created a client with invalid receiveMode` + // Supposed to be reverted and made changes accordingly once the Toplevel Client is added. + // it("SessionReceiver: Missing ReceiveMode", async function(): Promise { + // receiverClient = new ServiceBusReceiverClient( + // { + // queueName: EntityNames.QUEUE_NAME_SESSION, + // connectionString: getEnvVars()["SERVICEBUS_CONNECTION_STRING"] + // }, + // undefined as any, + // { + // id: TestMessage.sessionId + // } + // ) as any; + // should.equal( + // receiverClient.receiveMode, + // ReceiveMode.peekLock, + // "Default receiveMode not set when receiveMode not provided to constructor." + // ); + // }); + + // it("SessionReceiver: Invalid ReceiveMode", async function(): Promise { + // receiverClient = new ServiceBusReceiverClient( + // { + // queueName: EntityNames.QUEUE_NAME_SESSION, + // connectionString: getEnvVars()["SERVICEBUS_CONNECTION_STRING"] + // }, + // 123 as any, + // { + // id: TestMessage.sessionId + // } + // ) as any; + // should.equal( + // receiverClient.receiveMode, + // ReceiveMode.peekLock, + // "Default receiveMode not set when receiveMode not provided to constructor." + // ); + // }); + + it("SessionReceiver: Throws error if created a client with invalid receiveMode", async function(): Promise< + void + > { + let errorCaught: string = ""; + try { + const { queue } = serviceBusClient.test.getTestEntities( + TestClientType.PartitionedQueueWithSessions + ); + + await serviceBusClient.getSessionReceiver(queue!, 123 as any, { + sessionId: TestMessage.sessionId + }); + } catch (error) { + errorCaught = error.message; + } + should.equal( + errorCaught, + "Invalid receiveMode provided", + "Did not throw error if created a client with invalid receiveMode." + ); + }); + + it("Peek: Invalid maxMessageCount in SessionReceiver", async function(): Promise { + const peekedResults = await receiver.diagnostics.peek(-100); + should.equal(peekedResults.length, 0); + }); + + it("Peek: Wrong type maxMessageCount in SessionReceiver", async function(): Promise { + let caughtError: Error | undefined; + try { + await receiver.diagnostics.peek("somestring" as any); + } catch (error) { + caughtError = error; + } + should.equal(caughtError && caughtError.name, "TypeError"); + should.equal( + caughtError && caughtError.message, + `The parameter "maxMessageCount" should be of type "number"` + ); + }); + + it("PeekBySequenceNumber: Invalid maxMessageCount in SessionReceiver", async function(): Promise< + void + > { + const peekedResults = await receiver.diagnostics.peekBySequenceNumber(Long.ZERO, -100); + should.equal(peekedResults.length, 0); + }); + + it("PeekBySequenceNumber: Wrong type maxMessageCount in SessionReceiver", async function(): Promise< + void + > { + let caughtError: Error | undefined; + try { + await receiver.diagnostics.peekBySequenceNumber(Long.ZERO, "somestring" as any); + } catch (error) { + caughtError = error; + } + should.equal(caughtError && caughtError.name, "TypeError"); + should.equal( + caughtError && caughtError.message, + `The parameter "maxMessageCount" should be of type "number"` + ); + }); + + it("PeekBySequenceNumber: Wrong type sequenceNumber in SessionReceiver", async function(): Promise< + void + > { + let caughtError: Error | undefined; + try { + await receiver.diagnostics.peekBySequenceNumber("somestring" as any); + } catch (error) { + caughtError = error; + } + should.equal(caughtError && caughtError.name, "TypeError"); + should.equal( + caughtError && caughtError.message, + `The parameter "fromSequenceNumber" should be of type "Long"` + ); + }); + + it("PeekBySequenceNumber: Missing sequenceNumber in SessionReceiver", async function(): Promise< + void + > { + let caughtError: Error | undefined; + try { + await receiver.diagnostics.peekBySequenceNumber(undefined as any); + } catch (error) { + caughtError = error; + } + should.equal(caughtError && caughtError.name, "TypeError"); + should.equal(caughtError && caughtError.message, `Missing parameter "fromSequenceNumber"`); + }); + + it("RegisterMessageHandler: Missing onMessage in SessionReceiver", async function(): Promise< + void + > { + let caughtError: Error | undefined; + try { + await receiver.subscribe(undefined as any, undefined as any); + } catch (error) { + caughtError = error; + } + should.equal(caughtError && caughtError.name, "TypeError"); + should.equal(caughtError && caughtError.message, `Invalid "MessageHandlers" provided.`); + }); + + it("RegisterMessageHandler: Wrong type for onMessage in SessionReceiver", async function(): Promise< + void + > { + let caughtError: Error | undefined; + try { + await receiver.subscribe("somestring" as any, "somethingelse" as any); + } catch (error) { + caughtError = error; + } + should.equal(caughtError && caughtError.name, "TypeError"); + should.equal(caughtError && caughtError.message, `Invalid "MessageHandlers" provided.`); + }); + // #RevisitCommentedTestsAfterTheSingleClientAPI + // The following 2 tests didn't make sense for the current handler type. That being said, equivalent tests for current API need to be added. + // it("RegisterMessageHandler: Missing onError in SessionReceiver", async function(): Promise { + // let caughtError: Error | undefined; + // try { + // await receiverClient.registerMessageHandler(async () => { + // /** */ + // }, undefined as any); + // } catch (error) { + // caughtError = error; + // } + // should.equal(caughtError && caughtError.name, "TypeError"); + // should.equal(caughtError && caughtError.message, `Missing parameter "onError"`); + // }); + + // it("RegisterMessageHandler: Wrong type for onError in SessionReceiver", async function(): Promise< + // void + // > { + // let caughtError: Error | undefined; + // try { + // await receiverClient.registerMessageHandler(async () => { + // /** */ + // }, "somethingelse" as any); + // } catch (error) { + // caughtError = error; + // } + // should.equal(caughtError && caughtError.name, "TypeError"); + // should.equal( + // caughtError && caughtError.message, + // `The parameter 'onError' must be of type 'function'.` + // ); + // }); + + it("ReceiveDeferredMessage: Wrong type sequenceNumber in SessionReceiver", async function(): Promise< + void + > { + let caughtError: Error | undefined; + try { + await receiver.receiveDeferredMessage("somestring" as any); + } catch (error) { + caughtError = error; + } + should.equal(caughtError && caughtError.name, "TypeError"); + should.equal( + caughtError && caughtError.message, + `The parameter "sequenceNumber" should be of type "Long"` + ); + }); + + it("ReceiveDeferredMessage: Missing sequenceNumber in SessionReceiver", async function(): Promise< + void + > { + let caughtError: Error | undefined; + try { + await receiver.receiveDeferredMessage(undefined as any); + } catch (error) { + caughtError = error; + } + should.equal(caughtError && caughtError.name, "TypeError"); + should.equal(caughtError && caughtError.message, `Missing parameter "sequenceNumber"`); + }); + + it("ReceiveDeferredMessages: Wrong type sequenceNumbers in SessionReceiver", async function(): Promise< + void + > { + let caughtError: Error | undefined; + try { + await receiver.receiveDeferredMessages(["somestring"] as any); + } catch (error) { + caughtError = error; + } + should.equal(caughtError && caughtError.name, "TypeError"); + should.equal( + caughtError && caughtError.message, + `The parameter "sequenceNumbers" should be an array of type "Long"` + ); + }); + + it("ReceiveDeferredMessages: Missing sequenceNumbers in SessionReceiver", async function(): Promise< + void + > { + let caughtError: Error | undefined; + try { + await receiver.receiveDeferredMessages(undefined as any); + } catch (error) { + caughtError = error; + } + should.equal(caughtError && caughtError.name, "TypeError"); + should.equal(caughtError && caughtError.message, `Missing parameter "sequenceNumbers"`); + }); }); // #RevisitCommentedTestsAfterTheSingleClientAPI - // Reason for commenting the following 2 tests - // `ReceiveMode` is now being passed in the Client - and this test is covered in the newly added `SessionReceiver: Throws error if created a client with invalid receiveMode` - // Supposed to be reverted and made changes accordingly once the Toplevel Client is added. - // it("SessionReceiver: Missing ReceiveMode", async function(): Promise { - // receiverClient = new ServiceBusReceiverClient( - // { - // queueName: EntityNames.QUEUE_NAME_SESSION, - // connectionString: getEnvVars()["SERVICEBUS_CONNECTION_STRING"] - // }, - // undefined as any, - // { - // id: TestMessage.sessionId + // These tests are exactly same as the previous describe block - session vs non-session. + // Since the current 2-client API version doesn't differentiate between session vs session (w.r.t the methods), there is no need for duplication. + // This is subject to change when the top-level client is implemented. + // describe("Invalid parameters in Receiver #RunInBrowser", function(): void { + // let receiver: InternalReceiver; + // let receiverClient: QueueClient; + + // // Since, the below tests never actually make use of any AMQP links, there is no need to create + // // new sender/receiver clients before each test. Doing it once for each describe block. + // before(async () => { + // createServiceBusClient(); + // const clients = await getSenderReceiverClients( + // sbClient, + // TestClientType.PartitionedQueue, + // TestClientType.PartitionedQueue + // ); + + // const sender = clients.senderClient.createSender(); + // await sender.send(TestMessage.getSample()); + + // receiverClient = clients.receiverClient; + // receiver = receiverClient.createReceiver(ReceiveMode.peekLock); + // }); + + // after(async () => { + // await sbClient.close(); + // }); + + // it("Receiver: Missing ReceiveMode", async function(): Promise { + // await receiver.close(); + // receiver = receiverClient.createReceiver(undefined as any); + // should.equal( + // receiver.receiveMode, + // ReceiveMode.peekLock, + // "Default receiveMode not set when receiveMode not provided to constructor." + // ); + // }); + + // it("Receiver: Invalid ReceiveMode", async function(): Promise { + // await receiver.close(); + // receiver = receiverClient.createReceiver(123 as any); + // should.equal( + // receiver.receiveMode, + // ReceiveMode.peekLock, + // "Default receiveMode not set when receiveMode not provided to constructor." + // ); + // }); + + // it("RegisterMessageHandler: Missing onMessage in Receiver", async function(): Promise { + // let caughtError: Error | undefined; + // try { + // await receiver.registerMessageHandler(undefined as any, undefined as any); + // } catch (error) { + // caughtError = error; // } - // ) as any; - // should.equal( - // receiverClient.receiveMode, - // ReceiveMode.peekLock, - // "Default receiveMode not set when receiveMode not provided to constructor." - // ); - // }); - - // it("SessionReceiver: Invalid ReceiveMode", async function(): Promise { - // receiverClient = new ServiceBusReceiverClient( - // { - // queueName: EntityNames.QUEUE_NAME_SESSION, - // connectionString: getEnvVars()["SERVICEBUS_CONNECTION_STRING"] - // }, - // 123 as any, - // { - // id: TestMessage.sessionId + // should.equal(caughtError && caughtError.name, "TypeError"); + // should.equal(caughtError && caughtError.message, `Missing parameter "onMessage"`); + // }); + + // it("RegisterMessageHandler: Wrong type for onMessage in Receiver", async function(): Promise< + // void + // > { + // let caughtError: Error | undefined; + // try { + // await receiver.registerMessageHandler("somestring" as any, "somethingelse" as any); + // } catch (error) { + // caughtError = error; // } - // ) as any; - // should.equal( - // receiverClient.receiveMode, - // ReceiveMode.peekLock, - // "Default receiveMode not set when receiveMode not provided to constructor." - // ); - // }); - - it("SessionReceiver: Throws error if created a client with invalid receiveMode", async function(): Promise< - void - > { - let errorCaught: string = ""; - try { - receiverClient = new ServiceBusReceiverClient( - { - queueName: EntityNames.QUEUE_NAME_SESSION, - connectionString: getEnvVars()["SERVICEBUS_CONNECTION_STRING"] - }, - 123 as any, - { - id: TestMessage.sessionId - } - ) as any; - } catch (error) { - errorCaught = error.message; - } - should.equal( - errorCaught, - "Invalid receiveMode provided", - "Did not throw error if created a client with invalid receiveMode." - ); - }); - - it("Peek: Invalid maxMessageCount in SessionReceiver", async function(): Promise { - const peekedResults = await receiverClient.diagnostics.peek(-100); - should.equal(peekedResults.length, 0); - }); - - it("Peek: Wrong type maxMessageCount in SessionReceiver", async function(): Promise { - let caughtError: Error | undefined; - try { - await receiverClient.diagnostics.peek("somestring" as any); - } catch (error) { - caughtError = error; - } - should.equal(caughtError && caughtError.name, "TypeError"); - should.equal( - caughtError && caughtError.message, - `The parameter "maxMessageCount" should be of type "number"` - ); - }); - - it("PeekBySequenceNumber: Invalid maxMessageCount in SessionReceiver", async function(): Promise< - void - > { - const peekedResults = await receiverClient.diagnostics.peekBySequenceNumber(Long.ZERO, -100); - should.equal(peekedResults.length, 0); - }); - - it("PeekBySequenceNumber: Wrong type maxMessageCount in SessionReceiver", async function(): Promise< - void - > { - let caughtError: Error | undefined; - try { - await receiverClient.diagnostics.peekBySequenceNumber(Long.ZERO, "somestring" as any); - } catch (error) { - caughtError = error; - } - should.equal(caughtError && caughtError.name, "TypeError"); - should.equal( - caughtError && caughtError.message, - `The parameter "maxMessageCount" should be of type "number"` - ); - }); - - it("PeekBySequenceNumber: Wrong type sequenceNumber in SessionReceiver", async function(): Promise< - void - > { - let caughtError: Error | undefined; - try { - await receiverClient.diagnostics.peekBySequenceNumber("somestring" as any); - } catch (error) { - caughtError = error; - } - should.equal(caughtError && caughtError.name, "TypeError"); - should.equal( - caughtError && caughtError.message, - `The parameter "fromSequenceNumber" should be of type "Long"` - ); - }); - - it("PeekBySequenceNumber: Missing sequenceNumber in SessionReceiver", async function(): Promise< - void - > { - let caughtError: Error | undefined; - try { - await receiverClient.diagnostics.peekBySequenceNumber(undefined as any); - } catch (error) { - caughtError = error; - } - should.equal(caughtError && caughtError.name, "TypeError"); - should.equal(caughtError && caughtError.message, `Missing parameter "fromSequenceNumber"`); - }); - - it("RegisterMessageHandler: Missing onMessage in SessionReceiver", async function(): Promise< - void - > { - let caughtError: Error | undefined; - try { - await receiverClient.subscribe(undefined as any, undefined as any); - } catch (error) { - caughtError = error; - } - should.equal(caughtError && caughtError.name, "TypeError"); - should.equal(caughtError && caughtError.message, `Invalid "MessageHandlers" provided.`); - }); - - it("RegisterMessageHandler: Wrong type for onMessage in SessionReceiver", async function(): Promise< - void - > { - let caughtError: Error | undefined; - try { - await receiverClient.subscribe("somestring" as any, "somethingelse" as any); - } catch (error) { - caughtError = error; - } - should.equal(caughtError && caughtError.name, "TypeError"); - should.equal(caughtError && caughtError.message, `Invalid "MessageHandlers" provided.`); - }); - // #RevisitCommentedTestsAfterTheSingleClientAPI - // The following 2 tests didn't make sense for the current handler type. That being said, equivalent tests for current API need to be added. - // it("RegisterMessageHandler: Missing onError in SessionReceiver", async function(): Promise { - // let caughtError: Error | undefined; - // try { - // await receiverClient.registerMessageHandler(async () => { - // /** */ - // }, undefined as any); - // } catch (error) { - // caughtError = error; - // } - // should.equal(caughtError && caughtError.name, "TypeError"); - // should.equal(caughtError && caughtError.message, `Missing parameter "onError"`); - // }); - - // it("RegisterMessageHandler: Wrong type for onError in SessionReceiver", async function(): Promise< - // void - // > { - // let caughtError: Error | undefined; - // try { - // await receiverClient.registerMessageHandler(async () => { - // /** */ - // }, "somethingelse" as any); - // } catch (error) { - // caughtError = error; - // } - // should.equal(caughtError && caughtError.name, "TypeError"); - // should.equal( - // caughtError && caughtError.message, - // `The parameter 'onError' must be of type 'function'.` - // ); + // should.equal(caughtError && caughtError.name, "TypeError"); + // should.equal( + // caughtError && caughtError.message, + // `The parameter 'onMessage' must be of type 'function'.` + // ); + // }); + + // it("RegisterMessageHandler: Missing onError in Receiver", async function(): Promise { + // let caughtError: Error | undefined; + // try { + // await receiver.registerMessageHandler(async () => { + // /** */ + // }, undefined as any); + // } catch (error) { + // caughtError = error; + // } + // should.equal(caughtError && caughtError.name, "TypeError"); + // should.equal(caughtError && caughtError.message, `Missing parameter "onError"`); + // }); + + // it("RegisterMessageHandler: Wrong type for onError in Receiver", async function(): Promise { + // let caughtError: Error | undefined; + // try { + // await receiver.registerMessageHandler(async () => { + // /** */ + // }, "somethingelse" as any); + // } catch (error) { + // caughtError = error; + // } + // should.equal(caughtError && caughtError.name, "TypeError"); + // should.equal( + // caughtError && caughtError.message, + // `The parameter 'onError' must be of type 'function'.` + // ); + // }); + + // it("ReceiveDeferredMessage: Wrong type sequenceNumber in Receiver", async function(): Promise< + // void + // > { + // let caughtError: Error | undefined; + // try { + // await receiver.receiveDeferredMessage("somestring" as any); + // } catch (error) { + // caughtError = error; + // } + // should.equal(caughtError && caughtError.name, "TypeError"); + // should.equal( + // caughtError && caughtError.message, + // `The parameter "sequenceNumber" should be of type "Long"` + // ); + // }); + + // it("ReceiveDeferredMessage: Missing sequenceNumber in Receiver", async function(): Promise { + // let caughtError: Error | undefined; + // try { + // await receiver.receiveDeferredMessage(undefined as any); + // } catch (error) { + // caughtError = error; + // } + // should.equal(caughtError && caughtError.name, "TypeError"); + // should.equal(caughtError && caughtError.message, `Missing parameter "sequenceNumber"`); + // }); + + // it("ReceiveDeferredMessages: Wrong type sequenceNumbers in Receiver", async function(): Promise< + // void + // > { + // let caughtError: Error | undefined; + // try { + // await receiver.receiveDeferredMessages(["somestring"] as any); + // } catch (error) { + // caughtError = error; + // } + // should.equal(caughtError && caughtError.name, "TypeError"); + // should.equal( + // caughtError && caughtError.message, + // `The parameter "sequenceNumbers" should be an array of type "Long"` + // ); + // }); + + // it("ReceiveDeferredMessages: Missing sequenceNumbers in Receiver", async function(): Promise< + // void + // > { + // let caughtError: Error | undefined; + // try { + // await receiver.receiveDeferredMessages(undefined as any); + // } catch (error) { + // caughtError = error; + // } + // should.equal(caughtError && caughtError.name, "TypeError"); + // should.equal(caughtError && caughtError.message, `Missing parameter "sequenceNumbers"`); + // }); + + // it("RenewMessageLock: Missing lockTokenOrMessage in Receiver", async function(): Promise { + // let caughtError: Error | undefined; + // try { + // await (receiver).renewMessageLock(undefined as any); + // } catch (error) { + // caughtError = error; + // } + // should.equal(caughtError && caughtError.name, "TypeError"); + // should.equal(caughtError && caughtError.message, `Missing parameter "lockTokenOrMessage"`); + // }); + + // it("RenewMessageLock: Invalid string lockToken in Receiver", async function(): Promise { + // let caughtError: Error | undefined; + // try { + // await (receiver).renewMessageLock("string-which-is-not-uuid"); + // } catch (error) { + // caughtError = error; + // } + // should.equal( + // caughtError && caughtError.message, + // `Not a valid UUID string: string-which-is-not-uuid` + // ); + // }); + + // it("RenewMessageLock: Invalid message lockToken in Receiver", async function(): Promise { + // let caughtError: Error | undefined; + // try { + // const [receivedMsg] = await receiver.receiveMessages(1); + // if (!receivedMsg) { + // throw new Error("Message not received to renew lock on."); + // } + // (receivedMsg).lockToken = "string-which-is-not-uuid"; + // await (receiver).renewMessageLock(receivedMsg); + // } catch (error) { + // caughtError = error; + // } + // should.equal( + // caughtError && caughtError.message, + // `Not a valid UUID string: string-which-is-not-uuid` + // ); + // }); // }); - it("ReceiveDeferredMessage: Wrong type sequenceNumber in SessionReceiver", async function(): Promise< - void - > { - let caughtError: Error | undefined; - try { - await receiverClient.receiveDeferredMessage("somestring" as any); - } catch (error) { - caughtError = error; - } - should.equal(caughtError && caughtError.name, "TypeError"); - should.equal( - caughtError && caughtError.message, - `The parameter "sequenceNumber" should be of type "Long"` - ); - }); - - it("ReceiveDeferredMessage: Missing sequenceNumber in SessionReceiver", async function(): Promise< - void - > { - let caughtError: Error | undefined; - try { - await receiverClient.receiveDeferredMessage(undefined as any); - } catch (error) { - caughtError = error; - } - should.equal(caughtError && caughtError.name, "TypeError"); - should.equal(caughtError && caughtError.message, `Missing parameter "sequenceNumber"`); - }); - - it("ReceiveDeferredMessages: Wrong type sequenceNumbers in SessionReceiver", async function(): Promise< - void - > { - let caughtError: Error | undefined; - try { - await receiverClient.receiveDeferredMessages(["somestring"] as any); - } catch (error) { - caughtError = error; - } - should.equal(caughtError && caughtError.name, "TypeError"); - should.equal( - caughtError && caughtError.message, - `The parameter "sequenceNumbers" should be an array of type "Long"` - ); - }); - - it("ReceiveDeferredMessages: Missing sequenceNumbers in SessionReceiver", async function(): Promise< - void - > { - let caughtError: Error | undefined; - try { - await receiverClient.receiveDeferredMessages(undefined as any); - } catch (error) { - caughtError = error; - } - should.equal(caughtError && caughtError.name, "TypeError"); - should.equal(caughtError && caughtError.message, `Missing parameter "sequenceNumbers"`); - }); -}); - -// #RevisitCommentedTestsAfterTheSingleClientAPI -// These tests are exactly same as the previous describe block - session vs non-session. -// Since the current 2-client API version doesn't differentiate between session vs session (w.r.t the methods), there is no need for duplication. -// This is subject to change when the top-level client is implemented. -// describe("Invalid parameters in Receiver #RunInBrowser", function(): void { -// let receiver: InternalReceiver; -// let receiverClient: QueueClient; - -// // Since, the below tests never actually make use of any AMQP links, there is no need to create -// // new sender/receiver clients before each test. Doing it once for each describe block. -// before(async () => { -// createServiceBusClient(); -// const clients = await getSenderReceiverClients( -// sbClient, -// TestClientType.PartitionedQueue, -// TestClientType.PartitionedQueue -// ); - -// const sender = clients.senderClient.createSender(); -// await sender.send(TestMessage.getSample()); - -// receiverClient = clients.receiverClient; -// receiver = receiverClient.createReceiver(ReceiveMode.peekLock); -// }); - -// after(async () => { -// await sbClient.close(); -// }); - -// it("Receiver: Missing ReceiveMode", async function(): Promise { -// await receiver.close(); -// receiver = receiverClient.createReceiver(undefined as any); -// should.equal( -// receiver.receiveMode, -// ReceiveMode.peekLock, -// "Default receiveMode not set when receiveMode not provided to constructor." -// ); -// }); - -// it("Receiver: Invalid ReceiveMode", async function(): Promise { -// await receiver.close(); -// receiver = receiverClient.createReceiver(123 as any); -// should.equal( -// receiver.receiveMode, -// ReceiveMode.peekLock, -// "Default receiveMode not set when receiveMode not provided to constructor." -// ); -// }); - -// it("RegisterMessageHandler: Missing onMessage in Receiver", async function(): Promise { -// let caughtError: Error | undefined; -// try { -// await receiver.registerMessageHandler(undefined as any, undefined as any); -// } catch (error) { -// caughtError = error; -// } -// should.equal(caughtError && caughtError.name, "TypeError"); -// should.equal(caughtError && caughtError.message, `Missing parameter "onMessage"`); -// }); - -// it("RegisterMessageHandler: Wrong type for onMessage in Receiver", async function(): Promise< -// void -// > { -// let caughtError: Error | undefined; -// try { -// await receiver.registerMessageHandler("somestring" as any, "somethingelse" as any); -// } catch (error) { -// caughtError = error; -// } -// should.equal(caughtError && caughtError.name, "TypeError"); -// should.equal( -// caughtError && caughtError.message, -// `The parameter 'onMessage' must be of type 'function'.` -// ); -// }); - -// it("RegisterMessageHandler: Missing onError in Receiver", async function(): Promise { -// let caughtError: Error | undefined; -// try { -// await receiver.registerMessageHandler(async () => { -// /** */ -// }, undefined as any); -// } catch (error) { -// caughtError = error; -// } -// should.equal(caughtError && caughtError.name, "TypeError"); -// should.equal(caughtError && caughtError.message, `Missing parameter "onError"`); -// }); - -// it("RegisterMessageHandler: Wrong type for onError in Receiver", async function(): Promise { -// let caughtError: Error | undefined; -// try { -// await receiver.registerMessageHandler(async () => { -// /** */ -// }, "somethingelse" as any); -// } catch (error) { -// caughtError = error; -// } -// should.equal(caughtError && caughtError.name, "TypeError"); -// should.equal( -// caughtError && caughtError.message, -// `The parameter 'onError' must be of type 'function'.` -// ); -// }); - -// it("ReceiveDeferredMessage: Wrong type sequenceNumber in Receiver", async function(): Promise< -// void -// > { -// let caughtError: Error | undefined; -// try { -// await receiver.receiveDeferredMessage("somestring" as any); -// } catch (error) { -// caughtError = error; -// } -// should.equal(caughtError && caughtError.name, "TypeError"); -// should.equal( -// caughtError && caughtError.message, -// `The parameter "sequenceNumber" should be of type "Long"` -// ); -// }); - -// it("ReceiveDeferredMessage: Missing sequenceNumber in Receiver", async function(): Promise { -// let caughtError: Error | undefined; -// try { -// await receiver.receiveDeferredMessage(undefined as any); -// } catch (error) { -// caughtError = error; -// } -// should.equal(caughtError && caughtError.name, "TypeError"); -// should.equal(caughtError && caughtError.message, `Missing parameter "sequenceNumber"`); -// }); - -// it("ReceiveDeferredMessages: Wrong type sequenceNumbers in Receiver", async function(): Promise< -// void -// > { -// let caughtError: Error | undefined; -// try { -// await receiver.receiveDeferredMessages(["somestring"] as any); -// } catch (error) { -// caughtError = error; -// } -// should.equal(caughtError && caughtError.name, "TypeError"); -// should.equal( -// caughtError && caughtError.message, -// `The parameter "sequenceNumbers" should be an array of type "Long"` -// ); -// }); - -// it("ReceiveDeferredMessages: Missing sequenceNumbers in Receiver", async function(): Promise< -// void -// > { -// let caughtError: Error | undefined; -// try { -// await receiver.receiveDeferredMessages(undefined as any); -// } catch (error) { -// caughtError = error; -// } -// should.equal(caughtError && caughtError.name, "TypeError"); -// should.equal(caughtError && caughtError.message, `Missing parameter "sequenceNumbers"`); -// }); - -// it("RenewMessageLock: Missing lockTokenOrMessage in Receiver", async function(): Promise { -// let caughtError: Error | undefined; -// try { -// await (receiver).renewMessageLock(undefined as any); -// } catch (error) { -// caughtError = error; -// } -// should.equal(caughtError && caughtError.name, "TypeError"); -// should.equal(caughtError && caughtError.message, `Missing parameter "lockTokenOrMessage"`); -// }); - -// it("RenewMessageLock: Invalid string lockToken in Receiver", async function(): Promise { -// let caughtError: Error | undefined; -// try { -// await (receiver).renewMessageLock("string-which-is-not-uuid"); -// } catch (error) { -// caughtError = error; -// } -// should.equal( -// caughtError && caughtError.message, -// `Not a valid UUID string: string-which-is-not-uuid` -// ); -// }); - -// it("RenewMessageLock: Invalid message lockToken in Receiver", async function(): Promise { -// let caughtError: Error | undefined; -// try { -// const [receivedMsg] = await receiver.receiveMessages(1); -// if (!receivedMsg) { -// throw new Error("Message not received to renew lock on."); -// } -// (receivedMsg).lockToken = "string-which-is-not-uuid"; -// await (receiver).renewMessageLock(receivedMsg); -// } catch (error) { -// caughtError = error; -// } -// should.equal( -// caughtError && caughtError.message, -// `Not a valid UUID string: string-which-is-not-uuid` -// ); -// }); -// }); - -describe("Invalid parameters in Sender #RunInBrowser", function(): void { - let sender: ServiceBusSenderClient; - - // Since, the below tests never actually make use of any AMQP links, there is no need to create - // new sender/receiver clients before each test. Doing it once for each describe block. - before(async () => { - const clients = await getSenderReceiverClients(TestClientType.PartitionedQueue, "peekLock"); - - sender = clients.senderClient; - }); - - after(async () => { - await sender.close(); - }); - - it("Send: Missing message in Sender", async function(): Promise { - let caughtError: Error | undefined; - try { - await sender.send(undefined as any); - } catch (error) { - caughtError = error; - } - should.equal(caughtError && caughtError.name, "TypeError"); - should.equal(caughtError && caughtError.message, `Missing parameter "message"`); - }); - - it("Sendbatch: Missing messages in Sender", async function(): Promise { - let caughtError: Error | undefined; - try { - await sender.sendBatch(undefined as any); - } catch (error) { - caughtError = error; - } - should.equal(caughtError && caughtError.name, "TypeError"); - should.equal(caughtError && caughtError.message, `Missing parameter "messages"`); - }); - - it("ScheduledMessage: Missing date in Sender", async function(): Promise { - let caughtError: Error | undefined; - try { - await sender.scheduleMessage(undefined as any, undefined as any); - } catch (error) { - caughtError = error; - } - should.equal(caughtError && caughtError.name, "TypeError"); - should.equal(caughtError && caughtError.message, `Missing parameter "scheduledEnqueueTimeUtc"`); - }); + describe("Invalid parameters in Sender #RunInBrowser", function(): void { + let sender: Sender; - it("ScheduledMessage: Missing message in Sender", async function(): Promise { - let caughtError: Error | undefined; - try { - await sender.scheduleMessage(new Date(), undefined as any); - } catch (error) { - caughtError = error; - } - should.equal(caughtError && caughtError.name, "TypeError"); - should.equal(caughtError && caughtError.message, `Missing parameter "message"`); - }); - - it("ScheduledMessages: Missing date in Sender", async function(): Promise { - let caughtError: Error | undefined; - try { - await sender.scheduleMessages(undefined as any, undefined as any); - } catch (error) { - caughtError = error; - } - should.equal(caughtError && caughtError.name, "TypeError"); - should.equal(caughtError && caughtError.message, `Missing parameter "scheduledEnqueueTimeUtc"`); - }); - - it("ScheduledMessages: Missing messages in Sender", async function(): Promise { - let caughtError: Error | undefined; - try { - await sender.scheduleMessages(new Date(), undefined as any); - } catch (error) { - caughtError = error; - } - should.equal(caughtError && caughtError.name, "TypeError"); - should.equal(caughtError && caughtError.message, `Missing parameter "messages"`); - }); - - it("CancelScheduledMessage: Wrong type sequenceNumber in Sender", async function(): Promise< - void - > { - let caughtError: Error | undefined; - try { - await sender.cancelScheduledMessage("somestring" as any); - } catch (error) { - caughtError = error; - } - should.equal(caughtError && caughtError.name, "TypeError"); - should.equal( - caughtError && caughtError.message, - `The parameter "sequenceNumber" should be of type "Long"` - ); - }); - - it("CancelScheduledMessage: Missing sequenceNumber in Sender", async function(): Promise { - let caughtError: Error | undefined; - try { - await sender.cancelScheduledMessage(undefined as any); - } catch (error) { - caughtError = error; - } - should.equal(caughtError && caughtError.name, "TypeError"); - should.equal(caughtError && caughtError.message, `Missing parameter "sequenceNumber"`); - }); - - it("CancelScheduledMessages: Wrong type sequenceNumbers in Sender", async function(): Promise< - void - > { - let caughtError: Error | undefined; - try { - await sender.cancelScheduledMessages(["somestring"] as any); - } catch (error) { - caughtError = error; - } - should.equal(caughtError && caughtError.name, "TypeError"); - should.equal( - caughtError && caughtError.message, - `The parameter "sequenceNumbers" should be an array of type "Long"` - ); - }); + // Since, the below tests never actually make use of any AMQP links, there is no need to create + // new sender/receiver clients before each test. Doing it once for each describe block. + before(async () => { + const { queue } = await serviceBusClient.test.createTestEntities( + TestClientType.PartitionedQueue + ); - it("CancelScheduledMessages: Missing sequenceNumbers in Sender", async function(): Promise { - let caughtError: Error | undefined; - try { - await sender.cancelScheduledMessages(undefined as any); - } catch (error) { - caughtError = error; - } - should.equal(caughtError && caughtError.name, "TypeError"); - should.equal(caughtError && caughtError.message, `Missing parameter "sequenceNumbers"`); + //const clients = await getSenderReceiverClients(TestClientType.PartitionedQueue, "peekLock"); + sender = serviceBusClient.test.addToCleanup(serviceBusClient.getSender(queue!)); + }); + + after(() => { + return serviceBusClient.test.afterEach(); + }); + + it("Send: Missing message in Sender", async function(): Promise { + let caughtError: Error | undefined; + try { + await sender.send(undefined as any); + } catch (error) { + caughtError = error; + } + should.equal(caughtError && caughtError.name, "TypeError"); + should.equal(caughtError && caughtError.message, `Missing parameter "message"`); + }); + + it("Sendbatch: Missing messages in Sender", async function(): Promise { + let caughtError: Error | undefined; + try { + await sender.sendBatch(undefined as any); + } catch (error) { + caughtError = error; + } + should.equal(caughtError && caughtError.name, "TypeError"); + should.equal(caughtError && caughtError.message, `Missing parameter "messages"`); + }); + + it("ScheduledMessage: Missing date in Sender", async function(): Promise { + let caughtError: Error | undefined; + try { + await sender.scheduleMessage(undefined as any, undefined as any); + } catch (error) { + caughtError = error; + } + should.equal(caughtError && caughtError.name, "TypeError"); + should.equal( + caughtError && caughtError.message, + `Missing parameter "scheduledEnqueueTimeUtc"` + ); + }); + + it("ScheduledMessage: Missing message in Sender", async function(): Promise { + let caughtError: Error | undefined; + try { + await sender.scheduleMessage(new Date(), undefined as any); + } catch (error) { + caughtError = error; + } + should.equal(caughtError && caughtError.name, "TypeError"); + should.equal(caughtError && caughtError.message, `Missing parameter "message"`); + }); + + it("ScheduledMessages: Missing date in Sender", async function(): Promise { + let caughtError: Error | undefined; + try { + await sender.scheduleMessages(undefined as any, undefined as any); + } catch (error) { + caughtError = error; + } + should.equal(caughtError && caughtError.name, "TypeError"); + should.equal( + caughtError && caughtError.message, + `Missing parameter "scheduledEnqueueTimeUtc"` + ); + }); + + it("ScheduledMessages: Missing messages in Sender", async function(): Promise { + let caughtError: Error | undefined; + try { + await sender.scheduleMessages(new Date(), undefined as any); + } catch (error) { + caughtError = error; + } + should.equal(caughtError && caughtError.name, "TypeError"); + should.equal(caughtError && caughtError.message, `Missing parameter "messages"`); + }); + + it("CancelScheduledMessage: Wrong type sequenceNumber in Sender", async function(): Promise< + void + > { + let caughtError: Error | undefined; + try { + await sender.cancelScheduledMessage("somestring" as any); + } catch (error) { + caughtError = error; + } + should.equal(caughtError && caughtError.name, "TypeError"); + should.equal( + caughtError && caughtError.message, + `The parameter "sequenceNumber" should be of type "Long"` + ); + }); + + it("CancelScheduledMessage: Missing sequenceNumber in Sender", async function(): Promise { + let caughtError: Error | undefined; + try { + await sender.cancelScheduledMessage(undefined as any); + } catch (error) { + caughtError = error; + } + should.equal(caughtError && caughtError.name, "TypeError"); + should.equal(caughtError && caughtError.message, `Missing parameter "sequenceNumber"`); + }); + + it("CancelScheduledMessages: Wrong type sequenceNumbers in Sender", async function(): Promise< + void + > { + let caughtError: Error | undefined; + try { + await sender.cancelScheduledMessages(["somestring"] as any); + } catch (error) { + caughtError = error; + } + should.equal(caughtError && caughtError.name, "TypeError"); + should.equal( + caughtError && caughtError.message, + `The parameter "sequenceNumbers" should be an array of type "Long"` + ); + }); + + it("CancelScheduledMessages: Missing sequenceNumbers in Sender", async function(): Promise< + void + > { + let caughtError: Error | undefined; + try { + await sender.cancelScheduledMessages(undefined as any); + } catch (error) { + caughtError = error; + } + should.equal(caughtError && caughtError.name, "TypeError"); + should.equal(caughtError && caughtError.message, `Missing parameter "sequenceNumbers"`); + }); }); }); diff --git a/sdk/servicebus/service-bus/test/receiveAndDeleteMode.spec.ts b/sdk/servicebus/service-bus/test/receiveAndDeleteMode.spec.ts index 536a7b288d3f..bab8b16ea96a 100644 --- a/sdk/servicebus/service-bus/test/receiveAndDeleteMode.spec.ts +++ b/sdk/servicebus/service-bus/test/receiveAndDeleteMode.spec.ts @@ -6,114 +6,91 @@ const should = chai.should(); const expect = chai.expect; import chaiAsPromised from "chai-as-promised"; chai.use(chaiAsPromised); -import { - SendableMessageInfo, - ServiceBusSenderClient, - ContextWithSettlement, - ReceivedMessage, - ServiceBusMessage -} from "../src"; +import { SendableMessageInfo, ContextWithSettlement, ReceivedMessage, Receiver } from "../src"; -import { - TestMessage, - getSenderReceiverClients, - TestClientType, - purge, - checkWithTimeout, - isSessionfulEntity, - ReceiverClientTypeForUserT, - ReceiverClientTypeForUser -} from "./utils/testUtils"; +import { TestMessage, TestClientType, checkWithTimeout } from "./utils/testUtils"; import { getErrorMessageNotSupportedInReceiveAndDeleteMode } from "../src/util/errors"; -import { NonSessionReceiver } from "../src/serviceBusReceiverClient"; +import { Sender } from "../src/sender"; +import { + ServiceBusClientForTests, + createServiceBusClientForTests, + testPeekMsgsLength +} from "./utils/testutils2"; import { DispositionType } from "../src/serviceBusMessage"; -async function testPeekMsgsLength( - client: ReceiverClientTypeForUser, - expectedPeekLength: number -): Promise { - const peekedMsgs = await client.diagnostics.peek(expectedPeekLength + 1); - should.equal( - peekedMsgs.length, - expectedPeekLength, - "Unexpected number of msgs found when peeking" - ); -} - let errorWasThrown: boolean; -let senderClient: ServiceBusSenderClient; -let receiverClient: ReceiverClientTypeForUserT<"receiveAndDelete">; - -async function beforeEachTest( - entityType: TestClientType, - receiveMode?: "peekLock" | "receiveAndDelete" -): Promise { - let clients; - receiveMode = receiveMode === "peekLock" ? "peekLock" : "receiveAndDelete"; - if (isSessionfulEntity(entityType)) { - clients = await getSenderReceiverClients(entityType, receiveMode, undefined, { - id: TestMessage.sessionId - }); - } else { - clients = await getSenderReceiverClients(entityType, receiveMode); - } - senderClient = clients.senderClient; - receiverClient = clients.receiverClient; - - await purge(receiverClient); - const peekedMsgs = await receiverClient.diagnostics.peek(); - const receiverEntityType = receiverClient.entityType; - if (peekedMsgs.length) { - chai.assert.fail(`Please use an empty ${receiverEntityType} for integration testing`); - } - errorWasThrown = false; -} +describe("receive and delete", () => { + let senderClient: Sender; + let receiverClient: Receiver<{}>; + let serviceBusClient: ServiceBusClientForTests; -async function afterEachTest(): Promise { - await receiverClient.close(); - await senderClient.close(); -} + before(() => { + serviceBusClient = createServiceBusClientForTests(); + }); -describe("Batch Receiver in ReceiveAndDelete mode", function(): void { - afterEach(async () => { - await afterEachTest(); + after(() => { + return serviceBusClient.test.after(); }); - async function sendReceiveMsg(testMessages: SendableMessageInfo): Promise { - await senderClient.send(testMessages); - const msgs = (await receiverClient.receiveBatch(1)).messages; + async function beforeEachTest(entityType: TestClientType): Promise { + const entityNames = await serviceBusClient.test.createTestEntities(entityType); - should.equal(Array.isArray(msgs), true, "`ReceivedMessages` is not an array"); - should.equal(msgs.length, 1, "Unexpected number of messages"); - should.equal(msgs[0].body, testMessages.body, "MessageBody is different than expected"); - should.equal(msgs[0].messageId, testMessages.messageId, "MessageId is different than expected"); - should.equal(msgs[0].deliveryCount, 0, "DeliveryCount is different than expected"); - } + senderClient = serviceBusClient.test.addToCleanup( + serviceBusClient.getSender(entityNames.queue ?? entityNames.topic!) + ); + receiverClient = serviceBusClient.test.getReceiveAndDeleteReceiver(entityNames); - async function testNoSettlement(useSessions?: boolean): Promise { - const testMessages = useSessions ? TestMessage.getSessionSample() : TestMessage.getSample(); - await sendReceiveMsg(testMessages); + errorWasThrown = false; + } - await testPeekMsgsLength(receiverClient, 0); + function afterEachTest(): Promise { + return serviceBusClient.test.afterEach(); } - it("Partitioned Queue: No settlement of the message removes message #RunInBrowser", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedQueue); - await testNoSettlement(); - }); + describe("Batch Receiver in ReceiveAndDelete mode", function(): void { + afterEach(async () => { + await afterEachTest(); + }); - it("Partitioned Subscription: No settlement of the message removes message", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedSubscription); - await testNoSettlement(); - }); + async function sendReceiveMsg(testMessages: SendableMessageInfo): Promise { + await senderClient.send(testMessages); + const msgs = (await receiverClient.receiveBatch(1)).messages; + + should.equal(Array.isArray(msgs), true, "`ReceivedMessages` is not an array"); + should.equal(msgs.length, 1, "Unexpected number of messages"); + should.equal(msgs[0].body, testMessages.body, "MessageBody is different than expected"); + should.equal( + msgs[0].messageId, + testMessages.messageId, + "MessageId is different than expected" + ); + should.equal(msgs[0].deliveryCount, 0, "DeliveryCount is different than expected"); + } + + async function testNoSettlement(useSessions?: boolean): Promise { + const testMessages = useSessions ? TestMessage.getSessionSample() : TestMessage.getSample(); + await sendReceiveMsg(testMessages); + + await testPeekMsgsLength(receiverClient, 0); + } - /* it("Unpartitioned Queue: No settlement of the message removes message", async function(): Promise< + it("Partitioned Queue: No settlement of the message removes message #RunInBrowser", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedQueue); + await testNoSettlement(); + }); + + it("Partitioned Subscription: No settlement of the message removes message", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedSubscription); + await testNoSettlement(); + }); + + /* it("Unpartitioned Queue: No settlement of the message removes message", async function(): Promise< void > { await beforeEachTest(ClientType.UnpartitionedQueue, ClientType.UnpartitionedQueue); @@ -127,105 +104,112 @@ describe("Batch Receiver in ReceiveAndDelete mode", function(): void { await testNoSettlement(); });*/ - it("Partitioned Queue with Sessions: No settlement of the message removes message #RunInBrowser", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedQueueWithSessions); - await testNoSettlement(true); - }); - - it("Partitioned Subscription with Sessions: No settlement of the message removes message", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); - await testNoSettlement(true); - }); - - it("Unpartitioned Queue with Sessions: No settlement of the message removes message", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); - await testNoSettlement(true); - }); + it("Partitioned Queue with Sessions: No settlement of the message removes message #RunInBrowser", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedQueueWithSessions); + await testNoSettlement(true); + }); - it("Unpartitioned Subscription with Sessions: No settlement of the message removes message", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedSubscriptionWithSessions); - await testNoSettlement(true); - }); -}); + it("Partitioned Subscription with Sessions: No settlement of the message removes message", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); + await testNoSettlement(true); + }); -describe("Streaming Receiver in ReceiveAndDelete mode", function(): void { - let errorFromErrorHandler: Error | undefined; + it("Unpartitioned Queue with Sessions: No settlement of the message removes message", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); + await testNoSettlement(true); + }); - afterEach(async () => { - await afterEachTest(); + it("Unpartitioned Subscription with Sessions: No settlement of the message removes message", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedSubscriptionWithSessions); + await testNoSettlement(true); + }); }); - async function sendReceiveMsg( - testMessages: SendableMessageInfo, - autoCompleteFlag: boolean - ): Promise { - await senderClient.send(testMessages); + describe("Streaming Receiver in ReceiveAndDelete mode", function(): void { + let errorFromErrorHandler: Error | undefined; - const errors: string[] = []; - const receivedMsgs: ReceivedMessage[] = []; + afterEach(async () => { + await afterEachTest(); + }); - receiverClient.subscribe( - { - async processMessage(message: ReceivedMessage, context: {}): Promise { - receivedMsgs.push(message); + async function sendReceiveMsg( + testMessages: SendableMessageInfo, + autoCompleteFlag: boolean + ): Promise { + await senderClient.send(testMessages); + + const errors: string[] = []; + const receivedMsgs: ReceivedMessage[] = []; + + receiverClient.subscribe( + { + async processMessage(message: ReceivedMessage): Promise { + receivedMsgs.push(message); + }, + async processError(err: Error): Promise { + errors.push(err.message); + } }, - async processError(err: Error): Promise { - errors.push(err.message); - } - }, - { autoComplete: autoCompleteFlag } - ); - - const msgsCheck = await checkWithTimeout(() => receivedMsgs.length === 1); - should.equal(msgsCheck, true, "Could not receive the messages in expected time."); - - should.equal(receivedMsgs.length, 1, "Unexpected number of messages"); - should.equal(receivedMsgs[0].body, testMessages.body, "MessageBody is different than expected"); - should.equal( - receivedMsgs[0].messageId, - testMessages.messageId, - "MessageId is different than expected" - ); - - should.equal( - errorFromErrorHandler, - undefined, - errorFromErrorHandler && errorFromErrorHandler.message - ); + { autoComplete: autoCompleteFlag } + ); + + const msgsCheck = await checkWithTimeout(() => receivedMsgs.length === 1); + should.equal(msgsCheck, true, "Could not receive the messages in expected time."); + + should.equal(receivedMsgs.length, 1, "Unexpected number of messages"); + should.equal( + receivedMsgs[0].body, + testMessages.body, + "MessageBody is different than expected" + ); + should.equal( + receivedMsgs[0].messageId, + testMessages.messageId, + "MessageId is different than expected" + ); + + should.equal( + errorFromErrorHandler, + undefined, + errorFromErrorHandler && errorFromErrorHandler.message + ); - await testPeekMsgsLength(receiverClient, 0); - } + await testPeekMsgsLength(receiverClient, 0); + } - async function testNoSettlement(autoCompleteFlag: boolean, useSessions?: boolean): Promise { - const testMessages = useSessions ? TestMessage.getSessionSample() : TestMessage.getSample(); - await sendReceiveMsg(testMessages, autoCompleteFlag); + async function testNoSettlement( + autoCompleteFlag: boolean, + useSessions?: boolean + ): Promise { + const testMessages = useSessions ? TestMessage.getSessionSample() : TestMessage.getSample(); + await sendReceiveMsg(testMessages, autoCompleteFlag); - await testPeekMsgsLength(receiverClient, 0); - } + await testPeekMsgsLength(receiverClient, 0); + } - it("Partitioned Queue: With auto-complete enabled, no settlement of the message removes message", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedQueue); - await testNoSettlement(true); - }); + it("Partitioned Queue: With auto-complete enabled, no settlement of the message removes message", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedQueue); + await testNoSettlement(true); + }); - it("Partitioned Subscription: With auto-complete enabled, no settlement of the message removes message", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedSubscription); - await testNoSettlement(true); - }); + it("Partitioned Subscription: With auto-complete enabled, no settlement of the message removes message", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedSubscription); + await testNoSettlement(true); + }); - /* it("Unpartitioned Queue: With auto-complete enabled, no settlement of the message removes message", async function(): Promise< + /* it("Unpartitioned Queue: With auto-complete enabled, no settlement of the message removes message", async function(): Promise< void > { await beforeEachTest(ClientType.UnpartitionedQueue, ClientType.UnpartitionedQueue); @@ -239,49 +223,49 @@ describe("Streaming Receiver in ReceiveAndDelete mode", function(): void { await testNoSettlement(true); });*/ - it("Partitioned Queue with Sessions: With auto-complete enabled, no settlement of the message removes message", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedQueueWithSessions); - await testNoSettlement(true, true); - }); + it("Partitioned Queue with Sessions: With auto-complete enabled, no settlement of the message removes message", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedQueueWithSessions); + await testNoSettlement(true, true); + }); - it("Partitioned Subscription with Sessions: With auto-complete enabled, no settlement of the message removes message", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); - await testNoSettlement(true, true); - }); + it("Partitioned Subscription with Sessions: With auto-complete enabled, no settlement of the message removes message", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); + await testNoSettlement(true, true); + }); - it("Unpartitioned Queue with Sessions: With auto-complete enabled, no settlement of the message removes message", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); - await testNoSettlement(true, true); - }); + it("Unpartitioned Queue with Sessions: With auto-complete enabled, no settlement of the message removes message", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); + await testNoSettlement(true, true); + }); - it("Unpartitioned Subscription with Sessions: With auto-complete enabled, no settlement of the message removes message", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedSubscriptionWithSessions); - await testNoSettlement(true, true); - }); + it("Unpartitioned Subscription with Sessions: With auto-complete enabled, no settlement of the message removes message", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedSubscriptionWithSessions); + await testNoSettlement(true, true); + }); - it("Partitioned Queue: With auto-complete disabled, no settlement of the message removes message", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedQueue); - await testNoSettlement(false); - }); + it("Partitioned Queue: With auto-complete disabled, no settlement of the message removes message", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedQueue); + await testNoSettlement(false); + }); - it("Partitioned Subscription: With auto-complete disabled, no settlement of the message removes message", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedSubscription); - await testNoSettlement(false); - }); + it("Partitioned Subscription: With auto-complete disabled, no settlement of the message removes message", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedSubscription); + await testNoSettlement(false); + }); - /* it("Unpartitioned Queue: With auto-complete disabled, no settlement of the message removes message", async function(): Promise< + /* it("Unpartitioned Queue: With auto-complete disabled, no settlement of the message removes message", async function(): Promise< void > { await beforeEachTest(ClientType.UnpartitionedQueue, ClientType.UnpartitionedQueue); @@ -295,94 +279,102 @@ describe("Streaming Receiver in ReceiveAndDelete mode", function(): void { await testNoSettlement(false); });*/ - it("Partitioned Queue with Sessions: With auto-complete disabled, no settlement of the message removes message", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedQueueWithSessions); - await testNoSettlement(false, true); - }); + it("Partitioned Queue with Sessions: With auto-complete disabled, no settlement of the message removes message", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedQueueWithSessions); + await testNoSettlement(false, true); + }); - it("Partitioned Subscription with Sessions: With auto-complete disabled, no settlement of the message removes message", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); - await testNoSettlement(false, true); - }); + it("Partitioned Subscription with Sessions: With auto-complete disabled, no settlement of the message removes message", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); + await testNoSettlement(false, true); + }); - it("Unpartitioned Queue with Sessions: With auto-complete disabled, no settlement of the message removes message", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); - await testNoSettlement(false, true); - }); + it("Unpartitioned Queue with Sessions: With auto-complete disabled, no settlement of the message removes message", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); + await testNoSettlement(false, true); + }); - it("Unpartitioned Subscription with Sessions: With auto-complete disabled, no settlement of the message removes message", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedSubscriptionWithSessions); - await testNoSettlement(false, true); + it("Unpartitioned Subscription with Sessions: With auto-complete disabled, no settlement of the message removes message", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedSubscriptionWithSessions); + await testNoSettlement(false, true); + }); }); -}); -describe("Unsupported features in ReceiveAndDelete mode", function(): void { - afterEach(async () => { - await afterEachTest(); - }); - async function sendReceiveMsg( - testMessages: SendableMessageInfo - ): Promise<{ message: ReceivedMessage; context: ContextWithSettlement }> { - await senderClient.send(testMessages); - const msgs = (await receiverClient.receiveBatch(1)).messages; - - should.equal(Array.isArray(msgs), true, "`ReceivedMessages` is not an array"); - should.equal(msgs.length, 1, "Unexpected number of messages"); - should.equal(msgs[0].body, testMessages.body, "MessageBody is different than expected"); - should.equal(msgs[0].messageId, testMessages.messageId, "MessageId is different than expected"); - should.equal(msgs[0].deliveryCount, 0, "DeliveryCount is different than expected"); - - return { message: msgs[0], context: {} as ContextWithSettlement }; - } + describe("Settlement with ReceiveAndDelete mode", () => { + afterEach(async () => { + await afterEachTest(); + }); - const testError = (err: Error, operation: DispositionType): void => { - expect(err.message.toLowerCase(), "ErrorMessage is different than expected").includes( - `.context.${operation} is not a function` - ); - }; - - async function testSettlement(operation: DispositionType, useSessions?: boolean): Promise { - const testMessages = useSessions ? TestMessage.getSessionSample() : TestMessage.getSample(); - const msg = await sendReceiveMsg(testMessages); - try { - if (operation === DispositionType.complete) { - await msg.context.complete(msg.message); - } else if (operation === DispositionType.abandon) { - await msg.context.abandon(msg.message); - } else if (operation === DispositionType.deadletter) { - await msg.context.deadLetter(msg.message); - } else if (operation === DispositionType.defer) { - await msg.context.defer(msg.message); - } - } catch (err) { - errorWasThrown = true; - testError(err, operation); + async function sendReceiveMsg( + testMessages: SendableMessageInfo + ): Promise<{ message: ReceivedMessage; context: ContextWithSettlement }> { + await senderClient.send(testMessages); + const msgs = (await receiverClient.receiveBatch(1)).messages; + + should.equal(Array.isArray(msgs), true, "`ReceivedMessages` is not an array"); + should.equal(msgs.length, 1, "Unexpected number of messages"); + should.equal(msgs[0].body, testMessages.body, "MessageBody is different than expected"); + should.equal( + msgs[0].messageId, + testMessages.messageId, + "MessageId is different than expected" + ); + should.equal(msgs[0].deliveryCount, 0, "DeliveryCount is different than expected"); + + return { message: msgs[0], context: {} as ContextWithSettlement }; } - should.equal(errorWasThrown, true, "Error thrown flag must be true"); + const testError = (err: Error, operation: DispositionType): void => { + expect(err.message.toLowerCase(), "ErrorMessage is different than expected").includes( + `.context.${operation} is not a function` + ); + }; + + async function testSettlement( + operation: DispositionType, + useSessions?: boolean + ): Promise { + const testMessages = useSessions ? TestMessage.getSessionSample() : TestMessage.getSample(); + const msg = await sendReceiveMsg(testMessages); + try { + if (operation === DispositionType.complete) { + await msg.context.complete(msg.message); + } else if (operation === DispositionType.abandon) { + await msg.context.abandon(msg.message); + } else if (operation === DispositionType.deadletter) { + await msg.context.deadLetter(msg.message); + } else if (operation === DispositionType.defer) { + await msg.context.defer(msg.message); + } + } catch (err) { + errorWasThrown = true; + testError(err, operation); + } - await testPeekMsgsLength(receiverClient, 0); - } + should.equal(errorWasThrown, true, "Error thrown flag must be true"); - it("Partitioned Queue: complete() throws error", async function(): Promise { - await beforeEachTest(TestClientType.PartitionedQueue); - await testSettlement(DispositionType.complete); - }); + await testPeekMsgsLength(receiverClient, 0); + } - it("Partitioned Subscription: complete() throws error", async function(): Promise { - await beforeEachTest(TestClientType.PartitionedSubscription); - await testSettlement(DispositionType.complete); - }); + it("Partitioned Queue: complete() throws error", async function(): Promise { + await beforeEachTest(TestClientType.PartitionedQueue); + await testSettlement(DispositionType.complete); + }); - /* it("Unpartitioned Queue: complete() throws error", async function(): Promise { + it("Partitioned Subscription: complete() throws error", async function(): Promise { + await beforeEachTest(TestClientType.PartitionedSubscription); + await testSettlement(DispositionType.complete); + }); + + /* it("Unpartitioned Queue: complete() throws error", async function(): Promise { await beforeEachTest(ClientType.UnpartitionedQueue, ClientType.UnpartitionedQueue); await testSettlement(DispositionType.complete); }); @@ -394,31 +386,31 @@ describe("Unsupported features in ReceiveAndDelete mode", function(): void { await testSettlement(DispositionType.complete); });*/ - it("Partitioned Queue with Sessions: complete() throws error #RunInBrowser", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedQueueWithSessions); - await testSettlement(DispositionType.complete, true); - }); + it("Partitioned Queue with Sessions: complete() throws error #RunInBrowser", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedQueueWithSessions); + await testSettlement(DispositionType.complete, true); + }); - it("Partitioned Subscription with Sessions: complete() throws error", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); - await testSettlement(DispositionType.complete, true); - }); + it("Partitioned Subscription with Sessions: complete() throws error", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); + await testSettlement(DispositionType.complete, true); + }); - it("Partitioned Queue: abandon() throws error", async function(): Promise { - await beforeEachTest(TestClientType.PartitionedQueue); - await testSettlement(DispositionType.abandon); - }); + it("Partitioned Queue: abandon() throws error", async function(): Promise { + await beforeEachTest(TestClientType.PartitionedQueue); + await testSettlement(DispositionType.abandon); + }); - it("Partitioned Subscription: abandon() throws error", async function(): Promise { - await beforeEachTest(TestClientType.PartitionedSubscription); - await testSettlement(DispositionType.abandon); - }); + it("Partitioned Subscription: abandon() throws error", async function(): Promise { + await beforeEachTest(TestClientType.PartitionedSubscription); + await testSettlement(DispositionType.abandon); + }); - /* it("Unpartitioned Queue: abandon() throws error", async function(): Promise { + /* it("Unpartitioned Queue: abandon() throws error", async function(): Promise { await beforeEachTest(ClientType.UnpartitionedQueue, ClientType.UnpartitionedQueue); await testSettlement(DispositionType.abandon); }); @@ -430,31 +422,31 @@ describe("Unsupported features in ReceiveAndDelete mode", function(): void { await testSettlement(DispositionType.abandon); });*/ - it("Partitioned Queue with Sessions: abandon() throws error #RunInBrowser", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedQueueWithSessions); - await testSettlement(DispositionType.abandon, true); - }); + it("Partitioned Queue with Sessions: abandon() throws error #RunInBrowser", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedQueueWithSessions); + await testSettlement(DispositionType.abandon, true); + }); - it("Partitioned Subscription with Sessions: abandon() throws error", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); - await testSettlement(DispositionType.abandon, true); - }); + it("Partitioned Subscription with Sessions: abandon() throws error", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); + await testSettlement(DispositionType.abandon, true); + }); - it("Partitioned Queue: defer() throws error", async function(): Promise { - await beforeEachTest(TestClientType.PartitionedQueue); - await testSettlement(DispositionType.defer); - }); + it("Partitioned Queue: defer() throws error", async function(): Promise { + await beforeEachTest(TestClientType.PartitionedQueue); + await testSettlement(DispositionType.defer); + }); - it("Partitioned Subscription: defer() throws error", async function(): Promise { - await beforeEachTest(TestClientType.PartitionedSubscription); - await testSettlement(DispositionType.defer); - }); + it("Partitioned Subscription: defer() throws error", async function(): Promise { + await beforeEachTest(TestClientType.PartitionedSubscription); + await testSettlement(DispositionType.defer); + }); - /* it("Unpartitioned Queue: defer() throws error", async function(): Promise { + /* it("Unpartitioned Queue: defer() throws error", async function(): Promise { await beforeEachTest(ClientType.UnpartitionedQueue, ClientType.UnpartitionedQueue); await testSettlement(DispositionType.defer); }); @@ -466,31 +458,31 @@ describe("Unsupported features in ReceiveAndDelete mode", function(): void { await testSettlement(DispositionType.defer); });*/ - it("Partitioned Queue with Sessions: defer() throws error #RunInBrowser", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedQueueWithSessions); - await testSettlement(DispositionType.defer, true); - }); + it("Partitioned Queue with Sessions: defer() throws error #RunInBrowser", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedQueueWithSessions); + await testSettlement(DispositionType.defer, true); + }); - it("Partitioned Subscription with Sessions: defer() throws error", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); - await testSettlement(DispositionType.defer, true); - }); + it("Partitioned Subscription with Sessions: defer() throws error", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); + await testSettlement(DispositionType.defer, true); + }); - it("Partitioned Queue: deadLetter() throws error", async function(): Promise { - await beforeEachTest(TestClientType.PartitionedQueue); - await testSettlement(DispositionType.deadletter); - }); + it("Partitioned Queue: deadLetter() throws error", async function(): Promise { + await beforeEachTest(TestClientType.PartitionedQueue); + await testSettlement(DispositionType.deadletter); + }); - it("Partitioned Subscription: deadLetter() throws error", async function(): Promise { - await beforeEachTest(TestClientType.PartitionedSubscription); - await testSettlement(DispositionType.deadletter); - }); + it("Partitioned Subscription: deadLetter() throws error", async function(): Promise { + await beforeEachTest(TestClientType.PartitionedSubscription); + await testSettlement(DispositionType.deadletter); + }); - /* it("Unpartitioned Queue: deadLetter() throws error", async function(): Promise { + /* it("Unpartitioned Queue: deadLetter() throws error", async function(): Promise { await beforeEachTest(ClientType.UnpartitionedQueue, ClientType.UnpartitionedQueue); await testSettlement(DispositionType.deadletter); }); @@ -502,26 +494,193 @@ describe("Unsupported features in ReceiveAndDelete mode", function(): void { await testSettlement(DispositionType.deadletter); });*/ - it("Partitioned Queue with Sessions: deadLetter() throws error #RunInBrowser", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedQueueWithSessions); - await testSettlement(DispositionType.deadletter, true); - }); - - it("Partitioned Subscription with Sessions: deadLetter() throws error", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); - await testSettlement(DispositionType.deadletter, true); - }); + it("Partitioned Queue with Sessions: deadLetter() throws error #RunInBrowser", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedQueueWithSessions); + await testSettlement(DispositionType.deadletter, true); + }); - async function testRenewLock(): Promise { - const msg = await sendReceiveMsg(TestMessage.getSample()); + it("Partitioned Subscription with Sessions: deadLetter() throws error", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); + await testSettlement(DispositionType.deadletter, true); + }); - await (receiverClient as NonSessionReceiver<"receiveAndDelete">) - .renewMessageLock(msg.message) - .catch((err) => { + // #RevisitCommentedTestsAfterTheSingleClientAPI + // Settlement methods don't exist in the received context in ReceiveAndDelete mode + // const testError = (err: Error, operation: DispositionType): void => { + // should.equal( + // err.message, + // getErrorMessageNotSupportedInReceiveAndDeleteMode(`${operation} the message`), + // "ErrorMessage is different than expected" + // ); + // errorWasThrown = true; + // }; + + // async function testSettlement(operation: DispositionType, useSessions?: boolean): Promise { + // const testMessages = useSessions ? TestMessage.getSessionSample() : TestMessage.getSample(); + // const msg = await sendReceiveMsg(testMessages); + + // if (operation === DispositionType.complete) { + // await msg.context.complete(msg.message).catch((err) => testError(err, operation)); + // } else if (operation === DispositionType.abandon) { + // await msg.context.abandon(msg.message).catch((err) => testError(err, operation)); + // } else if (operation === DispositionType.deadletter) { + // await msg.context.deadLetter(msg.message).catch((err) => testError(err, operation)); + // } else if (operation === DispositionType.defer) { + // await msg.context.defer(msg.message).catch((err) => testError(err, operation)); + // } + + // should.equal(errorWasThrown, true, "Error thrown flag must be true"); + + // await testPeekMsgsLength(receiverClient, 0); + // } + + // it("Partitioned Subscription: complete() throws error", async function(): Promise { + // await beforeEachTest(TestClientType.PartitionedSubscription); + // await testSettlement(DispositionType.complete); + // }); + + // /* it("Unpartitioned Queue: complete() throws error", async function(): Promise { + // await beforeEachTest(ClientType.UnpartitionedQueue, ClientType.UnpartitionedQueue); + // await testSettlement(DispositionType.complete); + // }); + + // it("Unpartitioned Subscription: complete() throws error", async function(): Promise< + // void + // > { + // await beforeEachTest(ClientType.UnpartitionedTopic, ClientType.UnpartitionedSubscription); + // await testSettlement(DispositionType.complete); + // });*/ + + // it("Partitioned Queue with Sessions: complete() throws error #RunInBrowser", async function(): Promise< + // void + // > { + // await beforeEachTest(TestClientType.PartitionedQueueWithSessions); + // await testSettlement(DispositionType.complete, true); + // }); + + // it("Partitioned Subscription with Sessions: complete() throws error", async function(): Promise< + // void + // > { + // await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); + // await testSettlement(DispositionType.complete, true); + // }); + + // it("Partitioned Queue: abandon() throws error", async function(): Promise { + // await beforeEachTest(TestClientType.PartitionedQueue); + // await testSettlement(DispositionType.abandon); + // }); + + // it("Partitioned Subscription: abandon() throws error", async function(): Promise { + // await beforeEachTest(TestClientType.PartitionedSubscription); + // await testSettlement(DispositionType.abandon); + // }); + + // /* it("Unpartitioned Queue: abandon() throws error", async function(): Promise { + // await beforeEachTest(ClientType.UnpartitionedQueue, ClientType.UnpartitionedQueue); + // await testSettlement(DispositionType.abandon); + // }); + + // it("Unpartitioned Subscription: abandon() throws error", async function(): Promise< + // void + // > { + // await beforeEachTest(ClientType.UnpartitionedTopic, ClientType.UnpartitionedSubscription); + // await testSettlement(DispositionType.abandon); + // });*/ + + // it("Partitioned Queue with Sessions: abandon() throws error #RunInBrowser", async function(): Promise< + // void + // > { + // await beforeEachTest(TestClientType.PartitionedQueueWithSessions); + // await testSettlement(DispositionType.abandon, true); + // }); + + // it("Partitioned Subscription with Sessions: abandon() throws error", async function(): Promise< + // void + // > { + // await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); + // await testSettlement(DispositionType.abandon, true); + // }); + + // it("Partitioned Queue: defer() throws error", async function(): Promise { + // await beforeEachTest(TestClientType.PartitionedQueue); + // await testSettlement(DispositionType.defer); + // }); + + // it("Partitioned Subscription: defer() throws error", async function(): Promise { + // await beforeEachTest(TestClientType.PartitionedSubscription); + // await testSettlement(DispositionType.defer); + // }); + + // /* it("Unpartitioned Queue: defer() throws error", async function(): Promise { + // await beforeEachTest(ClientType.UnpartitionedQueue, ClientType.UnpartitionedQueue); + // await testSettlement(DispositionType.defer); + // }); + + // it("Unpartitioned Subscription: defer() throws error", async function(): Promise< + // void + // > { + // await beforeEachTest(ClientType.UnpartitionedTopic, ClientType.UnpartitionedSubscription); + // await testSettlement(DispositionType.defer); + // });*/ + + // it("Partitioned Queue with Sessions: defer() throws error #RunInBrowser", async function(): Promise< + // void + // > { + // await beforeEachTest(TestClientType.PartitionedQueueWithSessions); + // await testSettlement(DispositionType.defer, true); + // }); + + // it("Partitioned Subscription with Sessions: defer() throws error", async function(): Promise< + // void + // > { + // await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); + // await testSettlement(DispositionType.defer, true); + // }); + + // it("Partitioned Queue: deadLetter() throws error", async function(): Promise { + // await beforeEachTest(TestClientType.PartitionedQueue); + // await testSettlement(DispositionType.deadletter); + // }); + + // it("Partitioned Subscription: deadLetter() throws error", async function(): Promise { + // await beforeEachTest(TestClientType.PartitionedSubscription); + // await testSettlement(DispositionType.deadletter); + // }); + + // /* it("Unpartitioned Queue: deadLetter() throws error", async function(): Promise { + // await beforeEachTest(ClientType.UnpartitionedQueue, ClientType.UnpartitionedQueue); + // await testSettlement(DispositionType.deadletter); + // }); + + // it("Unpartitioned Subscription: deadLetter() throws error", async function(): Promise< + // void + // > { + // await beforeEachTest(ClientType.UnpartitionedTopic, ClientType.UnpartitionedSubscription); + // await testSettlement(DispositionType.deadletter); + // });*/ + + // it("Partitioned Queue with Sessions: deadLetter() throws error #RunInBrowser", async function(): Promise< + // void + // > { + // await beforeEachTest(TestClientType.PartitionedQueueWithSessions); + // await testSettlement(DispositionType.deadletter, true); + // }); + + // it("Partitioned Subscription with Sessions: deadLetter() throws error", async function(): Promise< + // void + // > { + // await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); + // await testSettlement(DispositionType.deadletter, true); + // }); + + async function testRenewLock(): Promise { + const msg = await sendReceiveMsg(TestMessage.getSample()); + + await receiverClient.renewMessageLock(msg.message).catch((err) => { should.equal( err.message, getErrorMessageNotSupportedInReceiveAndDeleteMode("renew the message lock"), @@ -530,19 +689,22 @@ describe("Unsupported features in ReceiveAndDelete mode", function(): void { errorWasThrown = true; }); - should.equal(errorWasThrown, true, "Error thrown flag must be true"); - } + should.equal(errorWasThrown, true, "Error thrown flag must be true"); + } - it("Partitioned Queue: Renew message lock throws error #RunInBrowser", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedQueue); - await testRenewLock(); - }); + it("Partitioned Queue: Renew message lock throws error #RunInBrowser", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedQueue); + await testRenewLock(); + }); - it("Partitioned Subscription: Renew message lock throws error", async function(): Promise { - await beforeEachTest(TestClientType.PartitionedSubscription); - await testRenewLock(); + it("Partitioned Subscription: Renew message lock throws error", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedSubscription); + await testRenewLock(); + }); }); /* it("Unpartitioned Queue: Renew message lock throws error", async function(): Promise { @@ -558,121 +720,123 @@ describe("Unsupported features in ReceiveAndDelete mode", function(): void { });*/ }); -describe("Receive Deferred messages in ReceiveAndDelete mode", function(): void { - let sequenceNumber: Long; - - afterEach(async () => { - await afterEachTest(); - }); - async function eachTest(testClientType: TestClientType, useSessions?: boolean) { - await beforeEachTest(testClientType, "peekLock"); - await deferMessage(useSessions); - await receiverClient.close(); - await receiveDeferredMessage(testClientType, useSessions); - } - async function deferMessage(useSessions?: boolean): Promise { - const testMessages = useSessions ? TestMessage.getSessionSample() : TestMessage.getSample(); - await senderClient.send(testMessages); - const batch = await receiverClient.receiveBatch(1); - const msgs = batch.messages; - - should.equal(Array.isArray(msgs), true, "`ReceivedMessages` is not an array"); - should.equal(msgs.length, 1, "Unexpected number of messages"); - should.equal(msgs[0].body, testMessages.body, "MessageBody is different than expected"); - should.equal(msgs[0].messageId, testMessages.messageId, "MessageId is different than expected"); - should.equal(msgs[0].deliveryCount, 0, "DeliveryCount is different than expected"); - - sequenceNumber = msgs[0].sequenceNumber!; - await (batch.context as ContextWithSettlement).defer(msgs[0]); - } - - async function receiveDeferredMessage( - testClientType: TestClientType, - useSessions: boolean | undefined - ): Promise { - receiverClient = ( - await getSenderReceiverClients( - testClientType, - "receiveAndDelete", - undefined, - useSessions ? { id: TestMessage.sessionId } : undefined, - false - ) - ).receiverClient; - const deferredMsgs: ServiceBusMessage | undefined = await receiverClient.receiveDeferredMessage( - sequenceNumber - ); - if (!deferredMsgs) { - throw `No message received for sequence number ${sequenceNumber}`; - } - - should.equal(deferredMsgs!.deliveryCount, 1, "DeliveryCount is different than expected"); - await testPeekMsgsLength(receiverClient, 0); - } - - /* it("Partitioned Queue: No settlement of the message removes message", async function(): Promise< - void - > { - await beforeEachTest( - TestClientType.PartitionedQueue, - TestClientType.PartitionedQueue, - undefined, - ReceiveMode.peekLock - ); - await deferMessage(); - await receiver.close(); - receiver = receiverClient.createReceiver(ReceiveMode.receiveAndDelete); - await receiveDeferredMessage(); - }); - - it("Partitioned Subscription: No settlement of the message removes message", async function(): Promise< - void - > { - await beforeEachTest( - TestClientType.PartitionedTopic, - TestClientType.PartitionedSubscription, - undefined, - ReceiveMode.peekLock - ); - await deferMessage(); - await receiver.close(); - receiver = receiverClient.createReceiver(ReceiveMode.receiveAndDelete); - await receiveDeferredMessage(); - }); */ - - it("Unpartitioned Queue: No settlement of the message removes message #RunInBrowser", async function(): Promise< - void - > { - await eachTest(TestClientType.UnpartitionedQueue); - }); - - it("Unpartitioned Subscription: No settlement of the message removes message", async function(): Promise< - void - > { - await eachTest(TestClientType.UnpartitionedSubscription); - }); - - it("Partitioned Queue with Sessions: No settlement of the message removes message", async function(): Promise< - void - > { - await eachTest(TestClientType.PartitionedQueueWithSessions, true); - }); - - it("Partitioned Subscription with Sessions: No settlement of the message removes message", async function(): Promise< - void - > { - await eachTest(TestClientType.PartitionedSubscriptionWithSessions, true); - }); - - it("Unpartitioned Queue with Sessions: No settlement of the message removes message #RunInBrowser", async function(): Promise< - void - > { - await eachTest(TestClientType.UnpartitionedQueueWithSessions, true); - }); - - it("Unpartitioned Subscription with Sessions: No settlement of the message removes message", async function(): Promise< - void - > { - await eachTest(TestClientType.UnpartitionedSubscriptionWithSessions, true); - }); -}); +// #RevisitCommentedTestsAfterTheSingleClientAPI +// Settlement methods(deferring here) don't exist in the received context in ReceiveAndDelete mode +// describe("Receive Deferred messages in ReceiveAndDelete mode", function(): void { +// let sequenceNumber: Long; + +// afterEach(async () => { +// await afterEachTest(); +// }); +// async function deferMessage(useSessions?: boolean): Promise { +// const testMessages = useSessions ? TestMessage.getSessionSample() : TestMessage.getSample(); +// await senderClient.send(testMessages); +// const batch = await await receiverClient.receiveBatch(1); +// const msgs = batch.messages; + +// should.equal(Array.isArray(msgs), true, "`ReceivedMessages` is not an array"); +// should.equal(msgs.length, 1, "Unexpected number of messages"); +// should.equal(msgs[0].body, testMessages.body, "MessageBody is different than expected"); +// should.equal(msgs[0].messageId, testMessages.messageId, "MessageId is different than expected"); +// should.equal(msgs[0].deliveryCount, 0, "DeliveryCount is different than expected"); + +// sequenceNumber = msgs[0].sequenceNumber!; +// await (batch.context as ContextWithSettlement).defer(msgs[0]); +// } + +// async function receiveDeferredMessage(): Promise { +// const deferredMsgs: ServiceBusMessage | undefined = await receiverClient.receiveDeferredMessage( +// sequenceNumber +// ); +// if (!deferredMsgs) { +// throw `No message received for sequence number ${sequenceNumber}`; +// } + +// should.equal(deferredMsgs!.deliveryCount, 1, "DeliveryCount is different than expected"); +// await testPeekMsgsLength(receiverClient, 0); +// } + +// /* it("Partitioned Queue: No settlement of the message removes message", async function(): Promise< +// void +// > { +// await beforeEachTest( +// TestClientType.PartitionedQueue, +// TestClientType.PartitionedQueue, +// undefined, +// ReceiveMode.peekLock +// ); +// await deferMessage(); +// await receiver.close(); +// receiver = receiverClient.createReceiver(ReceiveMode.receiveAndDelete); +// await receiveDeferredMessage(); +// }); + +// it("Partitioned Subscription: No settlement of the message removes message", async function(): Promise< +// void +// > { +// await beforeEachTest( +// TestClientType.PartitionedTopic, +// TestClientType.PartitionedSubscription, +// undefined, +// ReceiveMode.peekLock +// ); +// await deferMessage(); +// await receiver.close(); +// receiver = receiverClient.createReceiver(ReceiveMode.receiveAndDelete); +// await receiveDeferredMessage(); +// }); */ + +// it("Unpartitioned Queue: No settlement of the message removes message #RunInBrowser", async function(): Promise< +// void +// > { +// await beforeEachTest(TestClientType.UnpartitionedQueue); +// await deferMessage(); +// await receiverClient.close(); +// await receiveDeferredMessage(); +// }); + +// it("Unpartitioned Subscription: No settlement of the message removes message", async function(): Promise< +// void +// > { +// await beforeEachTest(TestClientType.UnpartitionedSubscription); +// await deferMessage(); +// await receiverClient.close(); +// await receiveDeferredMessage(); +// }); + +// it("Partitioned Queue with Sessions: No settlement of the message removes message", async function(): Promise< +// void +// > { +// await beforeEachTest(TestClientType.PartitionedQueueWithSessions); +// await deferMessage(true); +// await receiverClient.close(); +// await receiveDeferredMessage(); +// }); + +// it("Partitioned Subscription with Sessions: No settlement of the message removes message", async function(): Promise< +// void +// > { +// await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); +// await deferMessage(true); +// await receiverClient.close(); +// await receiveDeferredMessage(); +// }); + +// it("Unpartitioned Queue with Sessions: No settlement of the message removes message #RunInBrowser", async function(): Promise< +// void +// > { +// await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); +// await deferMessage(true); +// await receiverClient.close(); +// await receiveDeferredMessage(); +// }); + +// it("Unpartitioned Subscription with Sessions: No settlement of the message removes message", async function(): Promise< +// void +// > { +// await beforeEachTest(TestClientType.UnpartitionedSubscriptionWithSessions); +// await deferMessage(true); +// await receiverClient.close(); +// await receiveDeferredMessage(); +// }); +// }); diff --git a/sdk/servicebus/service-bus/test/renewLock.spec.ts b/sdk/servicebus/service-bus/test/renewLock.spec.ts index 02e6eeb8dde6..540f55d05c0f 100644 --- a/sdk/servicebus/service-bus/test/renewLock.spec.ts +++ b/sdk/servicebus/service-bus/test/renewLock.spec.ts @@ -5,514 +5,511 @@ import chai from "chai"; const should = chai.should(); import chaiAsPromised from "chai-as-promised"; chai.use(chaiAsPromised); -import { ServiceBusSenderClient, ReceivedMessage, ContextWithSettlement } from "../src"; +import { ReceivedMessage, ContextWithSettlement } from "../src"; import { delay } from "rhea-promise"; -import { - purge, - getSenderReceiverClients, - TestClientType, - TestMessage, - isSessionfulEntity -} from "./utils/testUtils"; -import { NonSessionReceiver } from "../src/serviceBusReceiverClient"; - -let senderClient: ServiceBusSenderClient; -let receiverClient: NonSessionReceiver<"peekLock">; - -async function beforeEachTest(entityType: TestClientType): Promise { - let clients; - if (isSessionfulEntity(entityType)) { - clients = await getSenderReceiverClients(entityType, "peekLock", undefined, { - id: TestMessage.sessionId - }); - } else { - clients = await getSenderReceiverClients(entityType, "peekLock"); +import { TestClientType, TestMessage } from "./utils/testUtils"; +import { ServiceBusClientForTests, createServiceBusClientForTests } from "./utils/testutils2"; +import { Receiver } from "../src/receivers/receiver"; +import { Sender } from "../src/sender"; + +describe("renew lock", () => { + let serviceBusClient: ServiceBusClientForTests; + let senderClient: Sender; + let receiverClient: Receiver; + + before(() => { + serviceBusClient = createServiceBusClientForTests(); + }); + + after(() => { + return serviceBusClient.test.after(); + }); + + async function beforeEachTest(entityType: TestClientType): Promise { + const entityNames = await serviceBusClient.test.createTestEntities(entityType); + receiverClient = serviceBusClient.test.getPeekLockReceiver(entityNames); + + senderClient = serviceBusClient.test.addToCleanup( + serviceBusClient.getSender(entityNames.queue ?? entityNames.topic!) + ); } - senderClient = clients.senderClient; - receiverClient = clients.receiverClient as NonSessionReceiver<"peekLock">; - - await purge(receiverClient); - const peekedMsgs = await receiverClient.diagnostics.peek(); - const receiverEntityType = receiverClient.entityType; - if (peekedMsgs.length) { - chai.assert.fail(`Please use an empty ${receiverEntityType} for integration testing`); + + function afterEachTest(): Promise { + return serviceBusClient.test.afterEach(); } -} -async function afterEachTest(): Promise { - await senderClient.close(); - await receiverClient.close(); -} + describe("Unpartitioned Queue - Lock Renewal #RunInBrowser", function(): void { + beforeEach(async () => { + await beforeEachTest(TestClientType.UnpartitionedQueue); + }); -describe("Unpartitioned Queue - Lock Renewal #RunInBrowser", function(): void { - beforeEach(async () => { - await beforeEachTest(TestClientType.UnpartitionedQueue); - }); + afterEach(async () => { + await afterEachTest(); + }); - afterEach(async () => { - await afterEachTest(); - }); + it("Batch Receiver: renewLock() resets lock duration each time.", async function(): Promise< + void + > { + await testBatchReceiverManualLockRenewalHappyCase(senderClient, receiverClient); + }); - it("Batch Receiver: renewLock() resets lock duration each time.", async function(): Promise< - void - > { - await testBatchReceiverManualLockRenewalHappyCase(senderClient, receiverClient); - }); + it("Batch Receiver: complete() after lock expiry with throws error", async function(): Promise< + void + > { + await testBatchReceiverManualLockRenewalErrorOnLockExpiry(senderClient, receiverClient); + }); - it("Batch Receiver: complete() after lock expiry with throws error", async function(): Promise< - void - > { - await testBatchReceiverManualLockRenewalErrorOnLockExpiry(senderClient, receiverClient); - }); + it("Streaming Receiver: renewLock() resets lock duration each time.", async function(): Promise< + void + > { + await testStreamingReceiverManualLockRenewalHappyCase(senderClient, receiverClient); + }); - it("Streaming Receiver: renewLock() resets lock duration each time.", async function(): Promise< - void - > { - await testStreamingReceiverManualLockRenewalHappyCase(senderClient, receiverClient); - }); + it("Streaming Receiver: complete() after lock expiry with auto-renewal disabled throws error", async function(): Promise< + void + > { + await testAutoLockRenewalConfigBehavior( + senderClient, + receiverClient, + { + maxAutoRenewDurationInSeconds: 0, + delayBeforeAttemptingToCompleteMessageInSeconds: 31, + willCompleteFail: true + }, + TestClientType.UnpartitionedQueue + ); + }); - it("Streaming Receiver: complete() after lock expiry with auto-renewal disabled throws error", async function(): Promise< - void - > { - await testAutoLockRenewalConfigBehavior( - senderClient, - receiverClient, - { - maxAutoRenewDurationInSeconds: 0, - delayBeforeAttemptingToCompleteMessageInSeconds: 31, - willCompleteFail: true - }, - TestClientType.UnpartitionedQueue - ); - }); + it("Streaming Receiver: lock will not expire until configured time", async function(): Promise< + void + > { + await testAutoLockRenewalConfigBehavior( + senderClient, + receiverClient, + { + maxAutoRenewDurationInSeconds: 38, + delayBeforeAttemptingToCompleteMessageInSeconds: 35, + willCompleteFail: false + }, + TestClientType.UnpartitionedQueue + ); + }); - it("Streaming Receiver: lock will not expire until configured time", async function(): Promise< - void - > { - await testAutoLockRenewalConfigBehavior( - senderClient, - receiverClient, - { - maxAutoRenewDurationInSeconds: 38, - delayBeforeAttemptingToCompleteMessageInSeconds: 35, - willCompleteFail: false - }, - TestClientType.UnpartitionedQueue - ); + it("Streaming Receiver: lock expires sometime after configured time", async function(): Promise< + void + > { + await testAutoLockRenewalConfigBehavior( + senderClient, + receiverClient, + { + maxAutoRenewDurationInSeconds: 35, + delayBeforeAttemptingToCompleteMessageInSeconds: 55, + willCompleteFail: true + }, + TestClientType.UnpartitionedQueue + ); + }).timeout(95000); + + it("Streaming Receiver: No lock renewal when config value is less than lock duration", async function(): Promise< + void + > { + await testAutoLockRenewalConfigBehavior( + senderClient, + receiverClient, + { + maxAutoRenewDurationInSeconds: 15, + delayBeforeAttemptingToCompleteMessageInSeconds: 31, + willCompleteFail: true + }, + TestClientType.UnpartitionedQueue + ); + }); }); - it("Streaming Receiver: lock expires sometime after configured time", async function(): Promise< - void - > { - await testAutoLockRenewalConfigBehavior( - senderClient, - receiverClient, - { - maxAutoRenewDurationInSeconds: 35, - delayBeforeAttemptingToCompleteMessageInSeconds: 55, - willCompleteFail: true - }, - TestClientType.UnpartitionedQueue - ); - }).timeout(95000); - - it("Streaming Receiver: No lock renewal when config value is less than lock duration", async function(): Promise< - void - > { - await testAutoLockRenewalConfigBehavior( - senderClient, - receiverClient, - { - maxAutoRenewDurationInSeconds: 15, - delayBeforeAttemptingToCompleteMessageInSeconds: 31, - willCompleteFail: true - }, - TestClientType.UnpartitionedQueue - ); - }); -}); + describe("Partitioned Queue - Lock Renewal", function(): void { + beforeEach(async () => { + await beforeEachTest(TestClientType.PartitionedQueue); + }); -describe("Partitioned Queue - Lock Renewal", function(): void { - beforeEach(async () => { - await beforeEachTest(TestClientType.PartitionedQueue); - }); + afterEach(async () => { + await afterEachTest(); + }); - afterEach(async () => { - await afterEachTest(); - }); + it("Batch Receiver: renewLock() resets lock duration each time.", async function(): Promise< + void + > { + await testBatchReceiverManualLockRenewalHappyCase(senderClient, receiverClient); + }); - it("Batch Receiver: renewLock() resets lock duration each time.", async function(): Promise< - void - > { - await testBatchReceiverManualLockRenewalHappyCase(senderClient, receiverClient); - }); + it("Batch Receiver: complete() after lock expiry with throws error", async function(): Promise< + void + > { + await testBatchReceiverManualLockRenewalErrorOnLockExpiry(senderClient, receiverClient); + }); - it("Batch Receiver: complete() after lock expiry with throws error", async function(): Promise< - void - > { - await testBatchReceiverManualLockRenewalErrorOnLockExpiry(senderClient, receiverClient); - }); + it("Streaming Receiver: renewLock() resets lock duration each time.", async function(): Promise< + void + > { + await testStreamingReceiverManualLockRenewalHappyCase(senderClient, receiverClient); + }); - it("Streaming Receiver: renewLock() resets lock duration each time.", async function(): Promise< - void - > { - await testStreamingReceiverManualLockRenewalHappyCase(senderClient, receiverClient); + it("Streaming Receiver: complete() after lock expiry with auto-renewal disabled throws error", async function(): Promise< + void + > { + await testAutoLockRenewalConfigBehavior( + senderClient, + receiverClient, + { + maxAutoRenewDurationInSeconds: 0, + delayBeforeAttemptingToCompleteMessageInSeconds: 31, + willCompleteFail: true + }, + TestClientType.PartitionedQueue + ); + }); }); - it("Streaming Receiver: complete() after lock expiry with auto-renewal disabled throws error", async function(): Promise< - void - > { - await testAutoLockRenewalConfigBehavior( - senderClient, - receiverClient, - { - maxAutoRenewDurationInSeconds: 0, - delayBeforeAttemptingToCompleteMessageInSeconds: 31, - willCompleteFail: true - }, - TestClientType.PartitionedQueue - ); - }); -}); + describe("Unpartitioned Subscription - Lock Renewal", function(): void { + beforeEach(async () => { + await beforeEachTest(TestClientType.UnpartitionedSubscription); + }); -describe("Unpartitioned Subscription - Lock Renewal", function(): void { - beforeEach(async () => { - await beforeEachTest(TestClientType.UnpartitionedSubscription); - }); + afterEach(async () => { + await afterEachTest(); + }); - afterEach(async () => { - await afterEachTest(); - }); + it("Batch Receiver: renewLock() resets lock duration each time.", async function(): Promise< + void + > { + await testBatchReceiverManualLockRenewalHappyCase(senderClient, receiverClient); + }); - it("Batch Receiver: renewLock() resets lock duration each time.", async function(): Promise< - void - > { - await testBatchReceiverManualLockRenewalHappyCase(senderClient, receiverClient); - }); + it("Batch Receiver: complete() after lock expiry with throws error", async function(): Promise< + void + > { + await testBatchReceiverManualLockRenewalErrorOnLockExpiry(senderClient, receiverClient); + }); - it("Batch Receiver: complete() after lock expiry with throws error", async function(): Promise< - void - > { - await testBatchReceiverManualLockRenewalErrorOnLockExpiry(senderClient, receiverClient); - }); + it("Streaming Receiver: renewLock() resets lock duration each time.", async function(): Promise< + void + > { + await testStreamingReceiverManualLockRenewalHappyCase(senderClient, receiverClient); + }); - it("Streaming Receiver: renewLock() resets lock duration each time.", async function(): Promise< - void - > { - await testStreamingReceiverManualLockRenewalHappyCase(senderClient, receiverClient); + it("Streaming Receiver: complete() after lock expiry with auto-renewal disabled throws error", async function(): Promise< + void + > { + await testAutoLockRenewalConfigBehavior( + senderClient, + receiverClient, + { + maxAutoRenewDurationInSeconds: 0, + delayBeforeAttemptingToCompleteMessageInSeconds: 31, + willCompleteFail: true + }, + TestClientType.UnpartitionedSubscription + ); + }); }); - it("Streaming Receiver: complete() after lock expiry with auto-renewal disabled throws error", async function(): Promise< - void - > { - await testAutoLockRenewalConfigBehavior( - senderClient, - receiverClient, - { - maxAutoRenewDurationInSeconds: 0, - delayBeforeAttemptingToCompleteMessageInSeconds: 31, - willCompleteFail: true - }, - TestClientType.UnpartitionedSubscription - ); - }); -}); + describe("Partitioned Subscription - Lock Renewal", function(): void { + beforeEach(async () => { + await beforeEachTest(TestClientType.PartitionedSubscription); + }); -describe("Partitioned Subscription - Lock Renewal", function(): void { - beforeEach(async () => { - await beforeEachTest(TestClientType.PartitionedSubscription); - }); + afterEach(async () => { + await afterEachTest(); + }); - afterEach(async () => { - await afterEachTest(); - }); + it("Batch Receiver: renewLock() resets lock duration each time.", async function(): Promise< + void + > { + await testBatchReceiverManualLockRenewalHappyCase(senderClient, receiverClient); + }); - it("Batch Receiver: renewLock() resets lock duration each time.", async function(): Promise< - void - > { - await testBatchReceiverManualLockRenewalHappyCase(senderClient, receiverClient); - }); + it("Batch Receiver: complete() after lock expiry with throws error", async function(): Promise< + void + > { + await testBatchReceiverManualLockRenewalErrorOnLockExpiry(senderClient, receiverClient); + }); - it("Batch Receiver: complete() after lock expiry with throws error", async function(): Promise< - void - > { - await testBatchReceiverManualLockRenewalErrorOnLockExpiry(senderClient, receiverClient); - }); + it("Streaming Receiver: renewLock() resets lock duration each time.", async function(): Promise< + void + > { + await testStreamingReceiverManualLockRenewalHappyCase(senderClient, receiverClient); + }); - it("Streaming Receiver: renewLock() resets lock duration each time.", async function(): Promise< - void - > { - await testStreamingReceiverManualLockRenewalHappyCase(senderClient, receiverClient); + it("Streaming Receiver: complete() after lock expiry with auto-renewal disabled throws error", async function(): Promise< + void + > { + await testAutoLockRenewalConfigBehavior( + senderClient, + receiverClient, + { + maxAutoRenewDurationInSeconds: 0, + delayBeforeAttemptingToCompleteMessageInSeconds: 31, + willCompleteFail: true + }, + TestClientType.PartitionedSubscription + ); + }); }); - it("Streaming Receiver: complete() after lock expiry with auto-renewal disabled throws error", async function(): Promise< - void - > { - await testAutoLockRenewalConfigBehavior( - senderClient, - receiverClient, - { - maxAutoRenewDurationInSeconds: 0, - delayBeforeAttemptingToCompleteMessageInSeconds: 31, - willCompleteFail: true - }, - TestClientType.PartitionedSubscription - ); - }); -}); + const lockDurationInMilliseconds = 30000; -const lockDurationInMilliseconds = 30000; - -let uncaughtErrorFromHandlers: Error | undefined; - -async function processError(err: Error): Promise { - uncaughtErrorFromHandlers = err; -} - -/** - * Test renewLock() after receiving a message using Batch Receiver - */ -async function testBatchReceiverManualLockRenewalHappyCase( - senderClient: ServiceBusSenderClient, - receiverClient: NonSessionReceiver<"peekLock"> -): Promise { - const testMessage = TestMessage.getSample(); - await senderClient.send(testMessage); - - const batch = await receiverClient.receiveBatch(1); - const msgs = batch.messages; - - // Compute expected initial lock expiry time - const expectedLockExpiryTimeUtc = new Date(); - expectedLockExpiryTimeUtc.setSeconds( - expectedLockExpiryTimeUtc.getSeconds() + lockDurationInMilliseconds / 1000 - ); - - should.equal(Array.isArray(msgs), true, "`ReceivedMessages` is not an array"); - should.equal(msgs.length, 1, "Unexpected number of messages"); - should.equal(msgs[0].body, testMessage.body, "MessageBody is different than expected"); - should.equal(msgs[0].messageId, testMessage.messageId, "MessageId is different than expected"); - - // Verify initial lock expiry time on the message - assertTimestampsAreApproximatelyEqual( - msgs[0].lockedUntilUtc, - expectedLockExpiryTimeUtc, - "Initial" - ); - - await delay(5000); - if (msgs[0].lockToken) { - await receiverClient.renewMessageLock(msgs[0].lockToken); - } + let uncaughtErrorFromHandlers: Error | undefined; - // Compute expected lock expiry time after renewing lock after 5 seconds - expectedLockExpiryTimeUtc.setSeconds(expectedLockExpiryTimeUtc.getSeconds() + 5); - - // Verify lock expiry time after renewLock() - assertTimestampsAreApproximatelyEqual( - msgs[0].lockedUntilUtc, - expectedLockExpiryTimeUtc, - "After renewlock()" - ); - - await batch.context.complete(msgs[0]); -} - -/** - * Test settling of message from Batch Receiver fails after message lock expires - */ -async function testBatchReceiverManualLockRenewalErrorOnLockExpiry( - senderClient: ServiceBusSenderClient, - receiverClient: NonSessionReceiver<"peekLock"> -): Promise { - const testMessage = TestMessage.getSample(); - await senderClient.send(testMessage); - - const batch = await receiverClient.receiveBatch(1); - const msgs = batch.messages; - - should.equal(Array.isArray(msgs), true, "`ReceivedMessages` is not an array"); - should.equal(msgs.length, 1, "Expected message length does not match"); - should.equal(msgs[0].body, testMessage.body, "MessageBody is different than expected"); - should.equal(msgs[0].messageId, testMessage.messageId, "MessageId is different than expected"); - - // Sleeping 30 seconds... - await delay(lockDurationInMilliseconds + 1000); - - let errorWasThrown: boolean = false; - await batch.context.complete(msgs[0]).catch((err) => { - should.equal(err.code, "MessageLockLostError", "Error code is different than expected"); - errorWasThrown = true; - }); + async function processError(err: Error): Promise { + uncaughtErrorFromHandlers = err; + } - should.equal(errorWasThrown, true, "Error thrown flag must be true"); - - // Clean up any left over messages - const unprocessedMsgsBatch = await receiverClient.receiveBatch(1); - await unprocessedMsgsBatch.context.complete(unprocessedMsgsBatch.messages[0]); -} - -/** - * Test renewLock() after receiving a message using Streaming Receiver with autoLockRenewal disabled - */ -async function testStreamingReceiverManualLockRenewalHappyCase( - senderClient: ServiceBusSenderClient, - receiverClient: NonSessionReceiver<"peekLock"> -): Promise { - let numOfMessagesReceived = 0; - const testMessage = TestMessage.getSample(); - await senderClient.send(testMessage); - - async function processMessage( - brokeredMessage: ReceivedMessage, - context: ContextWithSettlement + /** + * Test renewLock() after receiving a message using Batch Receiver + */ + async function testBatchReceiverManualLockRenewalHappyCase( + senderClient: Sender, + receiverClient: Receiver ): Promise { - if (numOfMessagesReceived < 1) { - numOfMessagesReceived++; + const testMessage = TestMessage.getSample(); + await senderClient.send(testMessage); - should.equal( - brokeredMessage.body, - testMessage.body, - "MessageBody is different than expected" - ); - should.equal( - brokeredMessage.messageId, - testMessage.messageId, - "MessageId is different than expected" - ); + const batch = await receiverClient.receiveBatch(1); + const msgs = batch.messages; - // Compute expected initial lock expiry time - const expectedLockExpiryTimeUtc = new Date(); - expectedLockExpiryTimeUtc.setSeconds( - expectedLockExpiryTimeUtc.getSeconds() + lockDurationInMilliseconds / 1000 - ); + // Compute expected initial lock expiry time + const expectedLockExpiryTimeUtc = new Date(); + expectedLockExpiryTimeUtc.setSeconds( + expectedLockExpiryTimeUtc.getSeconds() + lockDurationInMilliseconds / 1000 + ); - // Verify initial expiry time on message - assertTimestampsAreApproximatelyEqual( - brokeredMessage.lockedUntilUtc, - expectedLockExpiryTimeUtc, - "Initial" - ); + should.equal(Array.isArray(msgs), true, "`ReceivedMessages` is not an array"); + should.equal(msgs.length, 1, "Unexpected number of messages"); + should.equal(msgs[0].body, testMessage.body, "MessageBody is different than expected"); + should.equal(msgs[0].messageId, testMessage.messageId, "MessageId is different than expected"); - await delay(5000); - await receiverClient.renewMessageLock(brokeredMessage); + // Verify initial lock expiry time on the message + assertTimestampsAreApproximatelyEqual( + msgs[0].lockedUntilUtc, + expectedLockExpiryTimeUtc, + "Initial" + ); - // Compute expected lock expiry time after renewing lock after 5 seconds - expectedLockExpiryTimeUtc.setSeconds(expectedLockExpiryTimeUtc.getSeconds() + 5); + await delay(5000); + if (msgs[0].lockToken) { + await receiverClient.renewMessageLock(msgs[0].lockToken); + } - // Verify actual expiry time on session after first renewal - assertTimestampsAreApproximatelyEqual( - brokeredMessage.lockedUntilUtc, - expectedLockExpiryTimeUtc, - "After renewlock" - ); + // Compute expected lock expiry time after renewing lock after 5 seconds + expectedLockExpiryTimeUtc.setSeconds(expectedLockExpiryTimeUtc.getSeconds() + 5); - await context.complete(brokeredMessage); - } + // Verify lock expiry time after renewLock() + assertTimestampsAreApproximatelyEqual( + msgs[0].lockedUntilUtc, + expectedLockExpiryTimeUtc, + "After renewlock()" + ); + + await batch.context.complete(msgs[0]); } - receiverClient.subscribe( - { processMessage, processError }, - { - autoComplete: false, - maxMessageAutoRenewLockDurationInSeconds: 0 - } - ); - await delay(10000); - await receiverClient.close(); + /** + * Test settling of message from Batch Receiver fails after message lock expires + */ + async function testBatchReceiverManualLockRenewalErrorOnLockExpiry( + senderClient: Sender, + receiverClient: Receiver + ): Promise { + const testMessage = TestMessage.getSample(); + await senderClient.send(testMessage); + + const batch = await receiverClient.receiveBatch(1); + const msgs = batch.messages; + + should.equal(Array.isArray(msgs), true, "`ReceivedMessages` is not an array"); + should.equal(msgs.length, 1, "Expected message length does not match"); + should.equal(msgs[0].body, testMessage.body, "MessageBody is different than expected"); + should.equal(msgs[0].messageId, testMessage.messageId, "MessageId is different than expected"); + + // Sleeping 30 seconds... + await delay(lockDurationInMilliseconds + 1000); - if (uncaughtErrorFromHandlers) { - chai.assert.fail(uncaughtErrorFromHandlers.message); + let errorWasThrown: boolean = false; + await batch.context.complete(msgs[0]).catch((err) => { + should.equal(err.code, "MessageLockLostError", "Error code is different than expected"); + errorWasThrown = true; + }); + + should.equal(errorWasThrown, true, "Error thrown flag must be true"); + + // Clean up any left over messages + const unprocessedMsgsBatch = await receiverClient.receiveBatch(1); + await unprocessedMsgsBatch.context.complete(unprocessedMsgsBatch.messages[0]); } - should.equal(numOfMessagesReceived, 1, "Unexpected number of messages"); -} - -interface AutoLockRenewalTestOptions { - maxAutoRenewDurationInSeconds: number | undefined; - delayBeforeAttemptingToCompleteMessageInSeconds: number; - willCompleteFail: boolean; -} - -async function testAutoLockRenewalConfigBehavior( - senderClient: ServiceBusSenderClient, - receiverClient: NonSessionReceiver<"peekLock">, - options: AutoLockRenewalTestOptions, - entityType: TestClientType -): Promise { - let numOfMessagesReceived = 0; - const testMessage = TestMessage.getSample(); - await senderClient.send(testMessage); - - async function processMessage( - brokeredMessage: ReceivedMessage, - context: ContextWithSettlement + /** + * Test renewLock() after receiving a message using Streaming Receiver with autoLockRenewal disabled + */ + async function testStreamingReceiverManualLockRenewalHappyCase( + senderClient: Sender, + receiverClient: Receiver ): Promise { - if (numOfMessagesReceived < 1) { - numOfMessagesReceived++; + let numOfMessagesReceived = 0; + const testMessage = TestMessage.getSample(); + await senderClient.send(testMessage); + + async function processMessage( + brokeredMessage: ReceivedMessage, + context: ContextWithSettlement + ): Promise { + if (numOfMessagesReceived < 1) { + numOfMessagesReceived++; + + should.equal( + brokeredMessage.body, + testMessage.body, + "MessageBody is different than expected" + ); + should.equal( + brokeredMessage.messageId, + testMessage.messageId, + "MessageId is different than expected" + ); + + // Compute expected initial lock expiry time + const expectedLockExpiryTimeUtc = new Date(); + expectedLockExpiryTimeUtc.setSeconds( + expectedLockExpiryTimeUtc.getSeconds() + lockDurationInMilliseconds / 1000 + ); + + // Verify initial expiry time on message + assertTimestampsAreApproximatelyEqual( + brokeredMessage.lockedUntilUtc, + expectedLockExpiryTimeUtc, + "Initial" + ); + + await delay(5000); + await receiverClient.renewMessageLock(brokeredMessage); + + // Compute expected lock expiry time after renewing lock after 5 seconds + expectedLockExpiryTimeUtc.setSeconds(expectedLockExpiryTimeUtc.getSeconds() + 5); + + // Verify actual expiry time on session after first renewal + assertTimestampsAreApproximatelyEqual( + brokeredMessage.lockedUntilUtc, + expectedLockExpiryTimeUtc, + "After renewlock" + ); + + await context.complete(brokeredMessage); + } + } - should.equal( - brokeredMessage.body, - testMessage.body, - "MessageBody is different than expected" - ); - should.equal( - brokeredMessage.messageId, - testMessage.messageId, - "MessageId is different than expected" - ); + receiverClient.subscribe( + { processMessage, processError }, + { + autoComplete: false, + maxMessageAutoRenewLockDurationInSeconds: 0 + } + ); + await delay(10000); + await receiverClient.close(); - // Sleeping... - await delay(options.delayBeforeAttemptingToCompleteMessageInSeconds * 1000); + if (uncaughtErrorFromHandlers) { + chai.assert.fail(uncaughtErrorFromHandlers.message); + } - let errorWasThrown: boolean = false; - await context.complete(brokeredMessage).catch((err) => { - should.equal(err.code, "MessageLockLostError", "Error code is different than expected"); - errorWasThrown = true; - }); + should.equal(numOfMessagesReceived, 1, "Unexpected number of messages"); + } - should.equal(errorWasThrown, options.willCompleteFail, "Error Thrown flag value mismatch"); - } + interface AutoLockRenewalTestOptions { + maxAutoRenewDurationInSeconds: number | undefined; + delayBeforeAttemptingToCompleteMessageInSeconds: number; + willCompleteFail: boolean; } - receiverClient.subscribe( - { processMessage, processError }, - { - autoComplete: false, - maxMessageAutoRenewLockDurationInSeconds: options.maxAutoRenewDurationInSeconds + async function testAutoLockRenewalConfigBehavior( + senderClient: Sender, + receiverClient: Receiver, + options: AutoLockRenewalTestOptions, + entityType: TestClientType + ): Promise { + let numOfMessagesReceived = 0; + const testMessage = TestMessage.getSample(); + await senderClient.send(testMessage); + + async function processMessage( + brokeredMessage: ReceivedMessage, + context: ContextWithSettlement + ): Promise { + if (numOfMessagesReceived < 1) { + numOfMessagesReceived++; + + should.equal( + brokeredMessage.body, + testMessage.body, + "MessageBody is different than expected" + ); + should.equal( + brokeredMessage.messageId, + testMessage.messageId, + "MessageId is different than expected" + ); + + // Sleeping... + await delay(options.delayBeforeAttemptingToCompleteMessageInSeconds * 1000); + + let errorWasThrown: boolean = false; + await context.complete(brokeredMessage).catch((err) => { + should.equal(err.code, "MessageLockLostError", "Error code is different than expected"); + errorWasThrown = true; + }); + + should.equal(errorWasThrown, options.willCompleteFail, "Error Thrown flag value mismatch"); + } + } + + receiverClient.subscribe( + { processMessage, processError }, + { + autoComplete: false, + maxMessageAutoRenewLockDurationInSeconds: options.maxAutoRenewDurationInSeconds + } + ); + await delay(options.delayBeforeAttemptingToCompleteMessageInSeconds * 1000 + 10000); + + if (uncaughtErrorFromHandlers) { + chai.assert.fail(uncaughtErrorFromHandlers.message); } - ); - await delay(options.delayBeforeAttemptingToCompleteMessageInSeconds * 1000 + 10000); - if (uncaughtErrorFromHandlers) { - chai.assert.fail(uncaughtErrorFromHandlers.message); - } + should.equal(numOfMessagesReceived, 1, "Mismatch in number of messages received"); - should.equal(numOfMessagesReceived, 1, "Mismatch in number of messages received"); + if (options.willCompleteFail) { + // Clean up any left over messages + await receiverClient.close(); - if (options.willCompleteFail) { - // Clean up any left over messages - await receiverClient.close(); - receiverClient = ( - await getSenderReceiverClients(entityType, "peekLock", undefined, undefined, false) - ).receiverClient as NonSessionReceiver<"peekLock">; - const unprocessedMsgsBatch = await receiverClient.receiveBatch(1); - if (unprocessedMsgsBatch.messages.length) { - await unprocessedMsgsBatch.context.complete(unprocessedMsgsBatch.messages[0]); + receiverClient = serviceBusClient.test.getPeekLockReceiver( + await serviceBusClient.test.createTestEntities(entityType) + ); + + const unprocessedMsgsBatch = await receiverClient.receiveBatch(1); + if (unprocessedMsgsBatch.messages.length) { + await unprocessedMsgsBatch.context.complete(unprocessedMsgsBatch.messages[0]); + } } } -} - -function assertTimestampsAreApproximatelyEqual( - actualTimeInUTC: Date | undefined, - expectedTimeInUTC: Date, - label: string -): void { - if (actualTimeInUTC) { - should.equal( - Math.pow((actualTimeInUTC.valueOf() - expectedTimeInUTC.valueOf()) / 1000, 2) < 100, // Within +/- 10 seconds - true, - `${label}: Actual time ${actualTimeInUTC} must be approximately equal to ${expectedTimeInUTC}` - ); + + function assertTimestampsAreApproximatelyEqual( + actualTimeInUTC: Date | undefined, + expectedTimeInUTC: Date, + label: string + ): void { + if (actualTimeInUTC) { + should.equal( + Math.pow((actualTimeInUTC.valueOf() - expectedTimeInUTC.valueOf()) / 1000, 2) < 100, // Within +/- 10 seconds + true, + `${label}: Actual time ${actualTimeInUTC} must be approximately equal to ${expectedTimeInUTC}` + ); + } } -} +}); diff --git a/sdk/servicebus/service-bus/test/renewLockSessions.spec.ts b/sdk/servicebus/service-bus/test/renewLockSessions.spec.ts index 0cee8e1fa076..178210c865e8 100644 --- a/sdk/servicebus/service-bus/test/renewLockSessions.spec.ts +++ b/sdk/servicebus/service-bus/test/renewLockSessions.spec.ts @@ -8,580 +8,616 @@ chai.use(chaiAsPromised); import { MessagingError, delay, - ServiceBusSenderClient, - SessionReceiver, ReceivedMessage, - ContextWithSettlement + ContextWithSettlement, + SendableMessageInfo } from "../src"; -import { - getSenderReceiverClients, - TestClientType, - TestMessage, - isMessagingError -} from "./utils/testUtils"; - -let senderClient: ServiceBusSenderClient; -let receiverClient: SessionReceiver<"peekLock">; -let maxSessionAutoRenewLockDurationInSeconds: number; -async function beforeEachTest( - entityType: TestClientType, - maxSessionAutoRenewLockDurationInSeconds: number -): Promise { - const clients = await getSenderReceiverClients(entityType, "peekLock", undefined, { - id: TestMessage.sessionId, - maxSessionAutoRenewLockDurationInSeconds - }); - senderClient = clients.senderClient; - receiverClient = clients.receiverClient as SessionReceiver<"peekLock">; - - // Observation - - // Peeking into an empty session-enabled queue would run into either of the following errors.. - // 1. OperationTimeoutError: Unable to create the amqp receiver 'unpartitioned-queue-sessions-794f89be-3282-8b48-8ae0-a8af43c3ce36' - // on amqp session 'local-1_remote-1_connection-2' due to operation timeout. - // 2. MessagingError: Received an incorrect sessionId 'undefined' while creating the receiver 'unpartitioned-queue-sessions-86662b2b-acdc-1045-8ad4-fa3ab8807871'. - - // getSenderReceiverClients creates brand new queues/topic-subscriptions. - // Hence, commenting the following code since there is no need to purge/peek into a freshly created entity - - // await purge(receiverClient); - // const peekedMsgs = await receiverClient.diagnostics.peek(); - // const receiverEntityType = receiverClient.entityType; - // if (peekedMsgs.length) { - // chai.assert.fail(`Please use an empty ${receiverEntityType} for integration testing`); - // } -} - -async function afterEachTest(): Promise { - await senderClient.close(); - await receiverClient.close(); -} - -describe("Batch Receiver: renewLock() resets lock duration each time", function(): void { - beforeEach(() => { - maxSessionAutoRenewLockDurationInSeconds = 0; - }); +import { TestClientType, TestMessage, isMessagingError } from "./utils/testUtils"; +import { ServiceBusClientForTests, createServiceBusClientForTests } from "./utils/testutils2"; +import { Sender } from "../src/sender"; +import { SessionReceiver } from "../src/receivers/sessionReceiver"; - it("Unpartitioned Queue With Sessions - Lock Renewal for Sessions", async function(): Promise< - void - > { - await beforeEachTest( - TestClientType.UnpartitionedQueueWithSessions, - maxSessionAutoRenewLockDurationInSeconds - ); - await testBatchReceiverManualLockRenewalHappyCase(senderClient, receiverClient); - }); +describe("renew lock sessions", () => { + let sender: Sender; + let receiver: SessionReceiver; + let maxSessionAutoRenewLockDurationInSeconds: number; + let sessionId: string; - it("Partitioned Queue With Sessions - Lock Renewal for Sessions", async function(): Promise< - void - > { - await beforeEachTest( - TestClientType.PartitionedQueueWithSessions, - maxSessionAutoRenewLockDurationInSeconds - ); - await testBatchReceiverManualLockRenewalHappyCase(senderClient, receiverClient); - }); + let serviceBusClient: ServiceBusClientForTests; - it("Unpartitioned Subscription With Sessions - Lock Renewal for Sessions", async function(): Promise< - void - > { - await beforeEachTest( - TestClientType.UnpartitionedSubscriptionWithSessions, - maxSessionAutoRenewLockDurationInSeconds - ); - await testBatchReceiverManualLockRenewalHappyCase(senderClient, receiverClient); + before(async () => { + serviceBusClient = createServiceBusClientForTests(); }); - it("Partitioned Subscription With Sessions - Lock Renewal for Sessions", async function(): Promise< - void - > { - await beforeEachTest( - TestClientType.PartitionedSubscriptionWithSessions, - maxSessionAutoRenewLockDurationInSeconds - ); - await testBatchReceiverManualLockRenewalHappyCase(senderClient, receiverClient); + after(async () => { + await serviceBusClient.test.after(); }); -}); -describe("Batch Receiver: complete() after lock expiry with throws error", function(): void { - beforeEach(() => { - maxSessionAutoRenewLockDurationInSeconds = 0; - }); + async function beforeEachTest( + entityType: TestClientType, + maxSessionAutoRenewLockDurationInSeconds: number + ): Promise { + const entityNames = await serviceBusClient.test.createTestEntities(entityType); - it("Unpartitioned Queue With Sessions - Lock Renewal for Sessions", async function(): Promise< - void - > { - await beforeEachTest( - TestClientType.UnpartitionedQueueWithSessions, - maxSessionAutoRenewLockDurationInSeconds - ); - await testBatchReceiverManualLockRenewalErrorOnLockExpiry( - TestClientType.UnpartitionedQueueWithSessions, - senderClient, - receiverClient + sender = serviceBusClient.test.addToCleanup( + serviceBusClient.getSender(entityNames.queue ?? entityNames.topic!) ); - }); - it("Partitioned Queue With Sessions - Lock Renewal for Sessions", async function(): Promise< - void - > { - await beforeEachTest( - TestClientType.PartitionedQueueWithSessions, - maxSessionAutoRenewLockDurationInSeconds - ); - await testBatchReceiverManualLockRenewalErrorOnLockExpiry( - TestClientType.PartitionedQueueWithSessions, - senderClient, - receiverClient - ); - }); + sessionId = Date.now().toString(); - it("Unpartitioned Subscription With Sessions - Lock Renewal for Sessions", async function(): Promise< - void - > { - await beforeEachTest( - TestClientType.UnpartitionedSubscriptionWithSessions, + receiver = serviceBusClient.test.getSessionPeekLockReceiver(entityNames, { + sessionId, maxSessionAutoRenewLockDurationInSeconds - ); - await testBatchReceiverManualLockRenewalErrorOnLockExpiry( - TestClientType.UnpartitionedSubscriptionWithSessions, - senderClient, - receiverClient - ); - }); + }); - it("Partitioned Subscription With Sessions - Lock Renewal for Sessions", async function(): Promise< - void - > { - await beforeEachTest( - TestClientType.PartitionedSubscriptionWithSessions, - maxSessionAutoRenewLockDurationInSeconds - ); - await testBatchReceiverManualLockRenewalErrorOnLockExpiry( - TestClientType.PartitionedSubscriptionWithSessions, - senderClient, - receiverClient - ); - }); -}); + // Observation - + // Peeking into an empty session-enabled queue would run into either of the following errors.. + // 1. OperationTimeoutError: Unable to create the amqp receiver 'unpartitioned-queue-sessions-794f89be-3282-8b48-8ae0-a8af43c3ce36' + // on amqp session 'local-1_remote-1_connection-2' due to operation timeout. + // 2. MessagingError: Received an incorrect sessionId 'undefined' while creating the receiver 'unpartitioned-queue-sessions-86662b2b-acdc-1045-8ad4-fa3ab8807871'. + + // getSenderReceiverClients creates brand new queues/topic-subscriptions. + // Hence, commenting the following code since there is no need to purge/peek into a freshly created entity + + // await purge(receiverClient); + // const peekedMsgs = await receiverClient.diagnostics.peek(); + // const receiverEntityType = receiverClient.entityType; + // if (peekedMsgs.length) { + // chai.assert.fail(`Please use an empty ${receiverEntityType} for integration testing`); + // } + } -describe("Streaming Receiver: renewLock() resets lock duration each time", function(): void { - beforeEach(() => { - maxSessionAutoRenewLockDurationInSeconds = 0; - }); + describe("Batch Receiver: renewLock() resets lock duration each time", function(): void { + beforeEach(() => { + maxSessionAutoRenewLockDurationInSeconds = 0; + }); - it("Unpartitioned Queue With Sessions - Lock Renewal for Sessions", async function(): Promise< - void - > { - await beforeEachTest( - TestClientType.UnpartitionedQueueWithSessions, - maxSessionAutoRenewLockDurationInSeconds - ); - await testStreamingReceiverManualLockRenewalHappyCase(senderClient, receiverClient); - }); + afterEach(() => { + return serviceBusClient.test.afterEach(); + }); - it("Partitioned Queue With Sessions - Lock Renewal for Sessions", async function(): Promise< - void - > { - await beforeEachTest( - TestClientType.PartitionedQueueWithSessions, - maxSessionAutoRenewLockDurationInSeconds - ); - await testStreamingReceiverManualLockRenewalHappyCase(senderClient, receiverClient); - }); + it("Unpartitioned Queue With Sessions - Lock Renewal for Sessions", async function(): Promise< + void + > { + await beforeEachTest( + TestClientType.UnpartitionedQueueWithSessions, + maxSessionAutoRenewLockDurationInSeconds + ); + await testBatchReceiverManualLockRenewalHappyCase(sender, receiver); + }); - it("Unpartitioned Subscription With Sessions - Lock Renewal for Sessions", async function(): Promise< - void - > { - await beforeEachTest( - TestClientType.UnpartitionedSubscriptionWithSessions, - maxSessionAutoRenewLockDurationInSeconds - ); - await testStreamingReceiverManualLockRenewalHappyCase(senderClient, receiverClient); - }); + it("Partitioned Queue With Sessions - Lock Renewal for Sessions", async function(): Promise< + void + > { + await beforeEachTest( + TestClientType.PartitionedQueueWithSessions, + maxSessionAutoRenewLockDurationInSeconds + ); + await testBatchReceiverManualLockRenewalHappyCase(sender, receiver); + }); - it("Partitioned Subscription With Sessions - Lock Renewal for Sessions", async function(): Promise< - void - > { - await beforeEachTest( - TestClientType.PartitionedSubscriptionWithSessions, - maxSessionAutoRenewLockDurationInSeconds - ); - await testStreamingReceiverManualLockRenewalHappyCase(senderClient, receiverClient); - }); -}); + it("Unpartitioned Subscription With Sessions - Lock Renewal for Sessions", async function(): Promise< + void + > { + await beforeEachTest( + TestClientType.UnpartitionedSubscriptionWithSessions, + maxSessionAutoRenewLockDurationInSeconds + ); + await testBatchReceiverManualLockRenewalHappyCase(sender, receiver); + }); -describe("Streaming Receiver: complete() after lock expiry with auto-renewal disabled throws error", function(): void { - const options: AutoLockRenewalTestOptions = { - maxSessionAutoRenewLockDurationInSeconds: 0, - delayBeforeAttemptingToCompleteMessageInSeconds: 31, - expectSessionLockLostErrorToBeThrown: true - }; - - it("Unpartitioned Queue With Sessions - Lock Renewal for Sessions", async function(): Promise< - void - > { - await beforeEachTest( - TestClientType.UnpartitionedQueueWithSessions, - options.maxSessionAutoRenewLockDurationInSeconds - ); - await testAutoLockRenewalConfigBehavior(senderClient, receiverClient, options); + it("Partitioned Subscription With Sessions - Lock Renewal for Sessions", async function(): Promise< + void + > { + await beforeEachTest( + TestClientType.PartitionedSubscriptionWithSessions, + maxSessionAutoRenewLockDurationInSeconds + ); + await testBatchReceiverManualLockRenewalHappyCase(sender, receiver); + }); }); - it("Partitioned Queue With Sessions - Lock Renewal for Sessions", async function(): Promise< - void - > { - await beforeEachTest( - TestClientType.PartitionedQueueWithSessions, - options.maxSessionAutoRenewLockDurationInSeconds - ); - await testAutoLockRenewalConfigBehavior(senderClient, receiverClient, options); - }); + describe("Batch Receiver: complete() after lock expiry with throws error", function(): void { + beforeEach(() => { + maxSessionAutoRenewLockDurationInSeconds = 0; + }); - it("Unpartitioned Subscription With Sessions - Lock Renewal for Sessions", async function(): Promise< - void - > { - await beforeEachTest( - TestClientType.UnpartitionedSubscriptionWithSessions, - options.maxSessionAutoRenewLockDurationInSeconds - ); - await testAutoLockRenewalConfigBehavior(senderClient, receiverClient, options); - }); + afterEach(() => { + return serviceBusClient.test.afterEach(); + }); - it("Partitioned Subscription With Sessions - Lock Renewal for Sessions", async function(): Promise< - void - > { - await beforeEachTest( - TestClientType.PartitionedSubscriptionWithSessions, - options.maxSessionAutoRenewLockDurationInSeconds - ); - await testAutoLockRenewalConfigBehavior(senderClient, receiverClient, options); - }); -}); + it("Unpartitioned Queue With Sessions - Lock Renewal for Sessions", async function(): Promise< + void + > { + await beforeEachTest( + TestClientType.UnpartitionedQueueWithSessions, + maxSessionAutoRenewLockDurationInSeconds + ); + await testBatchReceiverManualLockRenewalErrorOnLockExpiry( + TestClientType.UnpartitionedQueueWithSessions, + sender, + receiver + ); + }); -describe("Test AutoLockRenewalConfigBehavior - Unpartitioned Queue With Sessions", function(): void { - afterEach(async () => { - await afterEachTest(); - }); + it("Partitioned Queue With Sessions - Lock Renewal for Sessions", async function(): Promise< + void + > { + await beforeEachTest( + TestClientType.PartitionedQueueWithSessions, + maxSessionAutoRenewLockDurationInSeconds + ); + await testBatchReceiverManualLockRenewalErrorOnLockExpiry( + TestClientType.PartitionedQueueWithSessions, + sender, + receiver + ); + }); - it("Streaming Receiver: lock will not expire until configured time", async function(): Promise< - void - > { - maxSessionAutoRenewLockDurationInSeconds = 38; - await beforeEachTest( - TestClientType.UnpartitionedQueueWithSessions, - maxSessionAutoRenewLockDurationInSeconds - ); - await testAutoLockRenewalConfigBehavior(senderClient, receiverClient, { - maxSessionAutoRenewLockDurationInSeconds, - delayBeforeAttemptingToCompleteMessageInSeconds: 35, - expectSessionLockLostErrorToBeThrown: false + it("Unpartitioned Subscription With Sessions - Lock Renewal for Sessions", async function(): Promise< + void + > { + await beforeEachTest( + TestClientType.UnpartitionedSubscriptionWithSessions, + maxSessionAutoRenewLockDurationInSeconds + ); + await testBatchReceiverManualLockRenewalErrorOnLockExpiry( + TestClientType.UnpartitionedSubscriptionWithSessions, + sender, + receiver + ); + }); + + it("Partitioned Subscription With Sessions - Lock Renewal for Sessions", async function(): Promise< + void + > { + await beforeEachTest( + TestClientType.PartitionedSubscriptionWithSessions, + maxSessionAutoRenewLockDurationInSeconds + ); + await testBatchReceiverManualLockRenewalErrorOnLockExpiry( + TestClientType.PartitionedSubscriptionWithSessions, + sender, + receiver + ); }); }); - it("Streaming Receiver: lock expires sometime after configured time", async function(): Promise< - void - > { - maxSessionAutoRenewLockDurationInSeconds = 35; - await beforeEachTest( - TestClientType.UnpartitionedQueueWithSessions, - maxSessionAutoRenewLockDurationInSeconds - ); - await testAutoLockRenewalConfigBehavior(senderClient, receiverClient, { - maxSessionAutoRenewLockDurationInSeconds, - delayBeforeAttemptingToCompleteMessageInSeconds: 80, - expectSessionLockLostErrorToBeThrown: true + describe("Streaming Receiver: renewLock() resets lock duration each time", function(): void { + beforeEach(() => { + maxSessionAutoRenewLockDurationInSeconds = 0; }); - }).timeout(95000); - - it("Receive a msg using Streaming Receiver, lock renewal does not take place when config value is less than lock duration", async function(): Promise< - void - > { - maxSessionAutoRenewLockDurationInSeconds = 15; - await beforeEachTest( - TestClientType.UnpartitionedQueueWithSessions, - maxSessionAutoRenewLockDurationInSeconds - ); - await testAutoLockRenewalConfigBehavior(senderClient, receiverClient, { - maxSessionAutoRenewLockDurationInSeconds, - delayBeforeAttemptingToCompleteMessageInSeconds: 31, - expectSessionLockLostErrorToBeThrown: true + + afterEach(() => { + return serviceBusClient.test.afterEach(); }); - }); -}); -const lockDurationInMilliseconds = 30000; -// const maxSessionAutoRenewLockDurationInSeconds = 300; -let uncaughtErrorFromHandlers: Error | undefined; - -async function processError(err: MessagingError | Error) { - uncaughtErrorFromHandlers = err; -} - -/** - * Test manual renewLock() using Batch Receiver, with autoLockRenewal disabled - */ -async function testBatchReceiverManualLockRenewalHappyCase( - senderClient: ServiceBusSenderClient, - receiverClient: SessionReceiver<"peekLock"> -): Promise { - const testMessage = TestMessage.getSessionSample(); - await senderClient.send(testMessage); - - const batch = await receiverClient.receiveBatch(1); - const msgs = batch.messages; - - // Compute expected initial lock expiry time - const expectedLockExpiryTimeUtc = new Date(); - expectedLockExpiryTimeUtc.setSeconds( - expectedLockExpiryTimeUtc.getSeconds() + lockDurationInMilliseconds / 1000 - ); - - should.equal(Array.isArray(msgs), true, "`ReceivedMessages` is not an array"); - should.equal(msgs.length, 1, "Unexpected number of messages"); - should.equal(msgs[0].body, testMessage.body, "MessageBody is different than expected"); - should.equal(msgs[0].messageId, testMessage.messageId, "MessageId is different than expected"); - - // Verify initial lock expiry time on the session - assertTimestampsAreApproximatelyEqual( - receiverClient.sessionLockedUntilUtc, - expectedLockExpiryTimeUtc, - "Initial" - ); - - await delay(5000); - await receiverClient.renewSessionLock(); - - // Compute expected lock expiry time after renewing lock after 5 seconds - expectedLockExpiryTimeUtc.setSeconds(expectedLockExpiryTimeUtc.getSeconds() + 5); - - // Verify lock expiry time after renewLock() - assertTimestampsAreApproximatelyEqual( - receiverClient.sessionLockedUntilUtc, - expectedLockExpiryTimeUtc, - "After renewlock()" - ); - - await batch.context.complete(msgs[0]); -} - -/** - * Test settling of message from Batch Receiver fails after session lock expires - */ -async function testBatchReceiverManualLockRenewalErrorOnLockExpiry( - entityType: TestClientType, - senderClient: ServiceBusSenderClient, - receiverClient: SessionReceiver<"peekLock"> -): Promise { - const testMessage = TestMessage.getSessionSample(); - await senderClient.send(testMessage); - - const batch = await receiverClient.receiveBatch(1); - const msgs = batch.messages; - - should.equal(Array.isArray(msgs), true, "`ReceivedMessages` is not an array"); - should.equal(msgs.length, 1, "Expected message length does not match"); - should.equal(msgs[0].body, testMessage.body, "MessageBody is different than expected"); - should.equal(msgs[0].messageId, testMessage.messageId, "MessageId is different than expected"); - - await delay(lockDurationInMilliseconds + 1000); - - let errorWasThrown: boolean = false; - await batch.context.complete(msgs[0]).catch((err) => { - should.equal(err.code, "SessionLockLostError", "Error code is different than expected"); - errorWasThrown = true; - }); + it("Unpartitioned Queue With Sessions - Lock Renewal for Sessions", async function(): Promise< + void + > { + await beforeEachTest( + TestClientType.UnpartitionedQueueWithSessions, + maxSessionAutoRenewLockDurationInSeconds + ); + await testStreamingReceiverManualLockRenewalHappyCase(sender, receiver); + }); - should.equal(errorWasThrown, true, "Error thrown flag must be true"); + it("Partitioned Queue With Sessions - Lock Renewal for Sessions", async function(): Promise< + void + > { + await beforeEachTest( + TestClientType.PartitionedQueueWithSessions, + maxSessionAutoRenewLockDurationInSeconds + ); + await testStreamingReceiverManualLockRenewalHappyCase(sender, receiver); + }); - // Clean up the messages. - await receiverClient.close(); - receiverClient = ( - await getSenderReceiverClients( - entityType, - "peekLock", - undefined, - { - id: undefined - }, - false - ) - ).receiverClient as SessionReceiver<"peekLock">; - const unprocessedMsgsBatch = await receiverClient.receiveBatch(1); - should.equal(unprocessedMsgsBatch.messages[0].deliveryCount, 1, "Unexpected deliveryCount"); - await unprocessedMsgsBatch.context.complete(unprocessedMsgsBatch.messages[0]); -} - -/** - * Test manual renewLock() using Streaming Receiver with autoLockRenewal disabled - */ -async function testStreamingReceiverManualLockRenewalHappyCase( - senderClient: ServiceBusSenderClient, - receiverClient: SessionReceiver<"peekLock"> -): Promise { - let numOfMessagesReceived = 0; - const testMessage = TestMessage.getSessionSample(); - await senderClient.send(testMessage); - - async function processMessage(brokeredMessage: ReceivedMessage, context: ContextWithSettlement) { - if (numOfMessagesReceived < 1) { - numOfMessagesReceived++; + it("Unpartitioned Subscription With Sessions - Lock Renewal for Sessions", async function(): Promise< + void + > { + await beforeEachTest( + TestClientType.UnpartitionedSubscriptionWithSessions, + maxSessionAutoRenewLockDurationInSeconds + ); + await testStreamingReceiverManualLockRenewalHappyCase(sender, receiver); + }); - should.equal( - brokeredMessage.body, - testMessage.body, - "MessageBody is different than expected" + it("Partitioned Subscription With Sessions - Lock Renewal for Sessions", async function(): Promise< + void + > { + await beforeEachTest( + TestClientType.PartitionedSubscriptionWithSessions, + maxSessionAutoRenewLockDurationInSeconds ); - should.equal( - brokeredMessage.messageId, - testMessage.messageId, - "MessageId is different than expected" + await testStreamingReceiverManualLockRenewalHappyCase(sender, receiver); + }); + }); + + describe("Streaming Receiver: complete() after lock expiry with auto-renewal disabled throws error", function(): void { + afterEach(() => { + return serviceBusClient.test.afterEach(); + }); + + const options: AutoLockRenewalTestOptions = { + maxSessionAutoRenewLockDurationInSeconds: 0, + delayBeforeAttemptingToCompleteMessageInSeconds: 31, + expectSessionLockLostErrorToBeThrown: true + }; + + it("Unpartitioned Queue With Sessions - Lock Renewal for Sessions", async function(): Promise< + void + > { + await beforeEachTest( + TestClientType.UnpartitionedQueueWithSessions, + options.maxSessionAutoRenewLockDurationInSeconds ); + await testAutoLockRenewalConfigBehavior(sender, receiver, options); + }); - // Compute expected initial lock expiry time - const expectedLockExpiryTimeUtc = new Date(); - expectedLockExpiryTimeUtc.setSeconds( - expectedLockExpiryTimeUtc.getSeconds() + lockDurationInMilliseconds / 1000 + it("Partitioned Queue With Sessions - Lock Renewal for Sessions", async function(): Promise< + void + > { + await beforeEachTest( + TestClientType.PartitionedQueueWithSessions, + options.maxSessionAutoRenewLockDurationInSeconds ); + await testAutoLockRenewalConfigBehavior(sender, receiver, options); + }); + + it("Unpartitioned Subscription With Sessions - Lock Renewal for Sessions", async function(): Promise< + void + > { + await beforeEachTest( + TestClientType.UnpartitionedSubscriptionWithSessions, + options.maxSessionAutoRenewLockDurationInSeconds + ); + await testAutoLockRenewalConfigBehavior(sender, receiver, options); + }); - // Verify initial expiry time on session - assertTimestampsAreApproximatelyEqual( - receiverClient.sessionLockedUntilUtc, - expectedLockExpiryTimeUtc, - "Initial" + it("Partitioned Subscription With Sessions - Lock Renewal for Sessions", async function(): Promise< + void + > { + await beforeEachTest( + TestClientType.PartitionedSubscriptionWithSessions, + options.maxSessionAutoRenewLockDurationInSeconds ); + await testAutoLockRenewalConfigBehavior(sender, receiver, options); + }); + }); - await delay(5000); - await receiverClient.renewSessionLock(); + describe("Test AutoLockRenewalConfigBehavior - Unpartitioned Queue With Sessions", function(): void { + afterEach(() => { + return serviceBusClient.test.afterEach(); + }); - // Compute expected lock expiry time after renewing lock after 5 seconds - expectedLockExpiryTimeUtc.setSeconds(expectedLockExpiryTimeUtc.getSeconds() + 5); + it("Streaming Receiver: lock will not expire until configured time", async function(): Promise< + void + > { + maxSessionAutoRenewLockDurationInSeconds = 38; + await beforeEachTest( + TestClientType.UnpartitionedQueueWithSessions, + maxSessionAutoRenewLockDurationInSeconds + ); + await testAutoLockRenewalConfigBehavior(sender, receiver, { + maxSessionAutoRenewLockDurationInSeconds, + delayBeforeAttemptingToCompleteMessageInSeconds: 35, + expectSessionLockLostErrorToBeThrown: false + }); + }); - // Verify actual expiry time on session after renewal - assertTimestampsAreApproximatelyEqual( - receiverClient.sessionLockedUntilUtc, - expectedLockExpiryTimeUtc, - "After renewlock()" + it("Streaming Receiver: lock expires sometime after configured time", async function(): Promise< + void + > { + maxSessionAutoRenewLockDurationInSeconds = 35; + await beforeEachTest( + TestClientType.UnpartitionedQueueWithSessions, + maxSessionAutoRenewLockDurationInSeconds + ); + await testAutoLockRenewalConfigBehavior(sender, receiver, { + maxSessionAutoRenewLockDurationInSeconds, + delayBeforeAttemptingToCompleteMessageInSeconds: 80, + expectSessionLockLostErrorToBeThrown: true + }); + }).timeout(95000); + + it("Receive a msg using Streaming Receiver, lock renewal does not take place when config value is less than lock duration", async function(): Promise< + void + > { + maxSessionAutoRenewLockDurationInSeconds = 15; + await beforeEachTest( + TestClientType.UnpartitionedQueueWithSessions, + maxSessionAutoRenewLockDurationInSeconds ); + await testAutoLockRenewalConfigBehavior(sender, receiver, { + maxSessionAutoRenewLockDurationInSeconds, + delayBeforeAttemptingToCompleteMessageInSeconds: 31, + expectSessionLockLostErrorToBeThrown: true + }); + }); + }); - await context.complete(brokeredMessage); - } + const lockDurationInMilliseconds = 30000; + // const maxSessionAutoRenewLockDurationInSeconds = 300; + let uncaughtErrorFromHandlers: Error | undefined; + + async function processError(err: MessagingError | Error) { + uncaughtErrorFromHandlers = err; } - receiverClient.subscribe( - { processMessage, processError }, - { - autoComplete: false - } - ); - await delay(10000); - await receiverClient.close(); + /** + * Test manual renewLock() using Batch Receiver, with autoLockRenewal disabled + */ + async function testBatchReceiverManualLockRenewalHappyCase( + senderClient: Sender, + receiverClient: SessionReceiver + ): Promise { + const testMessage = getTestMessage(); + testMessage.body = `testBatchReceiverManualLockRenewalHappyCase-${Date.now().toString()}`; + await senderClient.send(testMessage); - if (uncaughtErrorFromHandlers) { - chai.assert.fail(uncaughtErrorFromHandlers.message); + const batch = await receiverClient.receiveBatch(1); + const msgs = batch.messages; + + // Compute expected initial lock expiry time + const expectedLockExpiryTimeUtc = new Date(); + expectedLockExpiryTimeUtc.setSeconds( + expectedLockExpiryTimeUtc.getSeconds() + lockDurationInMilliseconds / 1000 + ); + + should.equal(Array.isArray(msgs), true, "`ReceivedMessages` is not an array"); + should.equal(msgs.length, 1, "Unexpected number of messages"); + should.equal(msgs[0].body, testMessage.body, "MessageBody is different than expected"); + should.equal(msgs[0].messageId, testMessage.messageId, "MessageId is different than expected"); + + // Verify initial lock expiry time on the session + assertTimestampsAreApproximatelyEqual( + receiverClient.sessionLockedUntilUtc, + expectedLockExpiryTimeUtc, + "Initial" + ); + + await delay(5000); + await receiverClient.renewSessionLock(); + + // Compute expected lock expiry time after renewing lock after 5 seconds + expectedLockExpiryTimeUtc.setSeconds(expectedLockExpiryTimeUtc.getSeconds() + 5); + + // Verify lock expiry time after renewLock() + assertTimestampsAreApproximatelyEqual( + receiverClient.sessionLockedUntilUtc, + expectedLockExpiryTimeUtc, + "After renewlock()" + ); + + await batch.context.complete(msgs[0]); } - should.equal(numOfMessagesReceived, 1, "Unexpected number of messages"); -} - -interface AutoLockRenewalTestOptions { - maxSessionAutoRenewLockDurationInSeconds: number; - delayBeforeAttemptingToCompleteMessageInSeconds: number; - expectSessionLockLostErrorToBeThrown: boolean; -} - -async function testAutoLockRenewalConfigBehavior( - senderClient: ServiceBusSenderClient, - receiverClient: SessionReceiver<"peekLock">, - options: AutoLockRenewalTestOptions -): Promise { - let numOfMessagesReceived = 0; - const testMessage = TestMessage.getSessionSample(); - await senderClient.send(testMessage); - - let sessionLockLostErrorThrown = false; - const messagesReceived: ReceivedMessage[] = []; - let contextToSettle: ContextWithSettlement; - - async function processMessage( - brokeredMessage: ReceivedMessage, - context: ContextWithSettlement + /** + * Test settling of message from Batch Receiver fails after session lock expires + */ + async function testBatchReceiverManualLockRenewalErrorOnLockExpiry( + entityType: TestClientType, + senderClient: Sender, + receiver: SessionReceiver ): Promise { - if (numOfMessagesReceived < 1) { - numOfMessagesReceived++; + const testMessage = getTestMessage(); + testMessage.body = `testBatchReceiverManualLockRenewalErrorOnLockExpiry-${Date.now().toString()}`; + await senderClient.send(testMessage); - should.equal( - brokeredMessage.body, - testMessage.body, - "MessageBody is different than expected" - ); - should.equal( - brokeredMessage.messageId, - testMessage.messageId, - "MessageId is different than expected" - ); + const batch = await receiver.receiveBatch(1); + const msgs = batch.messages; + + should.equal(Array.isArray(msgs), true, "`ReceivedMessages` is not an array"); + should.equal(msgs.length, 1, "Expected message length does not match"); + should.equal(msgs[0].body, testMessage.body, "MessageBody is different than expected"); + should.equal(msgs[0].messageId, testMessage.messageId, "MessageId is different than expected"); + + await delay(lockDurationInMilliseconds + 1000); + + let errorWasThrown: boolean = false; + await batch.context.complete(msgs[0]).catch((err) => { + should.equal(err.code, "SessionLockLostError", "Error code is different than expected"); + errorWasThrown = true; + }); + + should.equal(errorWasThrown, true, "Error thrown flag must be true"); + + // Clean up the messages. + await receiver.close(); - messagesReceived.push(brokeredMessage); - contextToSettle = context; + const entityNames = serviceBusClient.test.getTestEntities(entityType); + receiver = serviceBusClient.test.getSessionPeekLockReceiver(entityNames); - // Sleeping... - await delay(options.delayBeforeAttemptingToCompleteMessageInSeconds * 1000); + const unprocessedMsgsBatch = await receiver.receiveBatch(1); + should.equal(unprocessedMsgsBatch.messages[0].deliveryCount, 1, "Unexpected deliveryCount"); + await unprocessedMsgsBatch.context.complete(unprocessedMsgsBatch.messages[0]); + } + + /** + * Test manual renewLock() using Streaming Receiver with autoLockRenewal disabled + */ + async function testStreamingReceiverManualLockRenewalHappyCase( + senderClient: Sender, + receiverClient: SessionReceiver + ): Promise { + let numOfMessagesReceived = 0; + const testMessage = getTestMessage(); + testMessage.body = `testStreamingReceiverManualLockRenewalHappyCase-${Date.now().toString()}`; + await senderClient.send(testMessage); + + async function processMessage( + brokeredMessage: ReceivedMessage, + context: ContextWithSettlement + ) { + if (numOfMessagesReceived < 1) { + numOfMessagesReceived++; + + should.equal( + brokeredMessage.body, + testMessage.body, + "MessageBody is different than expected" + ); + should.equal( + brokeredMessage.messageId, + testMessage.messageId, + "MessageId is different than expected" + ); + + // Compute expected initial lock expiry time + const expectedLockExpiryTimeUtc = new Date(); + expectedLockExpiryTimeUtc.setSeconds( + expectedLockExpiryTimeUtc.getSeconds() + lockDurationInMilliseconds / 1000 + ); + + // Verify initial expiry time on session + assertTimestampsAreApproximatelyEqual( + receiverClient.sessionLockedUntilUtc, + expectedLockExpiryTimeUtc, + "Initial" + ); + + await delay(5000); + await receiverClient.renewSessionLock(); + + // Compute expected lock expiry time after renewing lock after 5 seconds + expectedLockExpiryTimeUtc.setSeconds(expectedLockExpiryTimeUtc.getSeconds() + 5); + + // Verify actual expiry time on session after renewal + assertTimestampsAreApproximatelyEqual( + receiverClient.sessionLockedUntilUtc, + expectedLockExpiryTimeUtc, + "After renewlock()" + ); + + await context.complete(brokeredMessage); + } } + + receiverClient.subscribe( + { processMessage, processError }, + { + autoComplete: false + } + ); + await delay(10000); + await receiverClient.close(); + + if (uncaughtErrorFromHandlers) { + chai.assert.fail(uncaughtErrorFromHandlers.message); + } + + should.equal(numOfMessagesReceived, 1, "Unexpected number of messages"); } - receiverClient.subscribe( - { - processMessage, - async processError(err: MessagingError | Error) { - if (isMessagingError(err) && err.code === "SessionLockLostError") { - sessionLockLostErrorThrown = true; - } else { - uncaughtErrorFromHandlers = err; - } + interface AutoLockRenewalTestOptions { + maxSessionAutoRenewLockDurationInSeconds: number; + delayBeforeAttemptingToCompleteMessageInSeconds: number; + expectSessionLockLostErrorToBeThrown: boolean; + } + + async function testAutoLockRenewalConfigBehavior( + senderClient: Sender, + receiverClient: SessionReceiver, + options: AutoLockRenewalTestOptions + ): Promise { + let numOfMessagesReceived = 0; + const testMessage = getTestMessage(); + testMessage.body = `testAutoLockRenewalConfigBehavior-${Date.now().toString()}`; + await senderClient.send(testMessage); + + let sessionLockLostErrorThrown = false; + const messagesReceived: ReceivedMessage[] = []; + let contextToSettle: ContextWithSettlement; + + async function processMessage( + brokeredMessage: ReceivedMessage, + context: ContextWithSettlement + ): Promise { + if (numOfMessagesReceived < 1) { + numOfMessagesReceived++; + + should.equal( + brokeredMessage.body, + testMessage.body, + "MessageBody is different than expected" + ); + should.equal( + brokeredMessage.messageId, + testMessage.messageId, + "MessageId is different than expected" + ); + + messagesReceived.push(brokeredMessage); + contextToSettle = context; + + // Sleeping... + await delay(options.delayBeforeAttemptingToCompleteMessageInSeconds * 1000); } - }, - { - autoComplete: false } - ); - await delay(options.delayBeforeAttemptingToCompleteMessageInSeconds * 1000 + 2000); - should.equal( - sessionLockLostErrorThrown, - options.expectSessionLockLostErrorToBeThrown, - "SessionLockLostErrorThrown flag must match" - ); + receiverClient.subscribe( + { + processMessage, + async processError(err: MessagingError | Error) { + if (isMessagingError(err) && err.code === "SessionLockLostError") { + sessionLockLostErrorThrown = true; + } else { + uncaughtErrorFromHandlers = err; + } + } + }, + { + autoComplete: false + } + ); - should.equal(messagesReceived.length, 1, "Mismatch in number of messages received"); + await delay(options.delayBeforeAttemptingToCompleteMessageInSeconds * 1000 + 2000); - let errorWasThrown: boolean = false; - await contextToSettle!.complete(messagesReceived[0]).catch((err) => { - should.equal(err.code, "SessionLockLostError", "Error code is different than expected"); - errorWasThrown = true; - }); + should.not.exist(uncaughtErrorFromHandlers?.message); + should.equal( + sessionLockLostErrorThrown, + options.expectSessionLockLostErrorToBeThrown, + "SessionLockLostErrorThrown flag must match" + ); - should.equal( - errorWasThrown, - options.expectSessionLockLostErrorToBeThrown, - "Error Thrown flag value mismatch" - ); + should.equal(messagesReceived.length, 1, "Mismatch in number of messages received"); - await receiverClient.close(); + let errorWasThrown: boolean = false; + await contextToSettle!.complete(messagesReceived[0]).catch((err) => { + should.equal(err.code, "SessionLockLostError", "Error code is different than expected"); + errorWasThrown = true; + }); - if (uncaughtErrorFromHandlers) { - chai.assert.fail(uncaughtErrorFromHandlers.message); - } -} - -function assertTimestampsAreApproximatelyEqual( - actualTimeInUTC: Date | undefined, - expectedTimeInUTC: Date, - label: string -): void { - if (actualTimeInUTC) { should.equal( - Math.pow((actualTimeInUTC.valueOf() - expectedTimeInUTC.valueOf()) / 1000, 2) < 100, // Within +/- 10 seconds - true, - `${label}: Actual time ${actualTimeInUTC} must be approximately equal to ${expectedTimeInUTC}` + errorWasThrown, + options.expectSessionLockLostErrorToBeThrown, + "Error Thrown flag value mismatch" ); + + await receiverClient.close(); + + if (uncaughtErrorFromHandlers) { + chai.assert.fail(uncaughtErrorFromHandlers.message); + } } -} + + function assertTimestampsAreApproximatelyEqual( + actualTimeInUTC: Date | undefined, + expectedTimeInUTC: Date, + label: string + ): void { + if (actualTimeInUTC) { + should.equal( + Math.pow((actualTimeInUTC.valueOf() - expectedTimeInUTC.valueOf()) / 1000, 2) < 100, // Within +/- 10 seconds + true, + `${label}: Actual time ${actualTimeInUTC} must be approximately equal to ${expectedTimeInUTC}` + ); + } + } + + function getTestMessage(): SendableMessageInfo { + const baseMessage = TestMessage.getSessionSample(); + baseMessage.sessionId = sessionId; + return baseMessage; + } +}); diff --git a/sdk/servicebus/service-bus/test/sendSchedule.spec.ts b/sdk/servicebus/service-bus/test/sendSchedule.spec.ts index 33cd3920799e..8f949a4313f3 100644 --- a/sdk/servicebus/service-bus/test/sendSchedule.spec.ts +++ b/sdk/servicebus/service-bus/test/sendSchedule.spec.ts @@ -5,663 +5,631 @@ import chai from "chai"; const should = chai.should(); import chaiAsPromised from "chai-as-promised"; chai.use(chaiAsPromised); -import { delay, SendableMessageInfo, ServiceBusSenderClient } from "../src"; +import { delay, SendableMessageInfo, ContextWithSettlement } from "../src"; +import { TestMessage, TestClientType } from "./utils/testUtils"; +import { Receiver } from "../src/receivers/receiver"; import { - TestMessage, - getSenderReceiverClients, - TestClientType, - purge, - isSessionfulEntity, - ReceiverClientTypeForUser, - ReceiverClientTypeForUserT -} from "./utils/testUtils"; - -async function testPeekMsgsLength( - client: ReceiverClientTypeForUser, - expectedPeekLength: number -): Promise { - const peekedMsgs = await client.diagnostics.peek(expectedPeekLength + 1); - should.equal( - peekedMsgs.length, - expectedPeekLength, - "Unexpected number of msgs found when peeking" - ); -} - -async function testReceivedMsgsLength( - receiverClient: ReceiverClientTypeForUser, - expectedReceivedMsgsLength: number -): Promise { - const receivedMsgs = await receiverClient.receiveBatch(expectedReceivedMsgsLength + 1, 5); - - should.equal( - receivedMsgs.messages.length, - expectedReceivedMsgsLength, - "Unexpected number of msgs found when receiving" - ); -} - -let senderClient: ServiceBusSenderClient; -let receiverClient: ReceiverClientTypeForUserT<"peekLock">; - -async function beforeEachTest(entityType: TestClientType): Promise { - let clients; - if (isSessionfulEntity(entityType)) { - clients = await getSenderReceiverClients(entityType, "peekLock", undefined, { - id: TestMessage.sessionId - }); - } else { - clients = await getSenderReceiverClients(entityType, "peekLock"); - } - senderClient = clients.senderClient; - receiverClient = clients.receiverClient; - - await purge(receiverClient); - const peekedMsgs = await receiverClient.diagnostics.peek(); - const receiverEntityType = receiverClient.entityType; - if (peekedMsgs.length) { - chai.assert.fail(`Please use an empty ${receiverEntityType} for integration testing`); - } -} - -async function afterEachTest(): Promise { - await senderClient.close(); - await receiverClient.close(); -} - -describe("Simple Send", function(): void { - afterEach(async () => { - await afterEachTest(); - }); - - async function testSimpleSend(useSessions: boolean, usePartitions: boolean): Promise { - const testMessage = useSessions ? TestMessage.getSessionSample() : TestMessage.getSample(); - await senderClient.send(testMessage); - const batch = await receiverClient.receiveBatch(1); - const msgs = batch.messages; - - should.equal(Array.isArray(msgs), true, "`ReceivedMessages` is not an array"); - should.equal(msgs.length, 1, "Unexpected number of messages"); - should.equal(msgs[0].deliveryCount, 0, "DeliveryCount is different than expected"); - - TestMessage.checkMessageContents(testMessage, msgs[0], useSessions, usePartitions); - - await batch.context.complete(msgs[0]); - - await testPeekMsgsLength(receiverClient, 0); - } - - it("Partitioned Queue: Simple Send", async function(): Promise { - await beforeEachTest(TestClientType.PartitionedQueue); - await testSimpleSend(false, true); - }); + ServiceBusClientForTests, + createServiceBusClientForTests, + testPeekMsgsLength +} from "./utils/testutils2"; +import { Sender } from "../src/sender"; - it("Partitioned Topic: Simple Send", async function(): Promise { - await beforeEachTest(TestClientType.PartitionedSubscription); - await testSimpleSend(false, true); - }); +describe("send scheduled messages", () => { + let senderClient: Sender; + let receiverClient: Receiver; + let serviceBusClient: ServiceBusClientForTests; - it("Unpartitioned Queue: Simple Send #RunInBrowser", async function(): Promise { - await beforeEachTest(TestClientType.UnpartitionedQueue); - await testSimpleSend(false, false); + before(() => { + serviceBusClient = createServiceBusClientForTests(); }); - it("Unpartitioned Topic: Simple Send", async function(): Promise { - await beforeEachTest(TestClientType.UnpartitionedSubscription); - await testSimpleSend(false, false); + after(() => { + return serviceBusClient.test.after(); }); - it("Partitioned Queue with Sessions: Simple Send", async function(): Promise { - await beforeEachTest(TestClientType.PartitionedQueueWithSessions); - await testSimpleSend(true, true); - }); + async function beforeEachTest(entityType: TestClientType): Promise { + const entityNames = await serviceBusClient.test.createTestEntities(entityType); + receiverClient = serviceBusClient.test.getPeekLockReceiver(entityNames); - it("Partitioned Topic with Sessions: Simple Send", async function(): Promise { - await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); - await testSimpleSend(true, true); - }); + senderClient = serviceBusClient.test.addToCleanup( + serviceBusClient.getSender(entityNames.queue ?? entityNames.topic!) + ); + } - it("Unpartitioned Queue with Sessions: Simple Send", async function(): Promise { - await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); - await testSimpleSend(true, false); - }); + async function afterEachTest(): Promise { + await senderClient.close(); + await receiverClient.close(); + } - it("Unpartitioned Topic with Sessions: Simple Send", async function(): Promise { - await beforeEachTest(TestClientType.UnpartitionedSubscriptionWithSessions); - await testSimpleSend(true, false); - }); -}); + describe("Simple Send", function(): void { + afterEach(async () => { + await afterEachTest(); + }); -describe("Simple Send Batch", function(): void { - afterEach(async () => { - await afterEachTest(); - }); + async function testSimpleSend(useSessions: boolean, usePartitions: boolean): Promise { + const testMessage = useSessions ? TestMessage.getSessionSample() : TestMessage.getSample(); + await senderClient.send(testMessage); + const batch = await receiverClient.receiveBatch(1); + const msgs = batch.messages; - async function testSimpleSendBatch(useSessions: boolean, usePartitions: boolean): Promise { - const testMessages = []; - testMessages.push(useSessions ? TestMessage.getSessionSample() : TestMessage.getSample()); - testMessages.push(useSessions ? TestMessage.getSessionSample() : TestMessage.getSample()); + should.equal(Array.isArray(msgs), true, "`ReceivedMessages` is not an array"); + should.equal(msgs.length, 1, "Unexpected number of messages"); + should.equal(msgs[0].deliveryCount, 0, "DeliveryCount is different than expected"); - await senderClient.sendBatch(testMessages); - const batch = await receiverClient.receiveBatch(2); - const msgs = batch.messages; + TestMessage.checkMessageContents(testMessage, msgs[0], useSessions, usePartitions); - should.equal(Array.isArray(msgs), true, "`ReceivedMessages` is not an array"); - should.equal(msgs.length, 2, "Unexpected number of messages"); + await batch.context.complete(msgs[0]); - if (testMessages[0].messageId === msgs[0].messageId) { - TestMessage.checkMessageContents(testMessages[0], msgs[0], useSessions, usePartitions); - TestMessage.checkMessageContents(testMessages[1], msgs[1], useSessions, usePartitions); - } else { - TestMessage.checkMessageContents(testMessages[1], msgs[0], useSessions, usePartitions); - TestMessage.checkMessageContents(testMessages[0], msgs[1], useSessions, usePartitions); + await testPeekMsgsLength(receiverClient, 0); } - await batch.context.complete(msgs[0]); - await batch.context.complete(msgs[1]); + it("Partitioned Queue: Simple Send", async function(): Promise { + await beforeEachTest(TestClientType.PartitionedQueue); + await testSimpleSend(false, true); + }); - await testPeekMsgsLength(receiverClient, 0); - } + it("Partitioned Topic: Simple Send", async function(): Promise { + await beforeEachTest(TestClientType.PartitionedSubscription); + await testSimpleSend(false, true); + }); - it("Partitioned Queue: Simple SendBatch", async function(): Promise { - await beforeEachTest(TestClientType.PartitionedQueue); - await testSimpleSendBatch(false, true); - }); + it("Unpartitioned Queue: Simple Send #RunInBrowser", async function(): Promise { + await beforeEachTest(TestClientType.UnpartitionedQueue); + await testSimpleSend(false, false); + }); - it("Partitioned Topic: Simple SendBatch", async function(): Promise { - await beforeEachTest(TestClientType.PartitionedSubscription); - await testSimpleSendBatch(false, true); - }); + it("Unpartitioned Topic: Simple Send", async function(): Promise { + await beforeEachTest(TestClientType.UnpartitionedSubscription); + await testSimpleSend(false, false); + }); - it("Unpartitioned Queue: Simple SendBatch #RunInBrowser", async function(): Promise { - await beforeEachTest(TestClientType.UnpartitionedQueue); - await testSimpleSendBatch(false, false); - }); + it("Partitioned Queue with Sessions: Simple Send", async function(): Promise { + await beforeEachTest(TestClientType.PartitionedQueueWithSessions); + await testSimpleSend(true, true); + }); - it("Unpartitioned Topic: Simple SendBatch", async function(): Promise { - await beforeEachTest(TestClientType.UnpartitionedSubscription); - await testSimpleSendBatch(false, false); - }); + it("Partitioned Topic with Sessions: Simple Send", async function(): Promise { + await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); + await testSimpleSend(true, true); + }); - it("Partitioned Queue with Sessions: Simple SendBatch", async function(): Promise { - await beforeEachTest(TestClientType.PartitionedQueueWithSessions); - await testSimpleSendBatch(true, true); - }); + it("Unpartitioned Queue with Sessions: Simple Send", async function(): Promise { + await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); + await testSimpleSend(true, false); + }); - it("Partitioned Topic with Sessions: Simple SendBatch", async function(): Promise { - await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); - await testSimpleSendBatch(true, true); + it("Unpartitioned Topic with Sessions: Simple Send", async function(): Promise { + await beforeEachTest(TestClientType.UnpartitionedSubscriptionWithSessions); + await testSimpleSend(true, false); + }); }); - it("Unpartitioned Queue with Sessions: Simple SendBatch", async function(): Promise { - await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); - await testSimpleSendBatch(true, false); - }); + describe("Simple Send Batch", function(): void { + afterEach(async () => { + await afterEachTest(); + }); - it("Unpartitioned Topic with Sessions: Simple SendBatch", async function(): Promise { - await beforeEachTest(TestClientType.UnpartitionedSubscriptionWithSessions); - await testSimpleSendBatch(true, false); - }); -}); + async function testSimpleSendBatch( + useSessions: boolean, + usePartitions: boolean + ): Promise { + const testMessages = []; + testMessages.push(useSessions ? TestMessage.getSessionSample() : TestMessage.getSample()); + testMessages.push(useSessions ? TestMessage.getSessionSample() : TestMessage.getSample()); + + await senderClient.sendBatch(testMessages); + const batch = await receiverClient.receiveBatch(2); + const msgs = batch.messages; + + should.equal(Array.isArray(msgs), true, "`ReceivedMessages` is not an array"); + should.equal(msgs.length, 2, "Unexpected number of messages"); + + if (testMessages[0].messageId === msgs[0].messageId) { + TestMessage.checkMessageContents(testMessages[0], msgs[0], useSessions, usePartitions); + TestMessage.checkMessageContents(testMessages[1], msgs[1], useSessions, usePartitions); + } else { + TestMessage.checkMessageContents(testMessages[1], msgs[0], useSessions, usePartitions); + TestMessage.checkMessageContents(testMessages[0], msgs[1], useSessions, usePartitions); + } -describe("Schedule single message", function(): void { - afterEach(async () => { - await afterEachTest(); - }); + await batch.context.complete(msgs[0]); + await batch.context.complete(msgs[1]); - /** - * Schedules a test message message to be sent at a later time, waits and then receives it - * @param useSessions Set to true if using session enabled queues or subscriptions - * @param useScheduleMessages Boolean to indicate whether to use `scheduleMessage` or - * `scheduleMessages` to ensure both get code coverage - */ - async function testScheduleMessage( - useSessions: boolean, - useScheduleMessages: boolean - ): Promise { - const testMessage = useSessions ? TestMessage.getSessionSample() : TestMessage.getSample(); - const scheduleTime = new Date(Date.now() + 10000); // 10 seconds from - - // Randomly choose scheduleMessage/scheduleMessages as the latter is expected to convert single - // input to array and then use it - if (useScheduleMessages) { - await senderClient.scheduleMessages(scheduleTime, testMessage as any); - } else { - await senderClient.scheduleMessage(scheduleTime, testMessage); + await testPeekMsgsLength(receiverClient, 0); } - const batch = await receiverClient.receiveBatch(1); - const msgs = batch.messages; - const msgEnqueueTime = msgs[0].enqueuedTimeUtc ? msgs[0].enqueuedTimeUtc.valueOf() : 0; + it("Partitioned Queue: Simple SendBatch", async function(): Promise { + await beforeEachTest(TestClientType.PartitionedQueue); + await testSimpleSendBatch(false, true); + }); - should.equal(Array.isArray(msgs), true, "`ReceivedMessages` is not an array"); - should.equal(msgs.length, 1, "Unexpected number of messages"); - should.equal( - msgEnqueueTime - scheduleTime.valueOf() >= 0, - true, - "Enqueued time must be greater than scheduled time" - ); // checking received message enqueue time is greater or equal to the scheduled time. - should.equal(msgs[0].body, testMessage.body, "MessageBody is different than expected"); - should.equal(msgs[0].messageId, testMessage.messageId, "MessageId is different than expected"); + it("Partitioned Topic: Simple SendBatch", async function(): Promise { + await beforeEachTest(TestClientType.PartitionedSubscription); + await testSimpleSendBatch(false, true); + }); - await batch.context.complete(msgs[0]); + it("Unpartitioned Queue: Simple SendBatch #RunInBrowser", async function(): Promise { + await beforeEachTest(TestClientType.UnpartitionedQueue); + await testSimpleSendBatch(false, false); + }); - await testPeekMsgsLength(receiverClient, 0); - } + it("Unpartitioned Topic: Simple SendBatch", async function(): Promise { + await beforeEachTest(TestClientType.UnpartitionedSubscription); + await testSimpleSendBatch(false, false); + }); - it("Partitioned Queue: Schedule single message", async function(): Promise { - await beforeEachTest(TestClientType.PartitionedQueue); - await testScheduleMessage(false, true); - }); + it("Partitioned Queue with Sessions: Simple SendBatch", async function(): Promise { + await beforeEachTest(TestClientType.PartitionedQueueWithSessions); + await testSimpleSendBatch(true, true); + }); - it("Partitioned Topic: Schedule single message", async function(): Promise { - await beforeEachTest(TestClientType.PartitionedSubscription); - await testScheduleMessage(false, false); - }); + it("Partitioned Topic with Sessions: Simple SendBatch", async function(): Promise { + await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); + await testSimpleSendBatch(true, true); + }); - it("Unpartitioned Queue: Schedule single message", async function(): Promise { - await beforeEachTest(TestClientType.UnpartitionedQueue); - await testScheduleMessage(false, false); - }); + it("Unpartitioned Queue with Sessions: Simple SendBatch", async function(): Promise { + await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); + await testSimpleSendBatch(true, false); + }); - it("Unpartitioned Topic: Schedule single message", async function(): Promise { - await beforeEachTest(TestClientType.UnpartitionedSubscription); - await testScheduleMessage(false, true); + it("Unpartitioned Topic with Sessions: Simple SendBatch", async function(): Promise { + await beforeEachTest(TestClientType.UnpartitionedSubscriptionWithSessions); + await testSimpleSendBatch(true, false); + }); }); - it("Partitioned Queue with Sessions: Schedule single message", async function(): Promise { - await beforeEachTest(TestClientType.PartitionedQueueWithSessions); - await testScheduleMessage(true, true); - }); + describe("Schedule single message", function(): void { + afterEach(async () => { + await afterEachTest(); + }); - it("Partitioned Topic with Sessions: Schedule single message", async function(): Promise { - await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); - await testScheduleMessage(true, true); - }); + /** + * Schedules a test message message to be sent at a later time, waits and then receives it + * @param useSessions Set to true if using session enabled queues or subscriptions + * @param useScheduleMessages Boolean to indicate whether to use `scheduleMessage` or + * `scheduleMessages` to ensure both get code coverage + */ + async function testScheduleMessage( + useSessions: boolean, + useScheduleMessages: boolean + ): Promise { + const testMessage = useSessions ? TestMessage.getSessionSample() : TestMessage.getSample(); + const scheduleTime = new Date(Date.now() + 10000); // 10 seconds from + + // Randomly choose scheduleMessage/scheduleMessages as the latter is expected to convert single + // input to array and then use it + if (useScheduleMessages) { + await senderClient.scheduleMessages(scheduleTime, testMessage as any); + } else { + await senderClient.scheduleMessage(scheduleTime, testMessage); + } - it("Unpartitioned Queue with Sessions: Schedule single message", async function(): Promise { - await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); - await testScheduleMessage(true, false); - }); + const batch = await receiverClient.receiveBatch(1); + const msgs = batch.messages; + const msgEnqueueTime = msgs[0].enqueuedTimeUtc ? msgs[0].enqueuedTimeUtc.valueOf() : 0; + + should.equal(Array.isArray(msgs), true, "`ReceivedMessages` is not an array"); + should.equal(msgs.length, 1, "Unexpected number of messages"); + should.equal( + msgEnqueueTime - scheduleTime.valueOf() >= 0, + true, + "Enqueued time must be greater than scheduled time" + ); // checking received message enqueue time is greater or equal to the scheduled time. + should.equal(msgs[0].body, testMessage.body, "MessageBody is different than expected"); + should.equal( + msgs[0].messageId, + testMessage.messageId, + "MessageId is different than expected" + ); + + await batch.context.complete(msgs[0]); + + await testPeekMsgsLength(receiverClient, 0); + } - it("Unpartitioned Topic with Sessions: Schedule single message", async function(): Promise { - await beforeEachTest(TestClientType.UnpartitionedSubscriptionWithSessions); - await testScheduleMessage(true, false); - }); -}); + it("Partitioned Queue: Schedule single message", async function(): Promise { + await beforeEachTest(TestClientType.PartitionedQueue); + await testScheduleMessage(false, true); + }); -describe("Schedule multiple messages", function(): void { - afterEach(async () => { - await afterEachTest(); - }); + it("Partitioned Topic: Schedule single message", async function(): Promise { + await beforeEachTest(TestClientType.PartitionedSubscription); + await testScheduleMessage(false, false); + }); - const messages: SendableMessageInfo[] = [ - { - body: "hello1", - messageId: `test message ${Math.random()}`, - partitionKey: "dummy" // partitionKey is only for partitioned queue/subscrption, Unpartitioned queue/subscrption do not care about partitionKey. - }, - { - body: "hello2", - messageId: `test message ${Math.random()}`, - partitionKey: "dummy" // partitionKey is only for partitioned queue/subscrption, Unpartitioned queue/subscrption do not care about partitionKey. - } - ]; - const messageWithSessions: SendableMessageInfo[] = [ - { - body: "hello1", - messageId: `test message ${Math.random()}`, - sessionId: TestMessage.sessionId - }, - { - body: "hello2", - messageId: `test message ${Math.random()}`, - sessionId: TestMessage.sessionId - } - ]; + it("Unpartitioned Queue: Schedule single message", async function(): Promise { + await beforeEachTest(TestClientType.UnpartitionedQueue); + await testScheduleMessage(false, false); + }); - async function testScheduleMessages(useSessions?: boolean): Promise { - const testMessages = useSessions ? messageWithSessions : messages; - const scheduleTime = new Date(Date.now() + 10000); // 10 seconds from now - await senderClient.scheduleMessages(scheduleTime, testMessages); + it("Unpartitioned Topic: Schedule single message", async function(): Promise { + await beforeEachTest(TestClientType.UnpartitionedSubscription); + await testScheduleMessage(false, true); + }); - const batch = await receiverClient.receiveBatch(2); - const msgs = batch.messages; - should.equal(Array.isArray(msgs), true, "`ReceivedMessages` is not an array"); - should.equal(msgs.length, 2, "Unexpected number of messages"); + it("Partitioned Queue with Sessions: Schedule single message", async function(): Promise { + await beforeEachTest(TestClientType.PartitionedQueueWithSessions); + await testScheduleMessage(true, true); + }); - const msgEnqueueTime1 = msgs[0].enqueuedTimeUtc ? msgs[0].enqueuedTimeUtc.valueOf() : 0; - const msgEnqueueTime2 = msgs[1].enqueuedTimeUtc ? msgs[1].enqueuedTimeUtc.valueOf() : 0; + it("Partitioned Topic with Sessions: Schedule single message", async function(): Promise { + await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); + await testScheduleMessage(true, true); + }); - // checking received message enqueue time is greater or equal to the scheduled time. - should.equal( - msgEnqueueTime1 - scheduleTime.valueOf() >= 0, - true, - "msgEnqueueTime1 time must be greater than scheduled time" - ); - should.equal( - msgEnqueueTime2 - scheduleTime.valueOf() >= 0, - true, - "msgEnqueueTime2 time must be greater than scheduled time" - ); - should.equal( - testMessages.some((x) => x.messageId === msgs[0].messageId), - true, - "MessageId of first message is different than expected" - ); - should.equal( - testMessages.some((x) => x.messageId === msgs[1].messageId), - true, - "MessageId of second message is different than expected" - ); + it("Unpartitioned Queue with Sessions: Schedule single message", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); + await testScheduleMessage(true, false); + }); - await batch.context.complete(msgs[0]); - await batch.context.complete(msgs[1]); + it("Unpartitioned Topic with Sessions: Schedule single message", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedSubscriptionWithSessions); + await testScheduleMessage(true, false); + }); + }); - await testPeekMsgsLength(receiverClient, 0); - } + describe("Schedule multiple messages", function(): void { + afterEach(async () => { + await afterEachTest(); + }); - it("Partitioned Queue: Schedule multiple messages", async function(): Promise { - await beforeEachTest(TestClientType.PartitionedQueue); - await testScheduleMessages(); - }); + const messages: SendableMessageInfo[] = [ + { + body: "hello1", + messageId: `test message ${Math.random()}`, + partitionKey: "dummy" // partitionKey is only for partitioned queue/subscrption, Unpartitioned queue/subscrption do not care about partitionKey. + }, + { + body: "hello2", + messageId: `test message ${Math.random()}`, + partitionKey: "dummy" // partitionKey is only for partitioned queue/subscrption, Unpartitioned queue/subscrption do not care about partitionKey. + } + ]; + const messageWithSessions: SendableMessageInfo[] = [ + { + body: "hello1", + messageId: `test message ${Math.random()}`, + sessionId: TestMessage.sessionId + }, + { + body: "hello2", + messageId: `test message ${Math.random()}`, + sessionId: TestMessage.sessionId + } + ]; + + async function testScheduleMessages(useSessions?: boolean): Promise { + const testMessages = useSessions ? messageWithSessions : messages; + const scheduleTime = new Date(Date.now() + 10000); // 10 seconds from now + await senderClient.scheduleMessages(scheduleTime, testMessages); + + const batch = await receiverClient.receiveBatch(2); + const msgs = batch.messages; + should.equal(Array.isArray(msgs), true, "`ReceivedMessages` is not an array"); + should.equal(msgs.length, 2, "Unexpected number of messages"); + + const msgEnqueueTime1 = msgs[0].enqueuedTimeUtc ? msgs[0].enqueuedTimeUtc.valueOf() : 0; + const msgEnqueueTime2 = msgs[1].enqueuedTimeUtc ? msgs[1].enqueuedTimeUtc.valueOf() : 0; + + // checking received message enqueue time is greater or equal to the scheduled time. + should.equal( + msgEnqueueTime1 - scheduleTime.valueOf() >= 0, + true, + "msgEnqueueTime1 time must be greater than scheduled time" + ); + should.equal( + msgEnqueueTime2 - scheduleTime.valueOf() >= 0, + true, + "msgEnqueueTime2 time must be greater than scheduled time" + ); + should.equal( + testMessages.some((x) => x.messageId === msgs[0].messageId), + true, + "MessageId of first message is different than expected" + ); + should.equal( + testMessages.some((x) => x.messageId === msgs[1].messageId), + true, + "MessageId of second message is different than expected" + ); + + await batch.context.complete(msgs[0]); + await batch.context.complete(msgs[1]); + + await testPeekMsgsLength(receiverClient, 0); + } - it("Partitioned Topic: Schedule multiple messages", async function(): Promise { - await beforeEachTest(TestClientType.PartitionedSubscription); - await testScheduleMessages(); - }); + it("Partitioned Queue: Schedule multiple messages", async function(): Promise { + await beforeEachTest(TestClientType.PartitionedQueue); + await testScheduleMessages(); + }); - it("UnPartitioned Queue: Schedule multiple messages #RunInBrowser", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedQueue); - await testScheduleMessages(); - }); + it("Partitioned Topic: Schedule multiple messages", async function(): Promise { + await beforeEachTest(TestClientType.PartitionedSubscription); + await testScheduleMessages(); + }); - it("UnPartitioned Topic: Schedule multiple messages", async function(): Promise { - await beforeEachTest(TestClientType.UnpartitionedSubscription); - await testScheduleMessages(); - }); + it("UnPartitioned Queue: Schedule multiple messages #RunInBrowser", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedQueue); + await testScheduleMessages(); + }); - it("Partitioned Queue with Sessions: Schedule multiple messages", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedQueueWithSessions); - await testScheduleMessages(true); - }); + it("UnPartitioned Topic: Schedule multiple messages", async function(): Promise { + await beforeEachTest(TestClientType.UnpartitionedSubscription); + await testScheduleMessages(); + }); - it("Partitioned Topic with Sessions: Schedule multiple messages", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); - await testScheduleMessages(true); - }); + it("Partitioned Queue with Sessions: Schedule multiple messages", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedQueueWithSessions); + await testScheduleMessages(true); + }); - it("Unpartitioned Queue with Sessions: Schedule multiple messages #RunInBrowser", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); - await testScheduleMessages(true); - }); + it("Partitioned Topic with Sessions: Schedule multiple messages", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); + await testScheduleMessages(true); + }); - it("Unpartitioned Topic with Sessions: Schedule multiple messages", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedSubscriptionWithSessions); - await testScheduleMessages(true); - }); -}); + it("Unpartitioned Queue with Sessions: Schedule multiple messages #RunInBrowser", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); + await testScheduleMessages(true); + }); -describe("Cancel single Scheduled message", function(): void { - afterEach(async () => { - await afterEachTest(); + it("Unpartitioned Topic with Sessions: Schedule multiple messages", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedSubscriptionWithSessions); + await testScheduleMessages(true); + }); }); - async function testCancelScheduleMessage(useSessions?: boolean): Promise { - const testMessage = useSessions ? TestMessage.getSessionSample() : TestMessage.getSample(); - const scheduleTime = new Date(Date.now() + 30000); // 30 seconds from now as anything less gives inconsistent results for cancelling - const sequenceNumber = await senderClient.scheduleMessage(scheduleTime, testMessage); + describe("Cancel single Scheduled message", function(): void { + afterEach(async () => { + await afterEachTest(); + }); - await delay(2000); + async function testCancelScheduleMessage(useSessions?: boolean): Promise { + const testMessage = useSessions ? TestMessage.getSessionSample() : TestMessage.getSample(); + const scheduleTime = new Date(Date.now() + 30000); // 30 seconds from now as anything less gives inconsistent results for cancelling + const sequenceNumber = await senderClient.scheduleMessage(scheduleTime, testMessage); - await senderClient.cancelScheduledMessage(sequenceNumber); + await delay(2000); - // Wait until we are sure we have passed the schedule time - await delay(30000); - await testReceivedMsgsLength(receiverClient, 0); - } + await senderClient.cancelScheduledMessage(sequenceNumber); - it("Partitioned Queue: Cancel single Scheduled message", async function(): Promise { - await beforeEachTest(TestClientType.PartitionedQueue); - await testCancelScheduleMessage(); - }); + // Wait until we are sure we have passed the schedule time + await delay(30000); + await testReceivedMsgsLength(receiverClient, 0); + } - it("Partitioned Topic: Cancel single Scheduled message", async function(): Promise { - await beforeEachTest(TestClientType.PartitionedSubscription); - await testCancelScheduleMessage(); - }); + it("Partitioned Queue: Cancel single Scheduled message", async function(): Promise { + await beforeEachTest(TestClientType.PartitionedQueue); + await testCancelScheduleMessage(); + }); - it("Unpartitioned Queue: Cancel single Scheduled message", async function(): Promise { - await beforeEachTest(TestClientType.UnpartitionedQueue); - await testCancelScheduleMessage(); - }); + it("Partitioned Topic: Cancel single Scheduled message", async function(): Promise { + await beforeEachTest(TestClientType.PartitionedSubscription); + await testCancelScheduleMessage(); + }); - it("Unpartitioned Topic: Cancel single Scheduled message", async function(): Promise { - await beforeEachTest(TestClientType.UnpartitionedSubscription); - await testCancelScheduleMessage(); - }); + it("Unpartitioned Queue: Cancel single Scheduled message", async function(): Promise { + await beforeEachTest(TestClientType.UnpartitionedQueue); + await testCancelScheduleMessage(); + }); - it("Partitioned Queue with Sessions: Cancel single Scheduled message", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedQueueWithSessions); - await testCancelScheduleMessage(true); - }); + it("Unpartitioned Topic: Cancel single Scheduled message", async function(): Promise { + await beforeEachTest(TestClientType.UnpartitionedSubscription); + await testCancelScheduleMessage(); + }); - it("Partitioned Topic with Sessions: Cancel single Scheduled message", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); - await testCancelScheduleMessage(true); - }); + it("Partitioned Queue with Sessions: Cancel single Scheduled message", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedQueueWithSessions); + await testCancelScheduleMessage(true); + }); - it("Unpartitioned Queue with Sessions: Cancel single Scheduled message", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); - await testCancelScheduleMessage(true); - }); + it("Partitioned Topic with Sessions: Cancel single Scheduled message", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); + await testCancelScheduleMessage(true); + }); - it("Unpartitioned Topic with Sessions: Cancel single Scheduled message", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedSubscriptionWithSessions); - await testCancelScheduleMessage(true); - }); -}); + it("Unpartitioned Queue with Sessions: Cancel single Scheduled message", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); + await testCancelScheduleMessage(true); + }); -describe("Cancel multiple Scheduled messages", function(): void { - afterEach(async () => { - await afterEachTest(); + it("Unpartitioned Topic with Sessions: Cancel single Scheduled message", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedSubscriptionWithSessions); + await testCancelScheduleMessage(true); + }); }); - async function testCancelScheduleMessages(useSessions?: boolean): Promise { - const testMessage = useSessions ? TestMessage.getSessionSample() : TestMessage.getSample(); + describe("Cancel multiple Scheduled messages", function(): void { + afterEach(async () => { + await afterEachTest(); + }); - const scheduleTime = new Date(Date.now() + 30000); // 30 seconds from now as anything less gives inconsistent results for cancelling - const sequenceNumber1 = await senderClient.scheduleMessage(scheduleTime, testMessage); - const sequenceNumber2 = await senderClient.scheduleMessage(scheduleTime, testMessage); + async function testCancelScheduleMessages(useSessions?: boolean): Promise { + const testMessage = useSessions ? TestMessage.getSessionSample() : TestMessage.getSample(); - await delay(2000); + const scheduleTime = new Date(Date.now() + 30000); // 30 seconds from now as anything less gives inconsistent results for cancelling + const sequenceNumber1 = await senderClient.scheduleMessage(scheduleTime, testMessage); + const sequenceNumber2 = await senderClient.scheduleMessage(scheduleTime, testMessage); - await senderClient.cancelScheduledMessages([sequenceNumber1, sequenceNumber2]); + await delay(2000); - // Wait until we are sure we have passed the schedule time - await delay(30000); - await testReceivedMsgsLength(receiverClient, 0); - } + await senderClient.cancelScheduledMessages([sequenceNumber1, sequenceNumber2]); - it("Partitioned Queue: Cancel scheduled messages", async function(): Promise { - await beforeEachTest(TestClientType.PartitionedQueue); - await testCancelScheduleMessages(false); - }); - - it("Partitioned Topic: Cancel scheduled messages", async function(): Promise { - await beforeEachTest(TestClientType.PartitionedSubscription); - await testCancelScheduleMessages(false); - }); + // Wait until we are sure we have passed the schedule time + await delay(30000); + await testReceivedMsgsLength(receiverClient, 0); + } - it("Unpartitioned Queue: Cancel scheduled messages #RunInBrowser", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedQueue); - await testCancelScheduleMessages(false); - }); + it("Partitioned Queue: Cancel scheduled messages", async function(): Promise { + await beforeEachTest(TestClientType.PartitionedQueue); + await testCancelScheduleMessages(false); + }); - it("Unpartitioned Topic: Cancel scheduled messages", async function(): Promise { - await beforeEachTest(TestClientType.UnpartitionedSubscription); - await testCancelScheduleMessages(false); - }); + it("Partitioned Topic: Cancel scheduled messages", async function(): Promise { + await beforeEachTest(TestClientType.PartitionedSubscription); + await testCancelScheduleMessages(false); + }); - it("Partitioned Queue with Sessions: Cancel scheduled messages", async function(): Promise { - await beforeEachTest(TestClientType.PartitionedQueueWithSessions); - await testCancelScheduleMessages(true); - }); + it("Unpartitioned Queue: Cancel scheduled messages #RunInBrowser", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedQueue); + await testCancelScheduleMessages(false); + }); - it("Partitioned Topic with Sessions: Cancel scheduled messages", async function(): Promise { - await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); - await testCancelScheduleMessages(true); - }); + it("Unpartitioned Topic: Cancel scheduled messages", async function(): Promise { + await beforeEachTest(TestClientType.UnpartitionedSubscription); + await testCancelScheduleMessages(false); + }); - it("Unpartitioned Queue with Sessions: Cancel scheduled messages #RunInBrowser", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); - await testCancelScheduleMessages(true); - }); + it("Partitioned Queue with Sessions: Cancel scheduled messages", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedQueueWithSessions); + await testCancelScheduleMessages(true); + }); - it("Unpartitioned Topic with Sessions: Cancel scheduled messages", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedSubscriptionWithSessions); - await testCancelScheduleMessages(true); - }); -}); + it("Partitioned Topic with Sessions: Cancel scheduled messages", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); + await testCancelScheduleMessages(true); + }); -describe("SendableMessageInfo validations #RunInBrowser", function(): void { - const longString = - "A very very very very very very very very very very very very very very very very very very very very very very very very very long string."; - after(async () => { - await afterEachTest(); - }); + it("Unpartitioned Queue with Sessions: Cancel scheduled messages #RunInBrowser", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); + await testCancelScheduleMessages(true); + }); - before(async () => { - await beforeEachTest(TestClientType.PartitionedQueue); + it("Unpartitioned Topic with Sessions: Cancel scheduled messages", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedSubscriptionWithSessions); + await testCancelScheduleMessages(true); + }); }); - const testInputs: { - message: SendableMessageInfo; - expectedErrorMessage: string; - title?: string; - }[] = [ - { - message: { body: "", contentType: 1 as any }, - expectedErrorMessage: "The property 'contentType' on the message must be of type 'string'", - title: "contenType is of invalid type" - }, - { - message: { body: "", label: 1 as any }, - expectedErrorMessage: "The property 'label' on the message must be of type 'string'", - title: "label is of invalid type" - }, - { - message: { body: "", to: 1 as any }, - expectedErrorMessage: "The property 'to' on the message must be of type 'string'", - title: "to is of invalid type" - }, - { - message: { body: "", replyToSessionId: 1 as any }, - expectedErrorMessage: - "The property 'replyToSessionId' on the message must be of type 'string'", - title: "replyToSessionId is of invalid type" - }, - { - message: { body: "", sessionId: 1 as any }, - expectedErrorMessage: "The property 'sessionId' on the message must be of type 'string'", - title: "sessionId is of invalid type" - }, - { - message: { body: "", replyTo: 1 as any }, - expectedErrorMessage: "The property 'replyTo' on the message must be of type 'string'", - title: "replyTo is of invalid type" - }, - { - message: { body: "", timeToLive: "" as any }, - expectedErrorMessage: "The property 'timeToLive' on the message must be of type 'number'", - title: "timeToLive is of invalid type" - }, - { - message: { body: "", partitionKey: longString }, - expectedErrorMessage: - "Length of 'partitionKey' property on the message cannot be greater than 128 characters.", - title: "partitionKey is longer than 128 characters" - }, - { - message: { body: "", viaPartitionKey: longString }, - expectedErrorMessage: - "Length of 'viaPartitionKey' property on the message cannot be greater than 128 characters.", - title: "viaPartitionKey is longer than 128 characters" - }, - { - message: { body: "", sessionId: longString }, - expectedErrorMessage: - "Length of 'sessionId' property on the message cannot be greater than 128 characters.", - title: "sessionId is longer than 128 characters" - }, - { - message: { body: "", messageId: longString }, - expectedErrorMessage: - "Length of 'messageId' property on the message cannot be greater than 128 characters.", - title: "messageId is longer than 128 characters" - }, - { - message: { body: "", messageId: {} as any }, - expectedErrorMessage: - "The property 'messageId' on the message must be of type string, number or Buffer", - title: "messageId is of invalid type" - }, - { - message: { body: "", correlationId: {} as any }, - expectedErrorMessage: - "The property 'correlationId' on the message must be of type string, number or Buffer", - title: "correlationId is of invalid type" - } - ]; - - testInputs.forEach(function(testInput: any): void { - it("Send() throws if " + testInput.title, async function(): Promise { - let actualErrorMsg = ""; - await senderClient.send(testInput.message).catch((err) => { - actualErrorMsg = err.message; - }); - should.equal(actualErrorMsg, testInput.expectedErrorMessage, "Error not thrown as expected"); + describe("SendableMessageInfo validations #RunInBrowser", function(): void { + const longString = + "A very very very very very very very very very very very very very very very very very very very very very very very very very long string."; + after(async () => { + await afterEachTest(); }); - it("SendBatch() throws if in the first message, " + testInput.title, async function(): Promise< - void - > { - let actualErrorMsg = ""; - await senderClient.sendBatch([testInput.message, { body: "random" }]).catch((err) => { - actualErrorMsg = err.message; - }); - should.equal(actualErrorMsg, testInput.expectedErrorMessage, "Error not thrown as expected"); + before(async () => { + await beforeEachTest(TestClientType.PartitionedQueue); }); - it( - "SendBatch() throws if in the subsequent message, " + testInput.title, - async function(): Promise { + const testInputs: { + message: SendableMessageInfo; + expectedErrorMessage: string; + title?: string; + }[] = [ + { + message: { body: "", contentType: 1 as any }, + expectedErrorMessage: "The property 'contentType' on the message must be of type 'string'", + title: "contenType is of invalid type" + }, + { + message: { body: "", label: 1 as any }, + expectedErrorMessage: "The property 'label' on the message must be of type 'string'", + title: "label is of invalid type" + }, + { + message: { body: "", to: 1 as any }, + expectedErrorMessage: "The property 'to' on the message must be of type 'string'", + title: "to is of invalid type" + }, + { + message: { body: "", replyToSessionId: 1 as any }, + expectedErrorMessage: + "The property 'replyToSessionId' on the message must be of type 'string'", + title: "replyToSessionId is of invalid type" + }, + { + message: { body: "", sessionId: 1 as any }, + expectedErrorMessage: "The property 'sessionId' on the message must be of type 'string'", + title: "sessionId is of invalid type" + }, + { + message: { body: "", replyTo: 1 as any }, + expectedErrorMessage: "The property 'replyTo' on the message must be of type 'string'", + title: "replyTo is of invalid type" + }, + { + message: { body: "", timeToLive: "" as any }, + expectedErrorMessage: "The property 'timeToLive' on the message must be of type 'number'", + title: "timeToLive is of invalid type" + }, + { + message: { body: "", partitionKey: longString }, + expectedErrorMessage: + "Length of 'partitionKey' property on the message cannot be greater than 128 characters.", + title: "partitionKey is longer than 128 characters" + }, + { + message: { body: "", viaPartitionKey: longString }, + expectedErrorMessage: + "Length of 'viaPartitionKey' property on the message cannot be greater than 128 characters.", + title: "viaPartitionKey is longer than 128 characters" + }, + { + message: { body: "", sessionId: longString }, + expectedErrorMessage: + "Length of 'sessionId' property on the message cannot be greater than 128 characters.", + title: "sessionId is longer than 128 characters" + }, + { + message: { body: "", messageId: longString }, + expectedErrorMessage: + "Length of 'messageId' property on the message cannot be greater than 128 characters.", + title: "messageId is longer than 128 characters" + }, + { + message: { body: "", messageId: {} as any }, + expectedErrorMessage: + "The property 'messageId' on the message must be of type string, number or Buffer", + title: "messageId is of invalid type" + }, + { + message: { body: "", correlationId: {} as any }, + expectedErrorMessage: + "The property 'correlationId' on the message must be of type string, number or Buffer", + title: "correlationId is of invalid type" + } + ]; + + testInputs.forEach(function(testInput: any): void { + it("Send() throws if " + testInput.title, async function(): Promise { let actualErrorMsg = ""; - await senderClient.sendBatch([{ body: "random" }, testInput.message]).catch((err) => { + await senderClient.send(testInput.message).catch((err) => { actualErrorMsg = err.message; }); should.equal( @@ -669,17 +637,60 @@ describe("SendableMessageInfo validations #RunInBrowser", function(): void { testInput.expectedErrorMessage, "Error not thrown as expected" ); - } - ); + }); - it("ScheduleMessage() throws if " + testInput.title, async function(): Promise { - let actualErrorMsg = ""; - let actualErr; - await senderClient.scheduleMessage(new Date(), testInput.message).catch((err) => { - actualErr = err; - actualErrorMsg = err.message; + it( + "SendBatch() throws if in the first message, " + testInput.title, + async function(): Promise { + let actualErrorMsg = ""; + await senderClient.sendBatch([testInput.message, { body: "random" }]).catch((err) => { + actualErrorMsg = err.message; + }); + should.equal( + actualErrorMsg, + testInput.expectedErrorMessage, + "Error not thrown as expected" + ); + } + ); + + it( + "SendBatch() throws if in the subsequent message, " + testInput.title, + async function(): Promise { + let actualErrorMsg = ""; + await senderClient.sendBatch([{ body: "random" }, testInput.message]).catch((err) => { + actualErrorMsg = err.message; + }); + should.equal( + actualErrorMsg, + testInput.expectedErrorMessage, + "Error not thrown as expected" + ); + } + ); + + it("ScheduleMessage() throws if " + testInput.title, async function(): Promise { + let actualErrorMsg = ""; + let actualErr; + await senderClient.scheduleMessage(new Date(), testInput.message).catch((err) => { + actualErr = err; + actualErrorMsg = err.message; + }); + should.equal(actualErrorMsg, testInput.expectedErrorMessage, actualErr); }); - should.equal(actualErrorMsg, testInput.expectedErrorMessage, actualErr); }); }); + + async function testReceivedMsgsLength( + receiverClient: Receiver, + expectedReceivedMsgsLength: number + ): Promise { + const receivedMsgs = await receiverClient.receiveBatch(expectedReceivedMsgsLength + 1, 5); + + should.equal( + receivedMsgs.messages.length, + expectedReceivedMsgsLength, + "Unexpected number of msgs found when receiving" + ); + } }); diff --git a/sdk/servicebus/service-bus/test/sessionsRequiredCleanEntityTests.spec.ts b/sdk/servicebus/service-bus/test/sessionsRequiredCleanEntityTests.spec.ts new file mode 100644 index 000000000000..bf40207d9ea0 --- /dev/null +++ b/sdk/servicebus/service-bus/test/sessionsRequiredCleanEntityTests.spec.ts @@ -0,0 +1,329 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +import chai from "chai"; +import { + ServiceBusClientForTests, + createServiceBusClientForTests, + testPeekMsgsLength +} from "./utils/testutils2"; +import { Sender } from "../src/sender"; +import { SessionReceiver, ContextWithSettlement, SendableMessageInfo } from "../src"; +import { TestClientType, TestMessage } from "./utils/testUtils"; +const should = chai.should(); + +// NOTE: these tests should be reworked, if possible. Since they need to be deterministic +// and only grab the "expected" next session you need to ensure the entity (queue, sub) +// is completely empty. +// +// I've moved these tests in here and re-create entites after each test - it'e expensive +// but it'll allow them to be reliable. +describe("sessions tests - requires completely clean entity for each test", () => { + let serviceBusClient: ServiceBusClientForTests; + let sender: Sender; + let receiver: SessionReceiver; + + async function beforeEachNoSessionTest( + testClientType: TestClientType, + sessionId?: string + ): Promise { + serviceBusClient = createServiceBusClientForTests(); + const entityNames = await serviceBusClient.test.createTestEntities(testClientType); + + receiver = serviceBusClient.test.getSessionPeekLockReceiver(entityNames, { + sessionId + }); + + sender = serviceBusClient.test.addToCleanup( + serviceBusClient.getSender(entityNames.queue ?? entityNames.topic!) + ); + + // Observation - + // Peeking into an empty session-enabled queue would run into either of the following errors.. + // 1. OperationTimeoutError: Unable to create the amqp receiver 'unpartitioned-queue-sessions-794f89be-3282-8b48-8ae0-a8af43c3ce36' + // on amqp session 'local-1_remote-1_connection-2' due to operation timeout. + // 2. MessagingError: Received an incorrect sessionId 'undefined' while creating the receiver 'unpartitioned-queue-sessions-86662b2b-acdc-1045-8ad4-fa3ab8807871'. + + // getSenderReceiverClients creates brand new queues/topic-subscriptions. + // Hence, commenting the following code since there is no need to purge/peek into a freshly created entity + + // await purge(receiverClient); + // const peekedMsgs = await receiverClient.diagnostics.peek(); + // const receiverEntityType = receiverClient.entityType; + // if (peekedMsgs.length) { + // chai.assert.fail(`Please use an empty ${receiverEntityType} for integration testing`); + // } + } + + async function afterEachTest(): Promise { + await serviceBusClient.test.afterEach(); + // special circumstance since we're destroying everything after each of these tests. + await serviceBusClient.test.after(); + } + + describe("Peek session", function(): void { + afterEach(async () => { + await afterEachTest(); + }); + + async function eachTest(testClientType: TestClientType, useSessionId: boolean) { + await beforeEachNoSessionTest(testClientType); + await peekSession(testClientType, useSessionId); + } + + async function peekSession( + testClientType: TestClientType, + useSessionId: boolean + ): Promise { + const testMessage = TestMessage.getSessionSample(); + await sender.send(testMessage); + + const entityNames = serviceBusClient.test.getTestEntities(testClientType); + + // get the next available session ID rather than specifying one + receiver = await serviceBusClient.test.getSessionPeekLockReceiver(entityNames, { + sessionId: useSessionId ? testMessage.sessionId! : undefined + }); + + // At this point AMQP receiver link has not been established. + // peek() will not establish the link if sessionId was provided + const peekedMsgs = await receiver.diagnostics.peek(1); + should.equal(peekedMsgs.length, 1, "Unexpected number of messages peeked"); + should.equal(peekedMsgs[0].body, testMessage.body, "MessageBody is different than expected"); + should.equal( + peekedMsgs[0].messageId, + testMessage.messageId, + "MessageId is different than expected" + ); + should.equal( + peekedMsgs[0].sessionId, + testMessage.sessionId, + "SessionId is different than expected" + ); + + const batch = await receiver.receiveBatch(1); + const msgs = batch.messages; + should.equal(msgs.length, 1, "Unexpected number of messages received"); + should.equal(msgs[0].body, testMessage.body, "MessageBody is different than expected"); + should.equal( + msgs[0].messageId, + testMessage.messageId, + "MessageId is different than expected" + ); + should.equal( + msgs[0].sessionId, + testMessage.sessionId, + "SessionId is different than expected" + ); + + await batch.context.complete(msgs[0]); + } + + it("Partitioned Queue - Peek Session with sessionId", async function(): Promise { + await eachTest(TestClientType.PartitionedQueueWithSessions, true); + }); + it("Partitioned Subscription - Peek Session with sessionId", async function(): Promise { + await eachTest(TestClientType.PartitionedSubscriptionWithSessions, true); + }); + it("Unpartitioned Queue - Peek Session with sessionId #RunInBrowser", async function(): Promise< + void + > { + await eachTest(TestClientType.UnpartitionedQueueWithSessions, true); + }); + it("Unpartitioned Subscription - Peek Session with sessionId", async function(): Promise { + await eachTest(TestClientType.UnpartitionedSubscriptionWithSessions, true); + }); + it("Partitioned Queue - Peek Session without sessionId", async function(): Promise { + await eachTest(TestClientType.PartitionedQueueWithSessions, false); + }); + it("Partitioned Subscription - Peek Session without sessionId", async function(): Promise< + void + > { + await eachTest(TestClientType.PartitionedSubscriptionWithSessions, false); + }); + it("Unpartitioned Queue - Peek Session without sessionId #RunInBrowser", async function(): Promise< + void + > { + await eachTest(TestClientType.UnpartitionedQueueWithSessions, false); + }); + it("Unpartitioned Subscription - Peek Session without sessionId", async function(): Promise< + void + > { + await eachTest(TestClientType.UnpartitionedSubscriptionWithSessions, false); + }); + }); + + describe("SessionReceiver with no sessionId", function(): void { + const testSessionId2 = "my-session2"; + + afterEach(async () => { + await afterEachTest(); + }); + + const testMessagesWithDifferentSessionIds: SendableMessageInfo[] = [ + { + body: "hello1", + messageId: `test message ${Math.random()}`, + sessionId: TestMessage.sessionId + }, + { + body: "hello2", + messageId: `test message ${Math.random()}`, + sessionId: testSessionId2 + } + ]; + + async function testComplete_batching(testClientType: TestClientType): Promise { + await sender.send(testMessagesWithDifferentSessionIds[0]); + await sender.send(testMessagesWithDifferentSessionIds[1]); + + let batch = await receiver.receiveBatch(2); + let msgs = batch.messages; + + should.equal(msgs.length, 1, "Unexpected number of messages received"); + should.equal(receiver.sessionId, msgs[0].sessionId, "Unexpected sessionId in receiver"); + should.equal( + testMessagesWithDifferentSessionIds.some( + (x) => + msgs[0].body === x.body && + msgs[0].messageId === x.messageId && + msgs[0].sessionId === x.sessionId + ), + true, + "Received Message doesnt match any of the test messages" + ); + await batch.context.complete(msgs[0]); + await receiver.close(); + + const entityNames = serviceBusClient.test.getTestEntities(testClientType); + + // get the next available session ID rather than specifying one + receiver = serviceBusClient.test.getSessionPeekLockReceiver(entityNames); + + batch = await receiver.receiveBatch(2); + msgs = batch.messages; + + should.equal(msgs.length, 1, "Unexpected number of messages received"); + should.equal(receiver.sessionId, msgs[0].sessionId, "Unexpected sessionId in receiver"); + should.equal( + testMessagesWithDifferentSessionIds.some( + (x) => + msgs[0].body === x.body && + msgs[0].messageId === x.messageId && + msgs[0].sessionId === x.sessionId + ), + true, + "Received Message doesnt match any of the test messages" + ); + await batch.context.complete(msgs[0]); + await testPeekMsgsLength(receiver, 0); + } + + it("Partitioned Queue: complete() removes message from random session", async function(): Promise< + void + > { + await beforeEachNoSessionTest(TestClientType.PartitionedQueueWithSessions); + await testComplete_batching(TestClientType.PartitionedQueueWithSessions); + }); + + it("Partitioned Subscription: complete() removes message from random session", async function(): Promise< + void + > { + await beforeEachNoSessionTest(TestClientType.PartitionedSubscriptionWithSessions); + await testComplete_batching(TestClientType.PartitionedSubscriptionWithSessions); + }); + + it("Unpartitioned Queue: complete() removes message from random session #RunInBrowser", async function(): Promise< + void + > { + await beforeEachNoSessionTest(TestClientType.UnpartitionedQueueWithSessions); + await testComplete_batching(TestClientType.UnpartitionedQueueWithSessions); + }); + + it("Unpartitioned Subscription: complete() removes message from random session", async function(): Promise< + void + > { + await beforeEachNoSessionTest(TestClientType.UnpartitionedSubscriptionWithSessions); + await testComplete_batching(TestClientType.UnpartitionedSubscriptionWithSessions); + }); + }); + + describe("SessionReceiver with empty string as sessionId", function(): void { + afterEach(async () => { + await afterEachTest(); + }); + + // Sending messages with different session id, so that we know for sure we pick the right one + // and that Service Bus is not choosing a random one for us + const testMessagesWithDifferentSessionIds: SendableMessageInfo[] = [ + { + body: "hello1", + messageId: `test message ${Math.random()}`, + sessionId: TestMessage.sessionId + }, + { + body: "hello2", + messageId: `test message ${Math.random()}`, + sessionId: "" + } + ]; + + async function testComplete_batching(testClientType: TestClientType): Promise { + await sender.send(testMessagesWithDifferentSessionIds[0]); + await sender.send(testMessagesWithDifferentSessionIds[1]); + + const entityNames = serviceBusClient.test.getTestEntities(testClientType); + + // get the next available session ID rather than specifying one + receiver = serviceBusClient.test.getSessionPeekLockReceiver(entityNames, { sessionId: "" }); + + const batch = await receiver.receiveBatch(2); + const msgs = batch.messages; + + should.equal(msgs.length, 1, "Unexpected number of messages received"); + + should.equal(receiver.sessionId, "", "Unexpected sessionId in receiver"); + should.equal( + testMessagesWithDifferentSessionIds[1].body === msgs[0].body && + testMessagesWithDifferentSessionIds[1].messageId === msgs[0].messageId && + testMessagesWithDifferentSessionIds[1].sessionId === msgs[0].sessionId, + true, + "Received Message doesnt match expected test message" + ); + await batch.context.complete(msgs[0]); + + const peekedMsgsInSession = await receiver.diagnostics.peek(); + should.equal(peekedMsgsInSession.length, 0, "Unexpected number of messages peeked"); + + await receiver.close(); + } + + it("Partitioned Queue: complete() removes message from random session", async function(): Promise< + void + > { + await beforeEachNoSessionTest(TestClientType.PartitionedQueueWithSessions, ""); + await testComplete_batching(TestClientType.PartitionedQueueWithSessions); + }); + + it("Partitioned Subscription: complete() removes message from random session", async function(): Promise< + void + > { + await beforeEachNoSessionTest(TestClientType.PartitionedSubscriptionWithSessions, ""); + await testComplete_batching(TestClientType.PartitionedSubscriptionWithSessions); + }); + + it("Unpartitioned Queue: complete() removes message from random session", async function(): Promise< + void + > { + await beforeEachNoSessionTest(TestClientType.UnpartitionedQueueWithSessions, ""); + await testComplete_batching(TestClientType.UnpartitionedQueueWithSessions); + }); + + it("Unpartitioned Subscription: complete() removes message from random session", async function(): Promise< + void + > { + await beforeEachNoSessionTest(TestClientType.UnpartitionedSubscriptionWithSessions, ""); + await testComplete_batching(TestClientType.UnpartitionedSubscriptionWithSessions); + }); + }); +}); diff --git a/sdk/servicebus/service-bus/test/sessionsTests.spec.ts b/sdk/servicebus/service-bus/test/sessionsTests.spec.ts index 6b32a8722b04..700548f3cdce 100644 --- a/sdk/servicebus/service-bus/test/sessionsTests.spec.ts +++ b/sdk/servicebus/service-bus/test/sessionsTests.spec.ts @@ -5,37 +5,16 @@ import chai from "chai"; const should = chai.should(); import chaiAsPromised from "chai-as-promised"; chai.use(chaiAsPromised); -import { - delay, - SendableMessageInfo, - ServiceBusSenderClient, - SessionReceiver, - ReceivedMessage, - ContextWithSettlement -} from "../src"; +import { delay, ReceivedMessage, ContextWithSettlement } from "../src"; +import { TestMessage, TestClientType, checkWithTimeout } from "./utils/testUtils"; +import { Sender } from "../src/sender"; +import { SessionReceiver } from "../src/receivers/sessionReceiver"; import { - TestMessage, - getSenderReceiverClients, - TestClientType, - checkWithTimeout, - ReceiverClientTypeForUser -} from "./utils/testUtils"; - -async function testPeekMsgsLength( - client: ReceiverClientTypeForUser, - expectedPeekLength: number -): Promise { - const peekedMsgs = await client.diagnostics.peek(expectedPeekLength + 1); - should.equal( - peekedMsgs.length, - expectedPeekLength, - "Unexpected number of msgs found when peeking" - ); -} - -let senderClient: ServiceBusSenderClient; -let receiverClient: SessionReceiver<"peekLock">; + testPeekMsgsLength, + ServiceBusClientForTests, + createServiceBusClientForTests +} from "./utils/testutils2"; let unexpectedError: Error | undefined; @@ -45,517 +24,294 @@ async function processError(err: Error): Promise { } } -const testSessionId2 = "my-session2"; - -async function beforeEachTest( - testClientType: TestClientType, - sessionId: string | undefined -): Promise { - const clients = await getSenderReceiverClients(testClientType, "peekLock", undefined, { - id: sessionId - }); - senderClient = clients.senderClient; - receiverClient = clients.receiverClient as SessionReceiver<"peekLock">; - - // Observation - - // Peeking into an empty session-enabled queue would run into either of the following errors.. - // 1. OperationTimeoutError: Unable to create the amqp receiver 'unpartitioned-queue-sessions-794f89be-3282-8b48-8ae0-a8af43c3ce36' - // on amqp session 'local-1_remote-1_connection-2' due to operation timeout. - // 2. MessagingError: Received an incorrect sessionId 'undefined' while creating the receiver 'unpartitioned-queue-sessions-86662b2b-acdc-1045-8ad4-fa3ab8807871'. - - // getSenderReceiverClients creates brand new queues/topic-subscriptions. - // Hence, commenting the following code since there is no need to purge/peek into a freshly created entity - - // await purge(receiverClient); - // const peekedMsgs = await receiverClient.diagnostics.peek(); - // const receiverEntityType = receiverClient.entityType; - // if (peekedMsgs.length) { - // chai.assert.fail(`Please use an empty ${receiverEntityType} for integration testing`); - // } -} - -async function afterEachTest(): Promise { - await senderClient.close(); - await receiverClient.close(); -} - -describe("SessionReceiver with invalid sessionId", function(): void { - let sessionId: string; - beforeEach(() => { - sessionId = "non" + TestMessage.sessionId; - }); - - afterEach(async () => { - await afterEachTest(); - }); - - async function test_batching(testClientType: TestClientType): Promise { - const testMessage = TestMessage.getSessionSample(); - await senderClient.send(testMessage); - - let batch = await receiverClient.receiveBatch(1, 10); - let msgs = batch.messages; - should.equal(msgs.length, 0, "Unexpected number of messages received"); - - await receiverClient.close(); - receiverClient = ( - await getSenderReceiverClients( - testClientType, - "peekLock", - undefined, - { id: undefined }, - false - ) - ).receiverClient as SessionReceiver<"peekLock">; - - batch = await receiverClient.receiveBatch(1); - msgs = batch.messages; - should.equal(msgs.length, 1, "Unexpected number of messages received"); - should.equal(Array.isArray(msgs), true, "`ReceivedMessages` is not an array"); - should.equal(msgs[0].body, testMessage.body, "MessageBody is different than expected"); - should.equal(msgs[0].messageId, testMessage.messageId, "MessageId is different than expected"); - await batch.context.complete(msgs[0]); - await testPeekMsgsLength(receiverClient, 0); - } - - it("Partitioned Queue - Batch Receiver: no messages received for invalid sessionId", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedQueueWithSessions, sessionId); - await test_batching(TestClientType.PartitionedQueueWithSessions); - }); +describe("session tests", () => { + let serviceBusClient: ServiceBusClientForTests; + let sender: Sender; + let receiver: SessionReceiver; - it("Partitioned Subscription - Batch Receiver: no messages received for invalid sessionId", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions, sessionId); - await test_batching(TestClientType.PartitionedSubscriptionWithSessions); + before(async () => { + serviceBusClient = createServiceBusClientForTests(); }); - it("Unpartitioned Queue - Batch Receiver: no messages received for invalid sessionId", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions, sessionId); - await test_batching(TestClientType.UnpartitionedQueueWithSessions); + after(() => { + return serviceBusClient.test.after(); }); - it("Unpartitioned Subscription - Batch Receiver: no messages received for invalid sessionId", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedSubscriptionWithSessions, sessionId); - await test_batching(TestClientType.UnpartitionedSubscriptionWithSessions); - }); + async function beforeEachTest(testClientType: TestClientType, sessionId: string): Promise { + serviceBusClient = createServiceBusClientForTests(); + const entityNames = await serviceBusClient.test.createTestEntities(testClientType); - async function test_streaming(testClientType: TestClientType): Promise { - const testMessage = TestMessage.getSessionSample(); - await senderClient.send(testMessage); - - let receivedMsgs: ReceivedMessage[] = []; - receiverClient.subscribe({ - async processMessage(msg: ReceivedMessage, context: ContextWithSettlement) { - receivedMsgs.push(msg); - return Promise.resolve(); - }, - processError + receiver = await serviceBusClient.test.getSessionPeekLockReceiver(entityNames, { + sessionId }); - await delay(2000); - should.equal(receivedMsgs.length, 0, `Expected 0, received ${receivedMsgs.length} messages`); - await receiverClient.close(); - - receiverClient = ( - await getSenderReceiverClients( - testClientType, - "peekLock", - undefined, - { id: undefined }, - false - ) - ).receiverClient as SessionReceiver<"peekLock">; - receivedMsgs = []; - receiverClient.subscribe( - { - async processMessage(msg: ReceivedMessage, context: ContextWithSettlement) { - should.equal(msg.body, testMessage.body, "MessageBody is different than expected"); - should.equal( - msg.messageId, - testMessage.messageId, - "MessageId is different than expected" - ); - await context.complete(msg); - receivedMsgs.push(msg); - }, - processError - }, - { autoComplete: false } - ); - const msgsCheck = await checkWithTimeout(() => receivedMsgs.length === 1); - should.equal(msgsCheck, true, `Expected 1, received ${receivedMsgs.length} messages`); - should.equal(unexpectedError, undefined, unexpectedError && unexpectedError.message); + sender = serviceBusClient.test.addToCleanup( + serviceBusClient.getSender(entityNames.queue ?? entityNames.topic!) + ); - await testPeekMsgsLength(receiverClient, 0); + // Observation - + // Peeking into an empty session-enabled queue would run into either of the following errors.. + // 1. OperationTimeoutError: Unable to create the amqp receiver 'unpartitioned-queue-sessions-794f89be-3282-8b48-8ae0-a8af43c3ce36' + // on amqp session 'local-1_remote-1_connection-2' due to operation timeout. + // 2. MessagingError: Received an incorrect sessionId 'undefined' while creating the receiver 'unpartitioned-queue-sessions-86662b2b-acdc-1045-8ad4-fa3ab8807871'. + + // getSenderReceiverClients creates brand new queues/topic-subscriptions. + // Hence, commenting the following code since there is no need to purge/peek into a freshly created entity + + // await purge(receiverClient); + // const peekedMsgs = await receiverClient.diagnostics.peek(); + // const receiverEntityType = receiverClient.entityType; + // if (peekedMsgs.length) { + // chai.assert.fail(`Please use an empty ${receiverEntityType} for integration testing`); + // } } - it("Partitioned Queue - Streaming Receiver: no messages received for invalid sessionId", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedQueueWithSessions, sessionId); - await test_streaming(TestClientType.PartitionedQueueWithSessions); - }); - - it("Partitioned Subscription - Streaming Receiver: no messages received for invalid sessionId", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions, sessionId); - await test_streaming(TestClientType.PartitionedSubscriptionWithSessions); - }); - - it("Unpartitioned Queue - Streaming Receiver: no messages received for invalid sessionId", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions, sessionId); - await test_streaming(TestClientType.UnpartitionedQueueWithSessions); - }); - - it("Unpartitioned Subscription - Streaming Receiver: no messages received for invalid sessionId", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedSubscriptionWithSessions, sessionId); - await test_streaming(TestClientType.UnpartitionedSubscriptionWithSessions); - }); -}); - -describe("SessionReceiver with no sessionId", function(): void { - let sessionId: string | undefined; - beforeEach(() => { - sessionId = undefined; - }); - afterEach(async () => { - await afterEachTest(); - }); - - const testMessagesWithDifferentSessionIds: SendableMessageInfo[] = [ - { - body: "hello1", - messageId: `test message ${Math.random()}`, - sessionId: TestMessage.sessionId - }, - { - body: "hello2", - messageId: `test message ${Math.random()}`, - sessionId: testSessionId2 - } - ]; - - async function testComplete_batching(testClientType: TestClientType): Promise { - await senderClient.send(testMessagesWithDifferentSessionIds[0]); - await senderClient.send(testMessagesWithDifferentSessionIds[1]); - - let batch = await receiverClient.receiveBatch(2); - let msgs = batch.messages; - - should.equal(msgs.length, 1, "Unexpected number of messages received"); - should.equal(receiverClient.sessionId, msgs[0].sessionId, "Unexpected sessionId in receiver"); - should.equal( - testMessagesWithDifferentSessionIds.some( - (x) => - msgs[0].body === x.body && - msgs[0].messageId === x.messageId && - msgs[0].sessionId === x.sessionId - ), - true, - "Received Message doesnt match any of the test messages" - ); - await batch.context.complete(msgs[0]); - await receiverClient.close(); - - receiverClient = ( - await getSenderReceiverClients( - testClientType, - "peekLock", - undefined, - { id: undefined }, - false - ) - ).receiverClient as SessionReceiver<"peekLock">; - - batch = await receiverClient.receiveBatch(2); - msgs = batch.messages; - - should.equal(msgs.length, 1, "Unexpected number of messages received"); - should.equal(receiverClient.sessionId, msgs[0].sessionId, "Unexpected sessionId in receiver"); - should.equal( - testMessagesWithDifferentSessionIds.some( - (x) => - msgs[0].body === x.body && - msgs[0].messageId === x.messageId && - msgs[0].sessionId === x.sessionId - ), - true, - "Received Message doesnt match any of the test messages" - ); - await batch.context.complete(msgs[0]); - await testPeekMsgsLength(receiverClient, 0); + async function afterEachTest(): Promise { + await serviceBusClient.test.afterEach(); } - it("Partitioned Queue: complete() removes message from random session", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedQueueWithSessions, sessionId); - await testComplete_batching(TestClientType.PartitionedQueueWithSessions); - }); - - it("Partitioned Subscription: complete() removes message from random session", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions, sessionId); - await testComplete_batching(TestClientType.PartitionedSubscriptionWithSessions); - }); - - it("Unpartitioned Queue: complete() removes message from random session #RunInBrowser", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions, sessionId); - await testComplete_batching(TestClientType.UnpartitionedQueueWithSessions); - }); - - it("Unpartitioned Subscription: complete() removes message from random session", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedSubscriptionWithSessions, sessionId); - await testComplete_batching(TestClientType.UnpartitionedSubscriptionWithSessions); - }); -}); + describe("SessionReceiver with invalid sessionId", function(): void { + const nonExistentSessionId: string = "non" + TestMessage.sessionId; + // beforeEach(() => { + // sessionId = ; + // }); -describe("SessionReceiver with empty string as sessionId", function(): void { - let sessionId: string; - beforeEach(() => { - sessionId = ""; - }); - afterEach(async () => { - await afterEachTest(); - }); + afterEach(async () => { + await afterEachTest(); + }); - // Sending messages with different session id, so that we know for sure we pick the right one - // and that Service Bus is not choosing a random one for us - const testMessagesWithDifferentSessionIds: SendableMessageInfo[] = [ - { - body: "hello1", - messageId: `test message ${Math.random()}`, - sessionId: TestMessage.sessionId - }, - { - body: "hello2", - messageId: `test message ${Math.random()}`, - sessionId: "" + async function test_batching(testClientType: TestClientType): Promise { + const testMessage = TestMessage.getSessionSample(); + await sender.send(testMessage); + + let batch = await receiver.receiveBatch(1, 10); + let msgs = batch.messages; + should.equal(msgs.length, 0, "Unexpected number of messages received"); + + await receiver.close(); + + const entityNames = serviceBusClient.test.getTestEntities(testClientType); + + // get the next available session ID rather than specifying one + receiver = serviceBusClient.test.getSessionPeekLockReceiver(entityNames); + + batch = await receiver.receiveBatch(1); + msgs = batch.messages; + should.equal(msgs.length, 1, "Unexpected number of messages received"); + should.equal(Array.isArray(msgs), true, "`ReceivedMessages` is not an array"); + should.equal(msgs[0].body, testMessage.body, "MessageBody is different than expected"); + should.equal( + msgs[0].messageId, + testMessage.messageId, + "MessageId is different than expected" + ); + await batch.context.complete(msgs[0]); + await testPeekMsgsLength(receiver, 0); } - ]; - - async function testComplete_batching(testClientType: TestClientType): Promise { - await senderClient.send(testMessagesWithDifferentSessionIds[0]); - await senderClient.send(testMessagesWithDifferentSessionIds[1]); - - receiverClient = ( - await getSenderReceiverClients(testClientType, "peekLock", undefined, { id: "" }, false) - ).receiverClient as SessionReceiver<"peekLock">; - const batch = await receiverClient.receiveBatch(2); - const msgs = batch.messages; - - should.equal(msgs.length, 1, "Unexpected number of messages received"); - should.equal(receiverClient.sessionId, "", "Unexpected sessionId in receiver"); - should.equal( - testMessagesWithDifferentSessionIds[1].body === msgs[0].body && - testMessagesWithDifferentSessionIds[1].messageId === msgs[0].messageId && - testMessagesWithDifferentSessionIds[1].sessionId === msgs[0].sessionId, - true, - "Received Message doesnt match expected test message" - ); - await batch.context.complete(msgs[0]); - const peekedMsgsInSession = await receiverClient.diagnostics.peek(); - should.equal(peekedMsgsInSession.length, 0, "Unexpected number of messages peeked"); + it("Partitioned Queue - Batch Receiver: no messages received for invalid sessionId", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedQueueWithSessions, nonExistentSessionId); + await test_batching(TestClientType.PartitionedQueueWithSessions); + }); - await receiverClient.close(); - } + it("Partitioned Subscription - Batch Receiver: no messages received for invalid sessionId", async function(): Promise< + void + > { + await beforeEachTest( + TestClientType.PartitionedSubscriptionWithSessions, + nonExistentSessionId + ); + await test_batching(TestClientType.PartitionedSubscriptionWithSessions); + }); - it("Partitioned Queue: complete() removes message from random session", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedQueueWithSessions, sessionId); - await testComplete_batching(TestClientType.PartitionedQueueWithSessions); - }); + it("Unpartitioned Queue - Batch Receiver: no messages received for invalid sessionId", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions, nonExistentSessionId); + await test_batching(TestClientType.UnpartitionedQueueWithSessions); + }); - it("Partitioned Subscription: complete() removes message from random session", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions, sessionId); - await testComplete_batching(TestClientType.PartitionedSubscriptionWithSessions); - }); + it("Unpartitioned Subscription - Batch Receiver: no messages received for invalid sessionId", async function(): Promise< + void + > { + await beforeEachTest( + TestClientType.UnpartitionedSubscriptionWithSessions, + nonExistentSessionId + ); + await test_batching(TestClientType.UnpartitionedSubscriptionWithSessions); + }); - it("Unpartitioned Queue: complete() removes message from random session", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions, sessionId); - await testComplete_batching(TestClientType.UnpartitionedQueueWithSessions); - }); + async function test_streaming(testClientType: TestClientType): Promise { + const testMessage = TestMessage.getSessionSample(); + await sender.send(testMessage); - it("Unpartitioned Subscription: complete() removes message from random session", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedSubscriptionWithSessions, sessionId); - await testComplete_batching(TestClientType.UnpartitionedSubscriptionWithSessions); - }); -}); + let receivedMsgs: ReceivedMessage[] = []; + receiver.subscribe({ + async processMessage(msg: ReceivedMessage, context: ContextWithSettlement) { + receivedMsgs.push(msg); + return Promise.resolve(); + }, + processError + }); + await delay(2000); + should.equal(receivedMsgs.length, 0, `Expected 0, received ${receivedMsgs.length} messages`); + await receiver.close(); + + const entityNames = serviceBusClient.test.getTestEntities(testClientType); + + // get the next available session ID rather than specifying one + receiver = serviceBusClient.test.getSessionPeekLockReceiver(entityNames); + + receivedMsgs = []; + receiver.subscribe( + { + async processMessage(msg: ReceivedMessage, context: ContextWithSettlement) { + should.equal(msg.body, testMessage.body, "MessageBody is different than expected"); + should.equal( + msg.messageId, + testMessage.messageId, + "MessageId is different than expected" + ); + await context.complete(msg); + receivedMsgs.push(msg); + }, + processError + }, + { autoComplete: false } + ); -describe("Session State", function(): void { - afterEach(async () => { - await afterEachTest(); - }); + const msgsCheck = await checkWithTimeout(() => receivedMsgs.length === 1); + should.equal(msgsCheck, true, `Expected 1, received ${receivedMsgs.length} messages`); + should.equal(unexpectedError, undefined, unexpectedError && unexpectedError.message); - async function testGetSetState(testClientType: TestClientType): Promise { - const testMessage = TestMessage.getSessionSample(); - await senderClient.send(testMessage); - - let batch = await receiverClient.receiveBatch(2); - let msgs = batch.messages; - - should.equal(Array.isArray(msgs), true, "`ReceivedMessages` is not an array"); - should.equal(msgs.length, 1, "Unexpected number of messages received"); - should.equal(msgs[0].body, testMessage.body, "MessageBody is different than expected"); - should.equal(msgs[0].messageId, testMessage.messageId, "MessageId is different than expected"); - should.equal(msgs[0].sessionId, testMessage.sessionId, "SessionId is different than expected"); - - let testState = await receiverClient.getState(); - should.equal(!!testState, false, "SessionState is different than expected"); - await receiverClient.setState("new_state"); - testState = await receiverClient.getState(); - should.equal(testState, "new_state", "SessionState is different than expected"); - - await receiverClient.close(); - - receiverClient = ( - await getSenderReceiverClients( - testClientType, - "peekLock", - undefined, - { id: undefined }, - false - ) - ).receiverClient as SessionReceiver<"peekLock">; - batch = await receiverClient.receiveBatch(2); - msgs = batch.messages; - - should.equal(Array.isArray(msgs), true, "`ReceivedMessages` is not an array"); - should.equal(msgs.length, 1, "Unexpected number of messages received"); - should.equal(msgs[0].body, testMessage.body, "MessageBody is different than expected"); - should.equal(msgs[0].messageId, testMessage.messageId, "MessageId is different than expected"); - should.equal(msgs[0].sessionId, testMessage.sessionId, "SessionId is different than expected"); - - testState = await receiverClient.getState(); - should.equal(testState, "new_state", "SessionState is different than expected"); - - await receiverClient.setState(""); // clearing the session-state - await batch.context.complete(msgs[0]); - await testPeekMsgsLength(receiverClient, 0); - } - it("Partitioned Queue - Testing getState and setState", async function(): Promise { - await beforeEachTest(TestClientType.PartitionedQueueWithSessions, undefined); - await testGetSetState(TestClientType.PartitionedQueueWithSessions); - }); - it("Partitioned Subscription - Testing getState and setState", async function(): Promise { - await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions, undefined); - await testGetSetState(TestClientType.PartitionedSubscriptionWithSessions); - }); - it("Unpartitioned Queue - Testing getState and setState #RunInBrowser", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions, undefined); - await testGetSetState(TestClientType.UnpartitionedQueueWithSessions); - }); - it("Unpartitioned Subscription - Testing getState and setState", async function(): Promise { - await beforeEachTest(TestClientType.UnpartitionedSubscriptionWithSessions, undefined); - await testGetSetState(TestClientType.UnpartitionedSubscriptionWithSessions); - }); -}); + await testPeekMsgsLength(receiver, 0); + } -describe("Peek session", function(): void { - afterEach(async () => { - await afterEachTest(); - }); + it("Partitioned Queue - Streaming Receiver: no messages received for invalid sessionId", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedQueueWithSessions, nonExistentSessionId); + await test_streaming(TestClientType.PartitionedQueueWithSessions); + }); - async function eachTest(testClientType: TestClientType, useSessionId: boolean) { - await beforeEachTest(testClientType, undefined); - await peekSession(testClientType, useSessionId); - } + it("Partitioned Subscription - Streaming Receiver: no messages received for invalid sessionId", async function(): Promise< + void + > { + await beforeEachTest( + TestClientType.PartitionedSubscriptionWithSessions, + nonExistentSessionId + ); + await test_streaming(TestClientType.PartitionedSubscriptionWithSessions); + }); - async function peekSession(testClientType: TestClientType, useSessionId: boolean): Promise { - const testMessage = TestMessage.getSessionSample(); - await senderClient.send(testMessage); - - receiverClient = ( - await getSenderReceiverClients( - testClientType, - "peekLock", - undefined, - { id: useSessionId ? testMessage.sessionId : undefined }, - false - ) - ).receiverClient as SessionReceiver<"peekLock">; - - // At this point AMQP receiver link has not been established. - // peek() will not establish the link if sessionId was provided - const peekedMsgs = await receiverClient.diagnostics.peek(1); - should.equal(peekedMsgs.length, 1, "Unexpected number of messages peeked"); - should.equal(peekedMsgs[0].body, testMessage.body, "MessageBody is different than expected"); - should.equal( - peekedMsgs[0].messageId, - testMessage.messageId, - "MessageId is different than expected" - ); - should.equal( - peekedMsgs[0].sessionId, - testMessage.sessionId, - "SessionId is different than expected" - ); + it("Unpartitioned Queue - Streaming Receiver: no messages received for invalid sessionId", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions, nonExistentSessionId); + await test_streaming(TestClientType.UnpartitionedQueueWithSessions); + }); - const batch = await receiverClient.receiveBatch(1); - const msgs = batch.messages; - should.equal(msgs.length, 1, "Unexpected number of messages received"); - should.equal(msgs[0].body, testMessage.body, "MessageBody is different than expected"); - should.equal(msgs[0].messageId, testMessage.messageId, "MessageId is different than expected"); - should.equal(msgs[0].sessionId, testMessage.sessionId, "SessionId is different than expected"); + it("Unpartitioned Subscription - Streaming Receiver: no messages received for invalid sessionId", async function(): Promise< + void + > { + await beforeEachTest( + TestClientType.UnpartitionedSubscriptionWithSessions, + nonExistentSessionId + ); + await test_streaming(TestClientType.UnpartitionedSubscriptionWithSessions); + }); + }); - await batch.context.complete(msgs[0]); - } + describe("Session State", function(): void { + afterEach(async () => { + await afterEachTest(); + }); - it("Partitioned Queue - Peek Session with sessionId", async function(): Promise { - await eachTest(TestClientType.PartitionedQueueWithSessions, true); - }); - it("Partitioned Subscription - Peek Session with sessionId", async function(): Promise { - await eachTest(TestClientType.PartitionedSubscriptionWithSessions, true); - }); - it("Unpartitioned Queue - Peek Session with sessionId #RunInBrowser", async function(): Promise< - void - > { - await eachTest(TestClientType.UnpartitionedQueueWithSessions, true); - }); - it("Unpartitioned Subscription - Peek Session with sessionId", async function(): Promise { - await eachTest(TestClientType.UnpartitionedSubscriptionWithSessions, true); - }); - it("Partitioned Queue - Peek Session without sessionId", async function(): Promise { - await eachTest(TestClientType.PartitionedQueueWithSessions, false); - }); - it("Partitioned Subscription - Peek Session without sessionId", async function(): Promise { - await eachTest(TestClientType.PartitionedSubscriptionWithSessions, false); - }); - it("Unpartitioned Queue - Peek Session without sessionId #RunInBrowser", async function(): Promise< - void - > { - await eachTest(TestClientType.UnpartitionedQueueWithSessions, false); - }); - it("Unpartitioned Subscription - Peek Session without sessionId", async function(): Promise< - void - > { - await eachTest(TestClientType.UnpartitionedSubscriptionWithSessions, false); + async function testGetSetState(testClientType: TestClientType): Promise { + const testMessage = TestMessage.getSessionSample(); + await sender.send(testMessage); + + let batch = await receiver.receiveBatch(2); + let msgs = batch.messages; + + should.equal(Array.isArray(msgs), true, "`ReceivedMessages` is not an array"); + should.equal(msgs.length, 1, "Unexpected number of messages received"); + should.equal(msgs[0].body, testMessage.body, "MessageBody is different than expected"); + should.equal( + msgs[0].messageId, + testMessage.messageId, + "MessageId is different than expected" + ); + should.equal( + msgs[0].sessionId, + testMessage.sessionId, + "SessionId is different than expected" + ); + + let testState = await receiver.getState(); + should.equal(!!testState, false, "SessionState is different than expected"); + await receiver.setState("new_state"); + testState = await receiver.getState(); + should.equal(testState, "new_state", "SessionState is different than expected"); + + await receiver.close(); + + const entityNames = serviceBusClient.test.getTestEntities(testClientType); + + // get the next available session ID rather than specifying one + receiver = serviceBusClient.test.getSessionPeekLockReceiver(entityNames); + + batch = await receiver.receiveBatch(2); + msgs = batch.messages; + + should.equal(Array.isArray(msgs), true, "`ReceivedMessages` is not an array"); + should.equal(msgs.length, 1, "Unexpected number of messages received"); + should.equal(msgs[0].body, testMessage.body, "MessageBody is different than expected"); + should.equal( + msgs[0].messageId, + testMessage.messageId, + "MessageId is different than expected" + ); + should.equal( + msgs[0].sessionId, + testMessage.sessionId, + "SessionId is different than expected" + ); + + testState = await receiver.getState(); + should.equal(testState, "new_state", "SessionState is different than expected"); + + await receiver.setState(""); // clearing the session-state + await batch.context.complete(msgs[0]); + await testPeekMsgsLength(receiver, 0); + } + it("Partitioned Queue - Testing getState and setState", async function(): Promise { + await beforeEachTest(TestClientType.PartitionedQueueWithSessions, TestMessage.sessionId); + await testGetSetState(TestClientType.PartitionedQueueWithSessions); + }); + it("Partitioned Subscription - Testing getState and setState", async function(): Promise { + await beforeEachTest( + TestClientType.PartitionedSubscriptionWithSessions, + TestMessage.sessionId + ); + await testGetSetState(TestClientType.PartitionedSubscriptionWithSessions); + }); + it("Unpartitioned Queue - Testing getState and setState #RunInBrowser", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions, TestMessage.sessionId); + await testGetSetState(TestClientType.UnpartitionedQueueWithSessions); + }); + it("Unpartitioned Subscription - Testing getState and setState", async function(): Promise< + void + > { + await beforeEachTest( + TestClientType.UnpartitionedSubscriptionWithSessions, + TestMessage.sessionId + ); + await testGetSetState(TestClientType.UnpartitionedSubscriptionWithSessions); + }); }); }); diff --git a/sdk/servicebus/service-bus/test/streamingReceiver.spec.ts b/sdk/servicebus/service-bus/test/streamingReceiver.spec.ts index 0ea4569cc930..df947d7593f3 100644 --- a/sdk/servicebus/service-bus/test/streamingReceiver.spec.ts +++ b/sdk/servicebus/service-bus/test/streamingReceiver.spec.ts @@ -3,50 +3,23 @@ import chai from "chai"; import chaiAsPromised from "chai-as-promised"; -import { - delay, - ReceiveMode, - ServiceBusReceiverClient, - ServiceBusSenderClient, - ReceivedMessage, - ContextWithSettlement -} from "../src"; +import { delay, ReceiveMode, ReceivedMessage, ContextWithSettlement } from "../src"; import { getAlreadyReceivingErrorMsg } from "../src/util/errors"; -import { - checkWithTimeout, - TestClientType, - getSenderReceiverClients, - purge, - TestMessage, - EntityNames, - ReceiverClientTypeForUser, - ReceiverClientTypeForUserT -} from "./utils/testUtils"; +import { checkWithTimeout, TestClientType, TestMessage } from "./utils/testUtils"; import { StreamingReceiver } from "../src/core/streamingReceiver"; -import { AccessToken, parseConnectionString, TokenCredential } from "@azure/core-amqp"; -import { getEnvVars, EnvVarNames } from "./utils/envVarUtils"; -import { EnvironmentCredential } from "@azure/identity"; import { DispositionType } from "../src/serviceBusMessage"; +import { Receiver } from "../src/receivers/receiver"; +import { Sender } from "../src/sender"; +import { + ServiceBusClientForTests, + createServiceBusClientForTests, + testPeekMsgsLength +} from "./utils/testutils2"; const should = chai.should(); chai.use(chaiAsPromised); -async function testPeekMsgsLength( - client: ReceiverClientTypeForUser, - expectedPeekLength: number -): Promise { - const peekedMsgs = await client.diagnostics.peek(expectedPeekLength + 1); - should.equal( - peekedMsgs.length, - expectedPeekLength, - "Unexpected number of msgs found when peeking" - ); -} - -let senderClient: ServiceBusSenderClient; -let receiverClient: ReceiverClientTypeForUserT<"peekLock">; -let deadLetterClient: ReceiverClientTypeForUserT<"peekLock">; let errorWasThrown: boolean; let unexpectedError: Error | undefined; const maxDeliveryCount = 10; @@ -57,1037 +30,1090 @@ async function processError(err: Error): Promise { } } -async function beforeEachTest( - testClientType: TestClientType, - receiveMode?: "peekLock" | "receiveAndDelete" -): Promise { - if (!receiveMode) { - receiveMode = "peekLock"; - } - const clients = await getSenderReceiverClients(testClientType, receiveMode); - senderClient = clients.senderClient; - receiverClient = clients.receiverClient; - - deadLetterClient = new ServiceBusReceiverClient( - { - connectionString: getEnvVars().SERVICEBUS_CONNECTION_STRING, - queueName: receiverClient.getDeadLetterPath() - }, - "peekLock" - ); - - await purge(receiverClient); - await purge(deadLetterClient); - const peekedMsgs = await receiverClient.diagnostics.peek(); - - const receiverEntityType = receiverClient.entityType; - if (peekedMsgs.length) { - chai.assert.fail(`Please use an empty ${receiverEntityType} for integration testing`); - } - const peekedDeadMsgs = await deadLetterClient.diagnostics.peek(); - if (peekedDeadMsgs.length) { - chai.assert.fail( - `Please use an empty dead letter ${receiverEntityType} for integration testing` - ); - } - - errorWasThrown = false; - unexpectedError = undefined; -} - -async function afterEachTest(): Promise { - await senderClient.close(); - await receiverClient.close(); -} +describe("Streaming", () => { + let serviceBusClient: ServiceBusClientForTests; + let senderClient: Sender; + let receiverClient: Receiver; + let deadLetterClient: Receiver; -describe("Streaming - Misc Tests", function(): void { - afterEach(async () => { - await afterEachTest(); + before(() => { + serviceBusClient = createServiceBusClientForTests(); }); - async function testAutoComplete(): Promise { - const testMessage = TestMessage.getSample(); - await senderClient.send(testMessage); + after(async () => { + await serviceBusClient.test.after(); + }); - const receivedMsgs: ReceivedMessage[] = []; - receiverClient.subscribe({ - async processMessage(msg: ReceivedMessage) { - receivedMsgs.push(msg); - should.equal(msg.body, testMessage.body, "MessageBody is different than expected"); - should.equal(msg.messageId, testMessage.messageId, "MessageId is different than expected"); + async function beforeEachTest(testClientType: TestClientType): Promise { + const entityNames = await serviceBusClient.test.createTestEntities(testClientType); - return Promise.resolve(); - }, - processError - }); + receiverClient = await serviceBusClient.test.getPeekLockReceiver(entityNames); - const msgsCheck = await checkWithTimeout( - () => receivedMsgs.length === 1 && receivedMsgs[0].delivery.remote_settled === true + senderClient = serviceBusClient.test.addToCleanup( + serviceBusClient.getSender(entityNames.queue ?? entityNames.topic!) ); - should.equal( - msgsCheck, - true, - receivedMsgs.length !== 1 - ? `Expected 1, received ${receivedMsgs.length} messages` - : "Message didnt get auto-completed in time" + deadLetterClient = serviceBusClient.test.addToCleanup( + serviceBusClient.getReceiver(receiverClient.getDeadLetterPath(), "peekLock") ); - should.equal(unexpectedError, undefined, unexpectedError && unexpectedError.message); - should.equal(receivedMsgs.length, 1, "Unexpected number of messages"); - await testPeekMsgsLength(receiverClient, 0); + errorWasThrown = false; + unexpectedError = undefined; } - it("Partitioned Queue: AutoComplete removes the message", async function(): Promise { - await beforeEachTest(TestClientType.PartitionedQueue); - await testAutoComplete(); - }); + describe("Streaming - Misc Tests", function(): void { + afterEach(async () => { + return serviceBusClient.test.afterEach(); + }); - it("Partitioned Subscription: AutoComplete removes the message", async function(): Promise { - await beforeEachTest(TestClientType.PartitionedSubscription); - await testAutoComplete(); - }); + async function testAutoComplete(): Promise { + const testMessage = TestMessage.getSample(); + await senderClient.send(testMessage); - it("UnPartitioned Queue: AutoComplete removes the message #RunInBrowser", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedQueue); - await testAutoComplete(); - }); + const receivedMsgs: ReceivedMessage[] = []; + receiverClient.subscribe({ + async processMessage(msg: ReceivedMessage) { + receivedMsgs.push(msg); + should.equal(msg.body, testMessage.body, "MessageBody is different than expected"); + should.equal( + msg.messageId, + testMessage.messageId, + "MessageId is different than expected" + ); - it("UnPartitioned Subscription: AutoComplete removes the message", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedSubscription); - await testAutoComplete(); - }); + return Promise.resolve(); + }, + processError + }); - async function testManualComplete(): Promise { - const testMessage = TestMessage.getSample(); - await senderClient.send(testMessage); + const msgsCheck = await checkWithTimeout( + () => receivedMsgs.length === 1 && receivedMsgs[0].delivery.remote_settled === true + ); - const receivedMsgs: ReceivedMessage[] = []; - let contextToSettle: ContextWithSettlement; - receiverClient.subscribe( - { - async processMessage(msg: ReceivedMessage, context: ContextWithSettlement) { + should.equal( + msgsCheck, + true, + receivedMsgs.length !== 1 + ? `Expected 1, received ${receivedMsgs.length} messages` + : "Message didnt get auto-completed in time" + ); + + should.equal(unexpectedError, undefined, unexpectedError && unexpectedError.message); + should.equal(receivedMsgs.length, 1, "Unexpected number of messages"); + await testPeekMsgsLength(receiverClient, 0); + } + async function testAutoCompleteWithSenderAndReceiver(): Promise { + const testMessage = TestMessage.getSample(); + await senderClient.send(testMessage); + + const receivedMsgs: ReceivedMessage[] = []; + receiverClient.subscribe({ + async processMessage(msg: ReceivedMessage) { receivedMsgs.push(msg); - contextToSettle = context; should.equal(msg.body, testMessage.body, "MessageBody is different than expected"); should.equal( msg.messageId, testMessage.messageId, "MessageId is different than expected" ); + return Promise.resolve(); }, processError - }, - { autoComplete: false } - ); + }); - const msgsCheck = await checkWithTimeout(() => receivedMsgs.length === 1); + const msgsCheck = await checkWithTimeout( + () => receivedMsgs.length === 1 && receivedMsgs[0].delivery.remote_settled === true + ); - should.equal(msgsCheck, true, `Expected 1, received ${receivedMsgs.length} messages`); - await testPeekMsgsLength(receiverClient, 1); - should.equal(receivedMsgs.length, 1, "Unexpected number of messages"); + should.equal( + msgsCheck, + true, + receivedMsgs.length !== 1 + ? `Expected 1, received ${receivedMsgs.length} messages` + : "Message didnt get auto-completed in time" + ); - await contextToSettle!.complete(receivedMsgs[0]); + should.equal(unexpectedError, undefined, unexpectedError && unexpectedError.message); + should.equal(receivedMsgs.length, 1, "Unexpected number of messages"); - should.equal(unexpectedError, undefined, unexpectedError && unexpectedError.message); - await testPeekMsgsLength(receiverClient, 0); - } + const peekedMsgs = await receiverClient.diagnostics.peek(1); + should.equal(peekedMsgs.length, 0, "Unexpected number of msgs found when peeking"); + } - it("Partitioned Queue: Disabled autoComplete, no manual complete retains the message", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedQueue); - await testManualComplete(); - }); + it("Partitioned Queue: AutoComplete removes the message", async function(): Promise { + await beforeEachTest(TestClientType.PartitionedQueue); + await testAutoCompleteWithSenderAndReceiver(); + }); - it("Partitioned Subscription: Disabled autoComplete, no manual complete retains the message", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedSubscription); - await testManualComplete(); - }); + it("Partitioned Subscription: AutoComplete removes the message", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedSubscription); + await testAutoComplete(); + }); - it("UnPartitioned Queue: Disabled autoComplete, no manual complete retains the message #RunInBrowser", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedQueue); - await testManualComplete(); - }); + it("UnPartitioned Queue: AutoComplete removes the message #RunInBrowser", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedQueue); + await testAutoComplete(); + }); - it("UnPartitioned Subscription: Disabled autoComplete, no manual complete retains the message", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedSubscription); - await testManualComplete(); - }); + it("UnPartitioned Subscription: AutoComplete removes the message", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedSubscription); + await testAutoComplete(); + }); - it("onDetached should forward error messages if it fails to retry", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedQueue); - let streamingReceiver: StreamingReceiver | undefined; - try { - let actualError: Error | undefined; - streamingReceiver = await StreamingReceiver.create( - (receiverClient as any)._receiver._context, + async function testManualComplete(): Promise { + const testMessage = TestMessage.getSample(); + await senderClient.send(testMessage); + + const receivedMsgs: ReceivedMessage[] = []; + let contextToSettle: ContextWithSettlement; + receiverClient.subscribe( { - receiveMode: ReceiveMode.peekLock - } + async processMessage(msg: ReceivedMessage, context: ContextWithSettlement) { + receivedMsgs.push(msg); + contextToSettle = context; + should.equal(msg.body, testMessage.body, "MessageBody is different than expected"); + should.equal( + msg.messageId, + testMessage.messageId, + "MessageId is different than expected" + ); + return Promise.resolve(); + }, + processError + }, + { autoComplete: false } ); - streamingReceiver.receive( - async () => {}, - (err) => { - actualError = err; - } - ); + const msgsCheck = await checkWithTimeout(() => receivedMsgs.length === 1); + should.equal(msgsCheck, true, `Expected 1, received ${receivedMsgs.length} messages`); - // overwrite _init to throw a non-retryable error. - // this will be called by onDetached - (streamingReceiver as any)._init = async () => { - const error = new Error("Expected test error!"); - // prevent retry from translating error. - (error as any).translated = true; - throw error; - }; + await testPeekMsgsLength(receiverClient, 1); + should.equal(receivedMsgs.length, 1, "Unexpected number of messages"); - // call detached directly - await streamingReceiver.onDetached(); + await contextToSettle!.complete(receivedMsgs[0]); - should.equal( - actualError!.message, - "Expected test error!", - "Did not see the expected error in user-provided error handler." - ); - } finally { - if (streamingReceiver) { - await streamingReceiver.close(); - } + should.equal(unexpectedError, undefined, unexpectedError && unexpectedError.message); + await testPeekMsgsLength(receiverClient, 0); } - }); -}); -describe("Streaming - Complete message", function(): void { - afterEach(async () => { - await afterEachTest(); + it("Partitioned Queue: Disabled autoComplete, no manual complete retains the message", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedQueue); + await testManualComplete(); + }); + + it("Partitioned Subscription: Disabled autoComplete, no manual complete retains the message", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedSubscription); + await testManualComplete(); + }); + + it("UnPartitioned Queue: Disabled autoComplete, no manual complete retains the message #RunInBrowser", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedQueue); + await testManualComplete(); + }); + + it("UnPartitioned Subscription: Disabled autoComplete, no manual complete retains the message", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedSubscription); + await testManualComplete(); + }); + + it("onDetached should forward error messages if it fails to retry", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedQueue); + let streamingReceiver: StreamingReceiver | undefined; + try { + let actualError: Error | undefined; + streamingReceiver = await StreamingReceiver.create((receiverClient as any)._context, { + receiveMode: ReceiveMode.peekLock + }); + + streamingReceiver.receive( + async () => {}, + (err) => { + actualError = err; + } + ); + + // overwrite _init to throw a non-retryable error. + // this will be called by onDetached + (streamingReceiver as any)._init = async () => { + const error = new Error("Expected test error!"); + // prevent retry from translating error. + (error as any).translated = true; + throw error; + }; + + // call detached directly + await streamingReceiver.onDetached(); + + should.equal( + actualError!.message, + "Expected test error!", + "Did not see the expected error in user-provided error handler." + ); + } finally { + if (streamingReceiver) { + await streamingReceiver.close(); + } + } + }); }); - async function testComplete(autoComplete: boolean): Promise { - const testMessage = TestMessage.getSample(); - await senderClient.send(testMessage); + describe("Streaming - Complete message", function(): void { + afterEach(async () => { + return serviceBusClient.test.afterEach(); + }); - const receivedMsgs: ReceivedMessage[] = []; - receiverClient.subscribe( - { - async processMessage(msg: ReceivedMessage, context: ContextWithSettlement) { - should.equal(msg.body, testMessage.body, "MessageBody is different than expected"); - should.equal( - msg.messageId, - testMessage.messageId, - "MessageId is different than expected" - ); - await context.complete(msg); - receivedMsgs.push(msg); + async function testComplete(autoComplete: boolean): Promise { + const testMessage = TestMessage.getSample(); + await senderClient.send(testMessage); + + const receivedMsgs: ReceivedMessage[] = []; + receiverClient.subscribe( + { + async processMessage(msg: ReceivedMessage, context: ContextWithSettlement) { + should.equal(msg.body, testMessage.body, "MessageBody is different than expected"); + should.equal( + msg.messageId, + testMessage.messageId, + "MessageId is different than expected" + ); + await context.complete(msg); + receivedMsgs.push(msg); + }, + processError }, - processError - }, - { autoComplete } - ); + { autoComplete } + ); - const msgsCheck = await checkWithTimeout(() => receivedMsgs.length === 1); - should.equal(msgsCheck, true, `Expected 1, received ${receivedMsgs.length} messages`); + const msgsCheck = await checkWithTimeout(() => receivedMsgs.length === 1); + should.equal(msgsCheck, true, `Expected 1, received ${receivedMsgs.length} messages`); - should.equal(unexpectedError, undefined, unexpectedError && unexpectedError.message); - should.equal(receivedMsgs.length, 1, "Unexpected number of messages"); - await testPeekMsgsLength(receiverClient, 0); - } - it("Partitioned Queue: complete() removes message", async function(): Promise { - await beforeEachTest(TestClientType.PartitionedQueue); - await testComplete(false); - }); + should.equal(unexpectedError, undefined, unexpectedError && unexpectedError.message); + should.equal(receivedMsgs.length, 1, "Unexpected number of messages"); + await testPeekMsgsLength(receiverClient, 0); + } + it("Partitioned Queue: complete() removes message", async function(): Promise { + await beforeEachTest(TestClientType.PartitionedQueue); + await testComplete(false); + }); - it("Partitioned Subscription: complete() removes message", async function(): Promise { - await beforeEachTest(TestClientType.PartitionedSubscription); - await testComplete(false); - }); + it("Partitioned Subscription: complete() removes message", async function(): Promise { + await beforeEachTest(TestClientType.PartitionedSubscription); + await testComplete(false); + }); - it("UnPartitioned Queue: complete() removes message", async function(): Promise { - await beforeEachTest(TestClientType.UnpartitionedQueue); - await testComplete(false); - }); + it("UnPartitioned Queue: complete() removes message", async function(): Promise { + await beforeEachTest(TestClientType.UnpartitionedQueue); + await testComplete(false); + }); - it("UnPartitioned Subscription: complete() removes message", async function(): Promise { - await beforeEachTest(TestClientType.UnpartitionedSubscription); - await testComplete(false); - }); + it("UnPartitioned Subscription: complete() removes message", async function(): Promise { + await beforeEachTest(TestClientType.UnpartitionedSubscription); + await testComplete(false); + }); - it("Partitioned Queue with autoComplete: complete() removes message", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedQueue); - await testComplete(true); - }); + it("Partitioned Queue with autoComplete: complete() removes message", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedQueue); + await testComplete(true); + }); - it("Partitioned Subscription with autoComplete: complete() removes message", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedSubscription); - await testComplete(true); - }); + it("Partitioned Subscription with autoComplete: complete() removes message", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedSubscription); + await testComplete(true); + }); - it("UnPartitioned Queue with autoComplete: complete() removes message", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedQueue); - await testComplete(true); - }); + it("UnPartitioned Queue with autoComplete: complete() removes message", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedQueue); + await testComplete(true); + }); - it("UnPartitioned Subscription with autoComplete: complete() removes message", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedSubscription); - await testComplete(true); + it("UnPartitioned Subscription with autoComplete: complete() removes message", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedSubscription); + await testComplete(true); + }); }); -}); -describe("Streaming - Abandon message", function(): void { - afterEach(async () => { - await afterEachTest(); - }); + describe("Streaming - Abandon message", function(): void { + afterEach(async () => { + return serviceBusClient.test.afterEach(); + }); - async function testMultipleAbandons(): Promise { - const testMessage = TestMessage.getSample(); - await senderClient.send(testMessage); + async function testMultipleAbandons(): Promise { + const testMessage = TestMessage.getSample(); + await senderClient.send(testMessage); - let checkDeliveryCount = 0; + let checkDeliveryCount = 0; - receiverClient.subscribe( - { - async processMessage(msg: ReceivedMessage, context: ContextWithSettlement) { - should.equal( - msg.deliveryCount, - checkDeliveryCount, - "DeliveryCount is different than expected" - ); - await context.abandon(msg); - checkDeliveryCount++; + receiverClient.subscribe( + { + async processMessage(msg: ReceivedMessage, context: ContextWithSettlement) { + should.equal( + msg.deliveryCount, + checkDeliveryCount, + "DeliveryCount is different than expected" + ); + await context.abandon(msg); + checkDeliveryCount++; + }, + processError }, - processError - }, - { autoComplete: false } - ); + { autoComplete: false } + ); - const deliveryCountFlag = await checkWithTimeout(() => checkDeliveryCount === maxDeliveryCount); - should.equal(deliveryCountFlag, true, "DeliveryCount is different than expected"); + const deliveryCountFlag = await checkWithTimeout( + () => checkDeliveryCount === maxDeliveryCount + ); + should.equal(deliveryCountFlag, true, "DeliveryCount is different than expected"); - should.equal(unexpectedError, undefined, unexpectedError && unexpectedError.message); + should.equal(unexpectedError, undefined, unexpectedError && unexpectedError.message); - await testPeekMsgsLength(receiverClient, 0); // No messages in the queue + await testPeekMsgsLength(receiverClient, 0); // No messages in the queue - const deadLetterMsgsBatch = await deadLetterClient.receiveBatch(1); - const deadLetterMsgs = deadLetterMsgsBatch.messages; - should.equal(Array.isArray(deadLetterMsgs), true, "`ReceivedMessages` is not an array"); - should.equal(deadLetterMsgs.length, 1, "Unexpected number of messages"); - should.equal( - deadLetterMsgs[0].deliveryCount, - maxDeliveryCount, - "DeliveryCount is different than expected" - ); - should.equal( - deadLetterMsgs[0].messageId, - testMessage.messageId, - "MessageId is different than expected" - ); + const deadLetterMsgsBatch = await deadLetterClient.receiveBatch(1); + const deadLetterMsgs = deadLetterMsgsBatch.messages; + should.equal(Array.isArray(deadLetterMsgs), true, "`ReceivedMessages` is not an array"); + should.equal(deadLetterMsgs.length, 1, "Unexpected number of messages"); + should.equal( + deadLetterMsgs[0].deliveryCount, + maxDeliveryCount, + "DeliveryCount is different than expected" + ); + should.equal( + deadLetterMsgs[0].messageId, + testMessage.messageId, + "MessageId is different than expected" + ); - await deadLetterMsgsBatch.context.complete(deadLetterMsgs[0]); + await deadLetterMsgsBatch.context.complete(deadLetterMsgs[0]); - await testPeekMsgsLength(deadLetterClient, 0); - } + await testPeekMsgsLength(deadLetterClient, 0); + } - it("Partitioned Queue: Multiple abandons until maxDeliveryCount", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedQueue); - await testMultipleAbandons(); - }); + it("Partitioned Queue: Multiple abandons until maxDeliveryCount", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedQueue); + await testMultipleAbandons(); + }); - it("Partitioned Subscription: Multiple abandons until maxDeliveryCount", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedSubscription); - await testMultipleAbandons(); - }); + it("Partitioned Subscription: Multiple abandons until maxDeliveryCount", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedSubscription); + await testMultipleAbandons(); + }); - it("Unpartitioned Queue: Multiple abandons until maxDeliveryCount", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedQueue); - await testMultipleAbandons(); - }); + it("Unpartitioned Queue: Multiple abandons until maxDeliveryCount", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedQueue); + await testMultipleAbandons(); + }); - it("Unpartitioned Subscription: Multiple abandons until maxDeliveryCount", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedSubscription); - await testMultipleAbandons(); + it("Unpartitioned Subscription: Multiple abandons until maxDeliveryCount", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedSubscription); + await testMultipleAbandons(); + }); }); -}); -describe("Streaming - Defer message", function(): void { - afterEach(async () => { - await afterEachTest(); - }); + describe("Streaming - Defer message", function(): void { + afterEach(async () => { + return serviceBusClient.test.afterEach(); + }); - async function testDefer(testClientType: TestClientType, autoComplete: boolean): Promise { - const testMessage = TestMessage.getSample(); - await senderClient.send(testMessage); - let sequenceNum: any = 0; + async function testDefer(autoComplete: boolean): Promise { + const testMessage = TestMessage.getSample(); + await senderClient.send(testMessage); + let sequenceNum: any = 0; - receiverClient.subscribe( - { - async processMessage(msg: ReceivedMessage, context: ContextWithSettlement) { - await context.defer(msg); - sequenceNum = msg.sequenceNumber; + receiverClient.subscribe( + { + async processMessage(msg: ReceivedMessage, context: ContextWithSettlement) { + await context.defer(msg); + sequenceNum = msg.sequenceNumber; + }, + processError }, - processError - }, - { autoComplete } - ); - const sequenceNumCheck = await checkWithTimeout(() => sequenceNum !== 0); - should.equal( - sequenceNumCheck, - true, - "Either the message is not received or observed an unexpected SequenceNumber." - ); + { autoComplete } + ); + const sequenceNumCheck = await checkWithTimeout(() => sequenceNum !== 0); + should.equal( + sequenceNumCheck, + true, + "Either the message is not received or observed an unexpected SequenceNumber." + ); - should.equal(unexpectedError, undefined, unexpectedError && unexpectedError.message); - const deferredMsgs = await receiverClient.receiveDeferredMessages([sequenceNum]); - if (!deferredMsgs) { - throw "No message received for sequence number"; - } + should.equal(unexpectedError, undefined, unexpectedError && unexpectedError.message); + const deferredMsgs = await receiverClient.receiveDeferredMessages([sequenceNum]); + if (!deferredMsgs) { + throw "No message received for sequence number"; + } - should.equal(deferredMsgs[0].body, testMessage.body, "MessageBody is different than expected"); - should.equal( - deferredMsgs[0].messageId, - testMessage.messageId, - "MessageId is different than expected" - ); - should.equal(deferredMsgs[0].deliveryCount, 1, "DeliveryCount is different than expected"); + should.equal( + deferredMsgs[0].body, + testMessage.body, + "MessageBody is different than expected" + ); + should.equal( + deferredMsgs[0].messageId, + testMessage.messageId, + "MessageId is different than expected" + ); + should.equal(deferredMsgs[0].deliveryCount, 1, "DeliveryCount is different than expected"); - await deferredMsgs[0].complete(); - await testPeekMsgsLength(receiverClient, 0); - } + await deferredMsgs[0].complete(); + await testPeekMsgsLength(receiverClient, 0); + } - it("Partitioned Queue: defer() moves message to deferred queue", async function(): Promise { - await beforeEachTest(TestClientType.PartitionedQueue); - await testDefer(TestClientType.PartitionedQueue, false); - }); + it("Partitioned Queue: defer() moves message to deferred queue", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedQueue); + await testDefer(false); + }); - it("Partitioned Subscription: defer() moves message to deferred queue", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedSubscription); - await testDefer(TestClientType.PartitionedSubscription, false); - }); + it("Partitioned Subscription: defer() moves message to deferred queue", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedSubscription); + await testDefer(false); + }); - it("UnPartitioned Queue: defer() moves message to deferred queue", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedQueue); - await testDefer(TestClientType.UnpartitionedQueue, false); - }); + it("UnPartitioned Queue: defer() moves message to deferred queue", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedQueue); + await testDefer(false); + }); - it("UnPartitioned Subscription: defer() moves message to deferred queue", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedSubscription); - await testDefer(TestClientType.UnpartitionedSubscription, false); - }); + it("UnPartitioned Subscription: defer() moves message to deferred queue", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedSubscription); + await testDefer(false); + }); - it("Partitioned Queue with autoComplete: defer() moves message to deferred queue", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedQueue); - await testDefer(TestClientType.PartitionedQueue, true); - }); + it("Partitioned Queue with autoComplete: defer() moves message to deferred queue", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedQueue); + await testDefer(true); + }); - it("Partitioned Subscription with autoComplete: defer() moves message to deferred queue", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedSubscription); - await testDefer(TestClientType.PartitionedSubscription, true); - }); + it("Partitioned Subscription with autoComplete: defer() moves message to deferred queue", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedSubscription); + await testDefer(true); + }); - it("UnPartitioned Queue with autoComplete: defer() moves message to deferred queue", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedQueue); - await testDefer(TestClientType.UnpartitionedQueue, true); - }); + it("UnPartitioned Queue with autoComplete: defer() moves message to deferred queue", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedQueue); + await testDefer(true); + }); - it("UnPartitioned Subscription with autoComplete: defer() moves message to deferred queue", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedSubscription); - await testDefer(TestClientType.UnpartitionedSubscription, true); + it("UnPartitioned Subscription with autoComplete: defer() moves message to deferred queue", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedSubscription); + await testDefer(true); + }); }); -}); -describe("Streaming - Deadletter message", function(): void { - afterEach(async () => { - await afterEachTest(); - }); + describe("Streaming - Deadletter message", function(): void { + afterEach(async () => { + return serviceBusClient.test.afterEach(); + }); - async function testDeadletter(autoComplete: boolean): Promise { - const testMessage = TestMessage.getSample(); - await senderClient.send(testMessage); + async function testDeadletter(autoComplete: boolean): Promise { + const testMessage = TestMessage.getSample(); + await senderClient.send(testMessage); - const receivedMsgs: ReceivedMessage[] = []; + const receivedMsgs: ReceivedMessage[] = []; - receiverClient.subscribe( - { - async processMessage(msg: ReceivedMessage, context: ContextWithSettlement) { - await context.deadLetter(msg); - receivedMsgs.push(msg); + receiverClient.subscribe( + { + async processMessage(msg: ReceivedMessage, context: ContextWithSettlement) { + await context.deadLetter(msg); + receivedMsgs.push(msg); + }, + processError }, - processError - }, - { autoComplete } - ); - const msgsCheck = await checkWithTimeout(() => receivedMsgs.length === 1); - should.equal(msgsCheck, true, `Expected 1, received ${receivedMsgs.length} messages`); - - should.equal(unexpectedError, undefined, unexpectedError && unexpectedError.message); - should.equal(receivedMsgs.length, 1, "Unexpected number of messages"); - - await testPeekMsgsLength(receiverClient, 0); - - const deadLetterMsgsBatch = await deadLetterClient.receiveBatch(1); - const deadLetterMsgs = deadLetterMsgsBatch.messages; - should.equal(Array.isArray(deadLetterMsgs), true, "`ReceivedMessages` is not an array"); - should.equal(deadLetterMsgs.length, 1, "Unexpected number of messages"); - should.equal( - deadLetterMsgs[0].messageId, - testMessage.messageId, - "MessageId is different than expected" - ); + { autoComplete } + ); + const msgsCheck = await checkWithTimeout(() => receivedMsgs.length === 1); + should.equal(msgsCheck, true, `Expected 1, received ${receivedMsgs.length} messages`); - await deadLetterMsgsBatch.context.complete(deadLetterMsgs[0]); - await testPeekMsgsLength(deadLetterClient, 0); - } + should.equal(unexpectedError, undefined, unexpectedError && unexpectedError.message); + should.equal(receivedMsgs.length, 1, "Unexpected number of messages"); - it("Partitioned Queue: deadLetter() moves message to deadletter queue", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedQueue); - await testDeadletter(false); - }); + await testPeekMsgsLength(receiverClient, 0); - it("Partitioned Subscription: deadLetter() moves message to deadletter queue", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedSubscription); - await testDeadletter(false); - }); + const deadLetterMsgsBatch = await deadLetterClient.receiveBatch(1); + const deadLetterMsgs = deadLetterMsgsBatch.messages; + should.equal(Array.isArray(deadLetterMsgs), true, "`ReceivedMessages` is not an array"); + should.equal(deadLetterMsgs.length, 1, "Unexpected number of messages"); + should.equal( + deadLetterMsgs[0].messageId, + testMessage.messageId, + "MessageId is different than expected" + ); - it("UnPartitioned Queue: deadLetter() moves message to deadletter queue", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedQueue); - await testDeadletter(false); - }); + await deadLetterMsgsBatch.context.complete(deadLetterMsgs[0]); + await testPeekMsgsLength(deadLetterClient, 0); + } - it("UnPartitioned Subscription: deadLetter() moves message to deadletter queue", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedSubscription); - await testDeadletter(false); - }); + it("Partitioned Queue: deadLetter() moves message to deadletter queue", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedQueue); + await testDeadletter(false); + }); - it("Partitioned Queue with autoComplete: deadLetter() moves message to deadletter queue", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedQueue); - await testDeadletter(true); - }); + it("Partitioned Subscription: deadLetter() moves message to deadletter queue", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedSubscription); + await testDeadletter(false); + }); - it("Partitioned Subscription with autoComplete: deadLetter() moves message to deadletter", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedSubscription); - await testDeadletter(true); - }); + it("UnPartitioned Queue: deadLetter() moves message to deadletter queue", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedQueue); + await testDeadletter(false); + }); - it("UnPartitioned Queue with autoComplete: deadLetter() moves message to deadletter queue", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedQueue); - await testDeadletter(true); - }); + it("UnPartitioned Subscription: deadLetter() moves message to deadletter queue", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedSubscription); + await testDeadletter(false); + }); - it("UnPartitioned Subscription with autoComplete: deadLetter() moves message to deadletter queue", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedSubscription); - await testDeadletter(true); - }); -}); + it("Partitioned Queue with autoComplete: deadLetter() moves message to deadletter queue", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedQueue); + await testDeadletter(true); + }); -describe("Streaming - Multiple Receiver Operations", function(): void { - afterEach(async () => { - await afterEachTest(); - }); + it("Partitioned Subscription with autoComplete: deadLetter() moves message to deadletter", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedSubscription); + await testDeadletter(true); + }); + + it("UnPartitioned Queue with autoComplete: deadLetter() moves message to deadletter queue", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedQueue); + await testDeadletter(true); + }); - async function testMultipleReceiveCalls(): Promise { - let errorMessage; - const expectedErrorMessage = getAlreadyReceivingErrorMsg(receiverClient.entityPath); + it("UnPartitioned Subscription with autoComplete: deadLetter() moves message to deadletter queue", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedSubscription); + await testDeadletter(true); + }); + }); - receiverClient.subscribe({ - async processMessage(msg: ReceivedMessage, context: ContextWithSettlement) { - await context.complete(msg); - }, - processError + describe("Streaming - Multiple Receiver Operations", function(): void { + afterEach(async () => { + return serviceBusClient.test.afterEach(); }); - await delay(5000); - try { + + async function testMultipleReceiveCalls(): Promise { + let errorMessage; + const expectedErrorMessage = getAlreadyReceivingErrorMsg(receiverClient.entityPath); + receiverClient.subscribe({ async processMessage(msg: ReceivedMessage, context: ContextWithSettlement) { - return Promise.resolve(); + await context.complete(msg); }, processError }); - } catch (err) { - errorMessage = err && err.message; - } - should.equal( - errorMessage, - expectedErrorMessage, - "Unexpected error message for registerMessageHandler" - ); - should.equal(unexpectedError, undefined, unexpectedError && unexpectedError.message); + await delay(5000); + try { + receiverClient.subscribe({ + async processMessage() { + return Promise.resolve(); + }, + processError + }); + } catch (err) { + errorMessage = err && err.message; + } + should.equal( + errorMessage, + expectedErrorMessage, + "Unexpected error message for registerMessageHandler" + ); + should.equal(unexpectedError, undefined, unexpectedError && unexpectedError.message); - errorMessage = ""; - try { - await receiverClient.receiveBatch(1); - } catch (err) { - errorMessage = err && err.message; + errorMessage = ""; + try { + await receiverClient.receiveBatch(1); + } catch (err) { + errorMessage = err && err.message; + } + should.equal( + errorMessage, + expectedErrorMessage, + "Unexpected error message for receiveMessages" + ); } - should.equal( - errorMessage, - expectedErrorMessage, - "Unexpected error message for receiveMessages" - ); - } - it("UnPartitioned Queue: Second receive operation should fail if the first streaming receiver is not stopped #RunInBrowser", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedQueue); - await testMultipleReceiveCalls(); - }); -}); - -describe("Streaming - Settle an already Settled message throws error", () => { - afterEach(async () => { - await afterEachTest(); + it("UnPartitioned Queue: Second receive operation should fail if the first streaming receiver is not stopped #RunInBrowser", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedQueue); + await testMultipleReceiveCalls(); + }); }); - const testError = (err: Error, operation: DispositionType): void => { - should.equal( - err.message, - `Failed to ${operation} the message as this message is already settled.`, - "ErrorMessage is different than expected" - ); - errorWasThrown = true; - }; - - async function testSettlement(operation: DispositionType): Promise { - const testMessage = TestMessage.getSample(); - await senderClient.send(testMessage); - const receivedMsgs: ReceivedMessage[] = []; - let contextToSettle: ContextWithSettlement; - receiverClient.subscribe({ - async processMessage(msg: ReceivedMessage, context: ContextWithSettlement) { - receivedMsgs.push(msg); - contextToSettle = context; - return Promise.resolve(); - }, - processError - }); - - const msgsCheck = await checkWithTimeout( - () => receivedMsgs.length === 1 && receivedMsgs[0].delivery.remote_settled === true - ); - should.equal( - msgsCheck, - true, - receivedMsgs.length !== 1 - ? `Expected 1, received ${receivedMsgs.length} messages` - : "Message didnt get auto-completed in time" - ); + describe("Streaming - Settle an already Settled message throws error", () => { + afterEach(async () => { + return serviceBusClient.test.afterEach(); + }); - should.equal(unexpectedError, undefined, unexpectedError && unexpectedError.message); + const testError = (err: Error, operation: DispositionType): void => { + should.equal( + err.message, + `Failed to ${operation} the message as this message is already settled.`, + "ErrorMessage is different than expected" + ); + errorWasThrown = true; + }; + + async function testSettlement(operation: DispositionType): Promise { + const testMessage = TestMessage.getSample(); + await senderClient.send(testMessage); + const receivedMsgs: ReceivedMessage[] = []; + let contextToSettle: ContextWithSettlement; + receiverClient.subscribe({ + async processMessage(msg: ReceivedMessage, context: ContextWithSettlement) { + receivedMsgs.push(msg); + contextToSettle = context; + return Promise.resolve(); + }, + processError + }); - should.equal(receivedMsgs.length, 1, "Unexpected number of messages"); - should.equal(receivedMsgs[0].body, testMessage.body, "MessageBody is different than expected"); - should.equal( - receivedMsgs[0].messageId, - testMessage.messageId, - "MessageId is different than expected" - ); + const msgsCheck = await checkWithTimeout( + () => receivedMsgs.length === 1 && receivedMsgs[0].delivery.remote_settled === true + ); + should.equal( + msgsCheck, + true, + receivedMsgs.length !== 1 + ? `Expected 1, received ${receivedMsgs.length} messages` + : "Message didnt get auto-completed in time" + ); - await testPeekMsgsLength(receiverClient, 0); + should.equal(unexpectedError, undefined, unexpectedError && unexpectedError.message); - if (operation === DispositionType.complete) { - await contextToSettle!.complete(receivedMsgs[0]).catch((err) => testError(err, operation)); - } else if (operation === DispositionType.abandon) { - await contextToSettle!.abandon(receivedMsgs[0]).catch((err) => testError(err, operation)); - } else if (operation === DispositionType.deadletter) { - await contextToSettle!.deadLetter(receivedMsgs[0]).catch((err) => testError(err, operation)); - } else if (operation === DispositionType.defer) { - await contextToSettle!.defer(receivedMsgs[0]).catch((err) => testError(err, operation)); - } + should.equal(receivedMsgs.length, 1, "Unexpected number of messages"); + should.equal( + receivedMsgs[0].body, + testMessage.body, + "MessageBody is different than expected" + ); + should.equal( + receivedMsgs[0].messageId, + testMessage.messageId, + "MessageId is different than expected" + ); - should.equal(errorWasThrown, true, "Error thrown flag must be true"); - } + await testPeekMsgsLength(receiverClient, 0); + + if (operation === DispositionType.complete) { + await contextToSettle!.complete(receivedMsgs[0]).catch((err) => testError(err, operation)); + } else if (operation === DispositionType.abandon) { + await contextToSettle!.abandon(receivedMsgs[0]).catch((err) => testError(err, operation)); + } else if (operation === DispositionType.deadletter) { + await contextToSettle! + .deadLetter(receivedMsgs[0]) + .catch((err) => testError(err, operation)); + } else if (operation === DispositionType.defer) { + await contextToSettle!.defer(receivedMsgs[0]).catch((err) => testError(err, operation)); + } - it("UnPartitioned Queue: complete() throws error #RunInBrowser", async function(): Promise { - await beforeEachTest(TestClientType.UnpartitionedQueue); - await testSettlement(DispositionType.complete); - }); + should.equal(errorWasThrown, true, "Error thrown flag must be true"); + } - it("UnPartitioned Subscription: complete() throws error", async function(): Promise { - await beforeEachTest(TestClientType.UnpartitionedSubscription); - await testSettlement(DispositionType.complete); - }); + it("UnPartitioned Queue: complete() throws error #RunInBrowser", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedQueue); + await testSettlement(DispositionType.complete); + }); - it("UnPartitioned Queue: abandon() throws error #RunInBrowser", async function(): Promise { - await beforeEachTest(TestClientType.UnpartitionedQueue); - await testSettlement(DispositionType.abandon); - }); + it("UnPartitioned Subscription: complete() throws error", async function(): Promise { + await beforeEachTest(TestClientType.UnpartitionedSubscription); + await testSettlement(DispositionType.complete); + }); - it("UnPartitioned Subscription: abandon() throws error", async function(): Promise { - await beforeEachTest(TestClientType.UnpartitionedSubscription); - await testSettlement(DispositionType.abandon); - }); + it("UnPartitioned Queue: abandon() throws error #RunInBrowser", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedQueue); + await testSettlement(DispositionType.abandon); + }); - it("UnPartitioned Queue: defer() throws error #RunInBrowser", async function(): Promise { - await beforeEachTest(TestClientType.UnpartitionedQueue); - await testSettlement(DispositionType.defer); - }); + it("UnPartitioned Subscription: abandon() throws error", async function(): Promise { + await beforeEachTest(TestClientType.UnpartitionedSubscription); + await testSettlement(DispositionType.abandon); + }); - it("UnPartitioned Subscription: defer() throws error", async function(): Promise { - await beforeEachTest(TestClientType.UnpartitionedSubscription); - await testSettlement(DispositionType.defer); - }); + it("UnPartitioned Queue: defer() throws error #RunInBrowser", async function(): Promise { + await beforeEachTest(TestClientType.UnpartitionedQueue); + await testSettlement(DispositionType.defer); + }); - it("UnPartitioned Queue: deadLetter() throws error #RunInBrowser", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedQueue); - await testSettlement(DispositionType.deadletter); - }); + it("UnPartitioned Subscription: defer() throws error", async function(): Promise { + await beforeEachTest(TestClientType.UnpartitionedSubscription); + await testSettlement(DispositionType.defer); + }); - it("UnPartitioned Subscription: deadLetter() throws error", async function(): Promise { - await beforeEachTest(TestClientType.UnpartitionedSubscription); - await testSettlement(DispositionType.deadletter); - }); -}); + it("UnPartitioned Queue: deadLetter() throws error #RunInBrowser", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedQueue); + await testSettlement(DispositionType.deadletter); + }); -describe("Streaming - User Error", function(): void { - afterEach(async () => { - await afterEachTest(); + it("UnPartitioned Subscription: deadLetter() throws error", async function(): Promise { + await beforeEachTest(TestClientType.UnpartitionedSubscription); + await testSettlement(DispositionType.deadletter); + }); }); - async function testUserError(): Promise { - await senderClient.send(TestMessage.getSample()); - const errorMessage = "Will we see this error message?"; - - const receivedMsgs: ReceivedMessage[] = []; - receiverClient.subscribe({ - async processMessage(msg: ReceivedMessage, context: ContextWithSettlement) { - await context.complete(msg); - receivedMsgs.push(msg); - throw new Error(errorMessage); - }, - processError + describe("Streaming - User Error", function(): void { + afterEach(async () => { + return serviceBusClient.test.afterEach(); }); - const msgsCheck = await checkWithTimeout(() => receivedMsgs.length === 1); - - should.equal(msgsCheck, true, `Expected 1, received ${receivedMsgs.length} messages.`); - should.equal( - !!(receiverClient as any)._receiver._context.streamingReceiver, - true, - "Expected streaming receiver not to be cached." - ); - - should.equal( - unexpectedError && unexpectedError.message, - errorMessage, - "User error did not surface." - ); - should.equal(receivedMsgs.length, 1, "Unexpected number of messages"); - } + async function testUserError(): Promise { + await senderClient.send(TestMessage.getSample()); + const errorMessage = "Will we see this error message?"; - it("UnPartitioned Queue: onError handler is called for user error #RunInBrowser", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedQueue); - await testUserError(); - }); -}); + const receivedMsgs: ReceivedMessage[] = []; + receiverClient.subscribe({ + async processMessage(msg: ReceivedMessage, context: ContextWithSettlement) { + await context.complete(msg); + receivedMsgs.push(msg); + throw new Error(errorMessage); + }, + processError + }); -describe("Streaming - Failed init should not cache receiver", function(): void { - afterEach(async () => { - await afterEachTest(); - }); + const msgsCheck = await checkWithTimeout(() => receivedMsgs.length === 1); - class TestTokenCredential extends EnvironmentCredential implements TokenCredential { - private firstCall = true; - static errorMessage = "This is a faulty token provider."; - constructor() { - super(); - } + should.equal(msgsCheck, true, `Expected 1, received ${receivedMsgs.length} messages.`); + should.equal( + !!(receiverClient as any)._context.streamingReceiver, + true, + "Expected streaming receiver not to be cached." + ); - async getToken(audience: string): Promise { - if (this.firstCall) { - this.firstCall = false; - throw new Error(TestTokenCredential.errorMessage); - } - return super.getToken(audience); + should.equal( + unexpectedError && unexpectedError.message, + errorMessage, + "User error did not surface." + ); + should.equal(receivedMsgs.length, 1, "Unexpected number of messages"); } - } - it("UnPartitioned Queue: Receiver is not cached when not initialized", async function(): Promise< - void - > { - const env: any = getEnvVars(); - - // Send a message using service bus client created with connection string - let clients = await getSenderReceiverClients(TestClientType.UnpartitionedQueue, "peekLock"); - senderClient = clients.senderClient; - receiverClient = clients.receiverClient; - await senderClient.send(TestMessage.getSample()); - await senderClient.close(); - await receiverClient.close(); - - // Receive using service bus client created with faulty token provider - const connectionObject: { - Endpoint: string; - SharedAccessKeyName: string; - SharedAccessKey: string; - } = parseConnectionString(env[EnvVarNames.SERVICEBUS_CONNECTION_STRING]); - const tokenProvider = new TestTokenCredential(); - receiverClient = new ServiceBusReceiverClient( - { - host: connectionObject.Endpoint.substr(5), - tokenCredential: tokenProvider, - queueName: EntityNames.QUEUE_NAME_NO_PARTITION - }, - "peekLock" - ); - - let actualError: Error; - receiverClient.subscribe({ - async processMessage(msg: ReceivedMessage) { - throw new Error("No messages should have been received with faulty token provider"); - }, - async processError(err) { - actualError = err; - } + it("UnPartitioned Queue: onError handler is called for user error #RunInBrowser", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedQueue); + await testUserError(); }); - - // Check for expected error and that receiver was not cached - const errCheck = await checkWithTimeout(() => !!actualError === true); - should.equal(errCheck, true, "Expected error to be thrown, but no error found."); - should.equal( - actualError!.message, - TestTokenCredential.errorMessage, - "Expected error from token provider, but unexpected error found." - ); - should.equal( - !!(clients.receiverClient as any)._context.streamingReceiver, - false, - "Expected Streaming receiver to not be cached" - ); }); -}); -describe("Streaming - maxConcurrentCalls", function(): void { - afterEach(async () => { - await afterEachTest(); - }); + describe("Streaming - Failed init should not cache receiver", function(): void { + afterEach(async () => { + return serviceBusClient.test.afterEach(); + }); - async function testConcurrency(maxConcurrentCalls?: number): Promise { - const testMessages = [TestMessage.getSample(), TestMessage.getSample()]; - await senderClient.sendBatch(testMessages); + // class TestTokenCredential extends EnvironmentCredential implements TokenCredential { + // private firstCall = true; + // static errorMessage = "This is a faulty token provider."; + // constructor() { + // super(); + // } + + // async getToken(audience: string): Promise { + // if (this.firstCall) { + // this.firstCall = false; + // throw new Error(TestTokenCredential.errorMessage); + // } + // return super.getToken(audience); + // } + // } + + // TODO: what is this test even actually testing? + // it("UnPartitioned Queue: Receiver is not cached when not initialized", async function(): Promise< + // void + // > { + // const env: any = getEnvVars(); + + // // Send a message using service bus client created with connection string + // let clients = await getSenderReceiverClients(TestClientType.UnpartitionedQueue, "peekLock"); + // senderClient = clients.senderClient; + // receiverClient = clients.receiverClient; + // await senderClient.send(TestMessage.getSample()); + // await senderClient.close(); + // await receiverClient.close(); + + // // Receive using service bus client created with faulty token provider + // const connectionObject: { + // Endpoint: string; + // SharedAccessKeyName: string; + // SharedAccessKey: string; + // } = parseConnectionString(env[EnvVarNames.SERVICEBUS_CONNECTION_STRING]); + // const tokenProvider = new TestTokenCredential(); + // receiverClient = new ServiceBusReceiverClient( + // { + // host: connectionObject.Endpoint.substr(5), + // tokenCredential: tokenProvider, + // queueName: EntityNames.QUEUE_NAME_NO_PARTITION + // }, + // "peekLock" + // ); + + // let actualError: Error; + // receiverClient.subscribe({ + // async processMessage(msg: ReceivedMessage) { + // throw new Error("No messages should have been received with faulty token provider"); + // }, + // async processError(err) { + // actualError = err; + // } + // }); + + // // Check for expected error and that receiver was not cached + // const errCheck = await checkWithTimeout(() => !!actualError === true); + // should.equal(errCheck, true, "Expected error to be thrown, but no error found."); + // should.equal( + // actualError!.message, + // TestTokenCredential.errorMessage, + // "Expected error from token provider, but unexpected error found." + // ); + // should.equal( + // !!(clients.receiverClient as any)._context.streamingReceiver, + // false, + // "Expected Streaming receiver to not be cached" + // ); + // }); + }); + + describe("Streaming - maxConcurrentCalls", function(): void { + afterEach(async () => { + return serviceBusClient.test.afterEach(); + }); - const settledMsgs: ReceivedMessage[] = []; - const receivedMsgs: ReceivedMessage[] = []; + async function testConcurrency(maxConcurrentCalls?: number): Promise { + const testMessages = [TestMessage.getSample(), TestMessage.getSample()]; + await senderClient.sendBatch(testMessages); - receiverClient.subscribe( - { - async processMessage(msg: ReceivedMessage, context: ContextWithSettlement) { - if (receivedMsgs.length === 1) { - if ((!maxConcurrentCalls || maxConcurrentCalls === 1) && settledMsgs.length === 0) { - throw new Error( - "onMessage for the second message should not have been called before the first message got settled" - ); - } - } else { - if (maxConcurrentCalls && maxConcurrentCalls > 1 && settledMsgs.length !== 0) { - throw new Error( - "onMessage for the second message should have been called before the first message got settled" - ); + const settledMsgs: ReceivedMessage[] = []; + const receivedMsgs: ReceivedMessage[] = []; + + receiverClient.subscribe( + { + async processMessage(msg: ReceivedMessage, context: ContextWithSettlement) { + if (receivedMsgs.length === 1) { + if ((!maxConcurrentCalls || maxConcurrentCalls === 1) && settledMsgs.length === 0) { + throw new Error( + "onMessage for the second message should not have been called before the first message got settled" + ); + } + } else { + if (maxConcurrentCalls && maxConcurrentCalls > 1 && settledMsgs.length !== 0) { + throw new Error( + "onMessage for the second message should have been called before the first message got settled" + ); + } } - } - receivedMsgs.push(msg); - await delay(2000); - await context.complete(msg); - settledMsgs.push(msg); + receivedMsgs.push(msg); + await delay(2000); + await context.complete(msg); + settledMsgs.push(msg); + }, + processError }, - processError - }, - maxConcurrentCalls ? { maxConcurrentCalls } : {} - ); + maxConcurrentCalls ? { maxConcurrentCalls } : {} + ); - await checkWithTimeout(() => settledMsgs.length === 2); - should.equal(unexpectedError, undefined, unexpectedError && unexpectedError.message); - should.equal(settledMsgs.length, 2, `Expected 2, received ${settledMsgs.length} messages.`); - } + await checkWithTimeout(() => settledMsgs.length === 2); + should.equal(unexpectedError, undefined, unexpectedError && unexpectedError.message); + should.equal(settledMsgs.length, 2, `Expected 2, received ${settledMsgs.length} messages.`); + } - it("Partitioned Queue: no maxConcurrentCalls passed", async function(): Promise { - await beforeEachTest(TestClientType.PartitionedQueue); - await testConcurrency(); - }); + it("Partitioned Queue: no maxConcurrentCalls passed", async function(): Promise { + await beforeEachTest(TestClientType.PartitionedQueue); + await testConcurrency(); + }); - it("Partitioned Queue: pass 1 for maxConcurrentCalls", async function(): Promise { - await beforeEachTest(TestClientType.PartitionedQueue); - await testConcurrency(1); - }); + it("Partitioned Queue: pass 1 for maxConcurrentCalls", async function(): Promise { + await beforeEachTest(TestClientType.PartitionedQueue); + await testConcurrency(1); + }); - it("Partitioned Queue: pass 2 for maxConcurrentCalls", async function(): Promise { - await beforeEachTest(TestClientType.PartitionedQueue); - await testConcurrency(2); - }); + it("Partitioned Queue: pass 2 for maxConcurrentCalls", async function(): Promise { + await beforeEachTest(TestClientType.PartitionedQueue); + await testConcurrency(2); + }); - it("Unpartitioned Queue: no maxConcurrentCalls passed #RunInBrowser", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedQueue); - await testConcurrency(); - }); + it("Unpartitioned Queue: no maxConcurrentCalls passed #RunInBrowser", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedQueue); + await testConcurrency(); + }); - it("Unpartitioned Queue: pass 1 for maxConcurrentCalls #RunInBrowser", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedQueue); - await testConcurrency(1); - }); + it("Unpartitioned Queue: pass 1 for maxConcurrentCalls #RunInBrowser", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedQueue); + await testConcurrency(1); + }); - it("Unpartitioned Queue: pass 2 for maxConcurrentCalls #RunInBrowser", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedQueue); - await testConcurrency(2); - }); + it("Unpartitioned Queue: pass 2 for maxConcurrentCalls #RunInBrowser", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedQueue); + await testConcurrency(2); + }); - it("Partitioned Subscription: no maxConcurrentCalls passed", async function(): Promise { - await beforeEachTest(TestClientType.PartitionedSubscription); - await testConcurrency(); - }); + it("Partitioned Subscription: no maxConcurrentCalls passed", async function(): Promise { + await beforeEachTest(TestClientType.PartitionedSubscription); + await testConcurrency(); + }); - it("Partitioned Queue: pass 1 for maxConcurrentCalls", async function(): Promise { - await beforeEachTest(TestClientType.PartitionedSubscription); - await testConcurrency(1); - }); + it("Partitioned Queue: pass 1 for maxConcurrentCalls", async function(): Promise { + await beforeEachTest(TestClientType.PartitionedSubscription); + await testConcurrency(1); + }); - it("Partitioned Queue: pass 2 for maxConcurrentCalls", async function(): Promise { - await beforeEachTest(TestClientType.PartitionedSubscription); - await testConcurrency(2); - }); + it("Partitioned Queue: pass 2 for maxConcurrentCalls", async function(): Promise { + await beforeEachTest(TestClientType.PartitionedSubscription); + await testConcurrency(2); + }); - it("Unpartitioned Subscription: no maxConcurrentCalls passed", async function(): Promise { - await beforeEachTest(TestClientType.UnpartitionedSubscription); - await testConcurrency(); - }); + it("Unpartitioned Subscription: no maxConcurrentCalls passed", async function(): Promise { + await beforeEachTest(TestClientType.UnpartitionedSubscription); + await testConcurrency(); + }); - it("Unpartitioned Subscription: pass 1 for maxConcurrentCalls", async function(): Promise { - await beforeEachTest(TestClientType.UnpartitionedSubscription); - await testConcurrency(1); - }); + it("Unpartitioned Subscription: pass 1 for maxConcurrentCalls", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedSubscription); + await testConcurrency(1); + }); - it("Unpartitioned Subscription: pass 2 for maxConcurrentCalls", async function(): Promise { - await beforeEachTest(TestClientType.UnpartitionedSubscription); - await testConcurrency(2); + it("Unpartitioned Subscription: pass 2 for maxConcurrentCalls", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedSubscription); + await testConcurrency(2); + }); }); + // #RevisitCommentedTestsAfterTheSingleClientAPI + // Receiver can't be closed separately with the current API + // Roll this back once the top level client is implemented + // describe("Streaming - Not receive messages after receiver is closed", function(): void { + // afterEach(async () => { + // await afterEachTest(); + // }); + + // async function testReceiveMessages(): Promise { + // const totalNumOfMessages = 5; + // let num = 1; + // const messages = []; + // while (num <= totalNumOfMessages) { + // const message = { + // messageId: num, + // body: "test", + // label: `${num}`, + // partitionKey: "dummy" // Ensures all messages go to same parition to make peek work reliably + // }; + // num++; + // messages.push(message); + // } + // await senderClient.sendBatch(messages); + + // const receivedMsgs: ReceivedMessage[] = []; + + // receiverClient.subscribe( + // { + // async processMessage(brokeredMessage: ReceivedMessage, context: ContextWithSettlement) { + // receivedMsgs.push(brokeredMessage); + // await context.complete(brokeredMessage); + // }, + // processError + // }, + // { + // autoComplete: false + // } + // ); + + // await delay(5000); + // should.equal( + // receivedMsgs.length, + // 0, + // `Expected 0 messages, but received ${receivedMsgs.length}` + // ); + // await testPeekMsgsLength(receiverClient, totalNumOfMessages); + // } + + // it("UnPartitioned Queue: Not receive messages after receiver is closed #RunInBrowser", async function(): Promise< + // void + // > { + // await beforeEachTest(TestClientType.UnpartitionedQueue); + // await testReceiveMessages(); + // }); + + // it("UnPartitioned Queue: (Receive And Delete mode) Not receive messages after receiver is closed #RunInBrowser", async function(): Promise< + // void + // > { + // await beforeEachTest(TestClientType.UnpartitionedQueue, "receiveAndDelete"); + // await testReceiveMessages(); + // }); + // }); }); -// #RevisitCommentedTestsAfterTheSingleClientAPI -// Receiver can't be closed separately with the current API -// Roll this back once the top level client is implemented -// describe("Streaming - Not receive messages after receiver is closed", function(): void { -// afterEach(async () => { -// await afterEachTest(); -// }); - -// async function testReceiveMessages(): Promise { -// const totalNumOfMessages = 5; -// let num = 1; -// const messages = []; -// while (num <= totalNumOfMessages) { -// const message = { -// messageId: num, -// body: "test", -// label: `${num}`, -// partitionKey: "dummy" // Ensures all messages go to same parition to make peek work reliably -// }; -// num++; -// messages.push(message); -// } -// await senderClient.sendBatch(messages); - -// const receivedMsgs: ReceivedMessage[] = []; - -// receiverClient.subscribe( -// { -// async processMessage(brokeredMessage: ReceivedMessage, context: ContextWithSettlement) { -// receivedMsgs.push(brokeredMessage); -// await context.complete(brokeredMessage); -// }, -// processError -// }, -// { -// autoComplete: false -// } -// ); - -// await delay(5000); -// should.equal( -// receivedMsgs.length, -// 0, -// `Expected 0 messages, but received ${receivedMsgs.length}` -// ); -// await testPeekMsgsLength(receiverClient, totalNumOfMessages); -// } - -// it("UnPartitioned Queue: Not receive messages after receiver is closed #RunInBrowser", async function(): Promise< -// void -// > { -// await beforeEachTest(TestClientType.UnpartitionedQueue); -// await testReceiveMessages(); -// }); - -// it("UnPartitioned Queue: (Receive And Delete mode) Not receive messages after receiver is closed #RunInBrowser", async function(): Promise< -// void -// > { -// await beforeEachTest(TestClientType.UnpartitionedQueue, "receiveAndDelete"); -// await testReceiveMessages(); -// }); -// }); diff --git a/sdk/servicebus/service-bus/test/streamingReceiverSessions.spec.ts b/sdk/servicebus/service-bus/test/streamingReceiverSessions.spec.ts index afeaf1566c39..36a814d097b3 100644 --- a/sdk/servicebus/service-bus/test/streamingReceiverSessions.spec.ts +++ b/sdk/servicebus/service-bus/test/streamingReceiverSessions.spec.ts @@ -3,158 +3,93 @@ import chai from "chai"; import chaiAsPromised from "chai-as-promised"; -import { delay, ReceivedMessage, ServiceBusSenderClient, ContextWithSettlement } from "../src"; +import { delay, ReceivedMessage, ContextWithSettlement } from "../src"; import { getAlreadyReceivingErrorMsg } from "../src/util/errors"; -import { - checkWithTimeout, - TestClientType, - getSenderReceiverClients, - purge, - TestMessage, - ReceiverClientTypeForUser, - ReceiverClientTypeForUserT -} from "./utils/testUtils"; -import { ServiceBusReceiverClient } from "../src/serviceBusReceiverClient"; -import { getEnvVars } from "./utils/envVarUtils"; +import { checkWithTimeout, TestClientType, TestMessage } from "./utils/testUtils"; import { DispositionType } from "../src/serviceBusMessage"; +import { SessionReceiver } from "../src/receivers/sessionReceiver"; +import { Receiver } from "../src/receivers/receiver"; +import { Sender } from "../src/sender"; +import { + createServiceBusClientForTests, + ServiceBusClientForTests, + testPeekMsgsLength +} from "./utils/testutils2"; const should = chai.should(); chai.use(chaiAsPromised); -async function testPeekMsgsLength( - client: ReceiverClientTypeForUser, - expectedPeekLength: number -): Promise { - const peekedMsgs = await client.diagnostics.peek(expectedPeekLength + 1); - should.equal( - peekedMsgs.length, - expectedPeekLength, - "Unexpected number of msgs found when peeking" - ); -} - -let senderClient: ServiceBusSenderClient; -let receiverClient: ReceiverClientTypeForUserT<"peekLock">; -let deadLetterClient: ReceiverClientTypeForUserT<"peekLock">; -let errorWasThrown: boolean; -let unexpectedError: Error | undefined; - -async function processError(err: Error): Promise { - if (err) { - unexpectedError = err; - } -} - -async function beforeEachTest( - testClientType: TestClientType, - receiveMode?: "peekLock" | "receiveAndDelete" -): Promise { - if (!receiveMode) { - receiveMode = "peekLock"; - } - let clients = await getSenderReceiverClients(testClientType, receiveMode, undefined, { - id: TestMessage.sessionId - }); - senderClient = clients.senderClient; - receiverClient = clients.receiverClient; - - deadLetterClient = new ServiceBusReceiverClient( - { - connectionString: getEnvVars().SERVICEBUS_CONNECTION_STRING, - queueName: receiverClient.getDeadLetterPath() - }, - "peekLock" - ); - - await purge(receiverClient); - await purge(deadLetterClient); - const peekedMsgs = await receiverClient.diagnostics.peek(); - const receiverEntityType = receiverClient.entityType; - if (peekedMsgs.length) { - chai.assert.fail(`Please use an empty ${receiverEntityType} for integration testing`); - } - - errorWasThrown = false; - unexpectedError = undefined; -} +describe("Streaming with sessions", () => { + let senderClient: Sender; + let receiverClient: SessionReceiver; + let deadLetterClient: Receiver; + let errorWasThrown: boolean; + let unexpectedError: Error | undefined; + let serviceBusClient: ServiceBusClientForTests; -async function afterEachTest(): Promise { - await senderClient.close(); - await receiverClient.close(); -} + before(() => { + serviceBusClient = createServiceBusClientForTests(); + }); -describe("Sessions Streaming - Misc Tests", function(): void { - afterEach(async () => { - await afterEachTest(); + after(async () => { + await serviceBusClient.test.after(); }); - async function testAutoComplete(): Promise { - const testMessage = TestMessage.getSessionSample(); - await senderClient.send(testMessage); + async function processError(err: Error): Promise { + if (err) { + unexpectedError = err; + } + } - const receivedMsgs: ReceivedMessage[] = []; - receiverClient.subscribe({ - async processMessage(msg: ReceivedMessage, context: ContextWithSettlement) { - receivedMsgs.push(msg); - should.equal(msg.body, testMessage.body, "MessageBody is different than expected"); - should.equal(msg.messageId, testMessage.messageId, "MessageId is different than expected"); - return Promise.resolve(); - ``; - }, - processError - }); + async function afterEachTest(): Promise { + await serviceBusClient.test.afterEach(); + } + async function beforeEachTest(testClientType: TestClientType): Promise { + const entityNames = await createReceiverForTests(testClientType); - const msgsCheck = await checkWithTimeout( - () => receivedMsgs.length === 1 && receivedMsgs[0].delivery.remote_settled === true + senderClient = serviceBusClient.test.addToCleanup( + serviceBusClient.getSender(entityNames.queue ?? entityNames.topic!) ); - should.equal( - msgsCheck, - true, - receivedMsgs.length !== 1 - ? `Expected 1, received ${receivedMsgs.length} messages` - : "Message didnt get auto-completed in time" + + deadLetterClient = serviceBusClient.test.addToCleanup( + serviceBusClient.getReceiver(receiverClient.getDeadLetterPath(), "peekLock") ); - should.equal(unexpectedError, undefined, unexpectedError && unexpectedError.message); - should.equal(receivedMsgs.length, 1, "Unexpected number of messages"); - await testPeekMsgsLength(receiverClient, 0); - } - it("Partitioned Queue: AutoComplete removes the message(with sessions)", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedQueueWithSessions); - await testAutoComplete(); - }); + errorWasThrown = false; + unexpectedError = undefined; + } - it("Partitioned Subscription: AutoComplete removes the message(with sessions)", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); - await testAutoComplete(); - }); + async function createReceiverForTests(testClientType: TestClientType) { + const entityNames = await serviceBusClient.test.createTestEntities(testClientType); + receiverClient = serviceBusClient.test.addToCleanup( + entityNames.queue + ? serviceBusClient.getSessionReceiver(entityNames.queue, "peekLock", { + sessionId: TestMessage.sessionId + }) + : serviceBusClient.getSessionReceiver( + entityNames.topic!, + entityNames.subscription!, + "peekLock", + { + // TODO: we should just be able to randomly generate this. Change _soon_. + sessionId: TestMessage.sessionId + } + ) + ); + return entityNames; + } - it("UnPartitioned Queue: AutoComplete removes the message(with sessions) #RunInBrowser", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); - await testAutoComplete(); - }); + describe("Sessions Streaming - Misc Tests", function(): void { + afterEach(async () => { + await afterEachTest(); + }); - it("UnPartitioned Subscription: AutoComplete removes the message(with sessions)", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedSubscriptionWithSessions); - await testAutoComplete(); - }); + async function testAutoComplete(): Promise { + const testMessage = TestMessage.getSessionSample(); + await senderClient.send(testMessage); - async function testManualComplete(): Promise { - const testMessage = TestMessage.getSessionSample(); - await senderClient.send(testMessage); - let contextToSettle: ContextWithSettlement; - const receivedMsgs: ReceivedMessage[] = []; - receiverClient.subscribe( - { + const receivedMsgs: ReceivedMessage[] = []; + receiverClient.subscribe({ async processMessage(msg: ReceivedMessage, context: ContextWithSettlement) { - contextToSettle = context; receivedMsgs.push(msg); should.equal(msg.body, testMessage.body, "MessageBody is different than expected"); should.equal( @@ -163,876 +98,942 @@ describe("Sessions Streaming - Misc Tests", function(): void { "MessageId is different than expected" ); return Promise.resolve(); + ``; }, processError - }, - { autoComplete: false } - ); + }); - const msgsCheck = await checkWithTimeout(() => receivedMsgs.length === 1); - should.equal(msgsCheck, true, `Expected 1, received ${receivedMsgs.length} messages`); + const msgsCheck = await checkWithTimeout( + () => receivedMsgs.length === 1 && receivedMsgs[0].delivery.remote_settled === true + ); + should.equal( + msgsCheck, + true, + receivedMsgs.length !== 1 + ? `Expected 1, received ${receivedMsgs.length} messages` + : "Message didnt get auto-completed in time" + ); + should.equal(unexpectedError, undefined, unexpectedError && unexpectedError.message); + should.equal(receivedMsgs.length, 1, "Unexpected number of messages"); + await testPeekMsgsLength(receiverClient, 0); + } - await testPeekMsgsLength(receiverClient, 1); + it("Partitioned Queue: AutoComplete removes the message(with sessions)", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedQueueWithSessions); + await testAutoComplete(); + }); - await contextToSettle!.complete(receivedMsgs[0]); + it("Partitioned Subscription: AutoComplete removes the message(with sessions)", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); + await testAutoComplete(); + }); - should.equal(unexpectedError, undefined, unexpectedError && unexpectedError.message); - should.equal(receivedMsgs.length, 1, "Unexpected number of messages"); - await testPeekMsgsLength(receiverClient, 0); - } + it("UnPartitioned Queue: AutoComplete removes the message(with sessions) #RunInBrowser", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); + await testAutoComplete(); + }); - it("Partitioned Queue: Disabled autoComplete, no manual complete retains the message(with sessions)", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedQueueWithSessions); - await testManualComplete(); - }); + it("UnPartitioned Subscription: AutoComplete removes the message(with sessions)", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedSubscriptionWithSessions); + await testAutoComplete(); + }); - it("Partitioned Subscription: Disabled autoComplete, no manual complete retains the message(with sessions)", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); - await testManualComplete(); - }); + async function testManualComplete(): Promise { + const testMessage = TestMessage.getSessionSample(); + await senderClient.send(testMessage); + let contextToSettle: ContextWithSettlement; + const receivedMsgs: ReceivedMessage[] = []; + receiverClient.subscribe( + { + async processMessage(msg: ReceivedMessage, context: ContextWithSettlement) { + contextToSettle = context; + receivedMsgs.push(msg); + should.equal(msg.body, testMessage.body, "MessageBody is different than expected"); + should.equal( + msg.messageId, + testMessage.messageId, + "MessageId is different than expected" + ); + return Promise.resolve(); + }, + processError + }, + { autoComplete: false } + ); - it("UnPartitioned Queue: Disabled autoComplete, no manual complete retains the message(with sessions) #RunInBrowser", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); - await testManualComplete(); - }); + const msgsCheck = await checkWithTimeout(() => receivedMsgs.length === 1); + should.equal(msgsCheck, true, `Expected 1, received ${receivedMsgs.length} messages`); - it("UnPartitioned Subscription: Disabled autoComplete, no manual complete retains the message(with sessions)", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedSubscriptionWithSessions); - await testManualComplete(); - }); -}); + await testPeekMsgsLength(receiverClient, 1); + + await contextToSettle!.complete(receivedMsgs[0]); -describe("Sessions Streaming - Complete message", function(): void { - afterEach(async () => { - await afterEachTest(); + should.equal(unexpectedError, undefined, unexpectedError && unexpectedError.message); + should.equal(receivedMsgs.length, 1, "Unexpected number of messages"); + await testPeekMsgsLength(receiverClient, 0); + } + + it("Partitioned Queue: Disabled autoComplete, no manual complete retains the message(with sessions)", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedQueueWithSessions); + await testManualComplete(); + }); + + it("Partitioned Subscription: Disabled autoComplete, no manual complete retains the message(with sessions)", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); + await testManualComplete(); + }); + + it("UnPartitioned Queue: Disabled autoComplete, no manual complete retains the message(with sessions) #RunInBrowser", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); + await testManualComplete(); + }); + + it("UnPartitioned Subscription: Disabled autoComplete, no manual complete retains the message(with sessions)", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedSubscriptionWithSessions); + await testManualComplete(); + }); }); - async function testComplete(autoComplete: boolean): Promise { - const testMessage = TestMessage.getSessionSample(); - await senderClient.send(testMessage); + describe("Sessions Streaming - Complete message", function(): void { + afterEach(async () => { + await afterEachTest(); + }); - const receivedMsgs: ReceivedMessage[] = []; - receiverClient.subscribe( - { - async processMessage(msg: ReceivedMessage, context: ContextWithSettlement) { - should.equal(msg.body, testMessage.body, "MessageBody is different than expected"); - should.equal( - msg.messageId, - testMessage.messageId, - "MessageId is different than expected" - ); - await context.complete(msg); - receivedMsgs.push(msg); + async function testComplete(autoComplete: boolean): Promise { + const testMessage = TestMessage.getSessionSample(); + await senderClient.send(testMessage); + + const receivedMsgs: ReceivedMessage[] = []; + receiverClient.subscribe( + { + async processMessage(msg: ReceivedMessage, context: ContextWithSettlement) { + should.equal(msg.body, testMessage.body, "MessageBody is different than expected"); + should.equal( + msg.messageId, + testMessage.messageId, + "MessageId is different than expected" + ); + await context.complete(msg); + receivedMsgs.push(msg); + }, + processError }, - processError - }, - { autoComplete } - ); + { autoComplete } + ); - const msgsCheck = await checkWithTimeout(() => receivedMsgs.length === 1); - should.equal(msgsCheck, true, `Expected 1, received ${receivedMsgs.length} messages`); + const msgsCheck = await checkWithTimeout(() => receivedMsgs.length === 1); + should.equal(msgsCheck, true, `Expected 1, received ${receivedMsgs.length} messages`); - should.equal(unexpectedError, undefined, unexpectedError && unexpectedError.message); - should.equal(receivedMsgs.length, 1, "Unexpected number of messages"); + should.equal(unexpectedError, undefined, unexpectedError && unexpectedError.message); + should.equal(receivedMsgs.length, 1, "Unexpected number of messages"); - await testPeekMsgsLength(receiverClient, 0); - } - it("Partitioned Queue: complete() removes message(with sessions)", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedQueueWithSessions); - await testComplete(false); - }); + await testPeekMsgsLength(receiverClient, 0); + } + it("Partitioned Queue: complete() removes message(with sessions)", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedQueueWithSessions); + await testComplete(false); + }); - it("Partitioned Subscription: complete() removes message(with sessions)", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); - await testComplete(false); - }); + it("Partitioned Subscription: complete() removes message(with sessions)", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); + await testComplete(false); + }); - it("UnPartitioned Queue: complete() removes message(with sessions)", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); - await testComplete(false); - }); + it("UnPartitioned Queue: complete() removes message(with sessions)", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); + await testComplete(false); + }); - it("UnPartitioned Subscription: complete() removes message(with sessions)", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedSubscriptionWithSessions); - await testComplete(false); - }); + it("UnPartitioned Subscription: complete() removes message(with sessions)", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedSubscriptionWithSessions); + await testComplete(false); + }); - it("Partitioned Queue with autoComplete: complete() removes message(with sessions)", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedQueueWithSessions); - await testComplete(true); - }); + it("Partitioned Queue with autoComplete: complete() removes message(with sessions)", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedQueueWithSessions); + await testComplete(true); + }); - it("Partitioned Subscription with autoComplete: complete() removes message(with sessions)", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); - await testComplete(true); - }); + it("Partitioned Subscription with autoComplete: complete() removes message(with sessions)", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); + await testComplete(true); + }); - it("UnPartitioned Queue with autoComplete: complete() removes message(with sessions)", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); - await testComplete(true); - }); + it("UnPartitioned Queue with autoComplete: complete() removes message(with sessions)", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); + await testComplete(true); + }); - it("UnPartitioned Subscription with autoComplete: complete() removes message(with sessions)", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedSubscriptionWithSessions); - await testComplete(true); + it("UnPartitioned Subscription with autoComplete: complete() removes message(with sessions)", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedSubscriptionWithSessions); + await testComplete(true); + }); }); -}); -describe("Sessions Streaming - Abandon message", function(): void { - afterEach(async () => { - await afterEachTest(); - }); - async function eachTest( - testClientType: TestClientType, - autoComplete: boolean, - receiveMode?: "peekLock" | "receiveAndDelete" | undefined - ) { - await beforeEachTest(testClientType, receiveMode); - await testAbandon(testClientType, autoComplete); - } - async function testAbandon(testClientType: TestClientType, autoComplete: boolean): Promise { - const testMessage = TestMessage.getSessionSample(); - await senderClient.send(testMessage); - let abandonFlag = 0; - receiverClient.subscribe( - { - async processMessage(msg: ReceivedMessage, context: ContextWithSettlement) { - return context.abandon(msg).then(() => { - abandonFlag = 1; - if (receiverClient.isReceivingMessages()) { - return receiverClient.close(); - } - return Promise.resolve(); - }); + // TODO: TEMPORARY + describe("Sessions Streaming - Abandon message", function(): void { + afterEach(async () => { + await afterEachTest(); + }); + async function eachTest(testClientType: TestClientType, autoComplete: boolean) { + await beforeEachTest(testClientType); + await testAbandon(testClientType, autoComplete); + } + async function testAbandon( + testClientType: TestClientType, + autoComplete: boolean + ): Promise { + const testMessage = TestMessage.getSessionSample(); + await senderClient.send(testMessage); + let abandonFlag = 0; + + receiverClient.subscribe( + { + async processMessage(msg: ReceivedMessage, context: ContextWithSettlement) { + return context.abandon(msg).then(() => { + abandonFlag = 1; + if (receiverClient.isReceivingMessages()) { + return receiverClient.close(); + } + return Promise.resolve(); + }); + }, + processError }, - processError - }, - { autoComplete } - ); + { autoComplete } + ); - const msgAbandonCheck = await checkWithTimeout(() => abandonFlag === 1); - should.equal(msgAbandonCheck, true, "Abandoning the message results in a failure"); + const msgAbandonCheck = await checkWithTimeout(() => abandonFlag === 1); + should.equal(msgAbandonCheck, true, "Abandoning the message results in a failure"); - if (receiverClient.isReceivingMessages()) { - await receiverClient.close(); - } + if (receiverClient.isReceivingMessages()) { + await receiverClient.close(); + } - should.equal(unexpectedError, undefined, unexpectedError && unexpectedError.message); - receiverClient = ( - await getSenderReceiverClients( - testClientType, - "peekLock", - undefined, - { id: TestMessage.sessionId }, - false - ) - ).receiverClient; - - const batch = await receiverClient.receiveBatch(1); - const receivedMsgs = batch.messages; - should.equal(receivedMsgs.length, 1, "Unexpected number of messages"); - should.equal( - receivedMsgs[0].messageId, - testMessage.messageId, - "MessageId is different than expected" - ); - should.equal(receivedMsgs[0].deliveryCount, 1, "DeliveryCount is different than expected"); - await batch.context.complete(receivedMsgs[0]); - await testPeekMsgsLength(receiverClient, 0); - } - it("Partitioned Queue: abandon() retains message with incremented deliveryCount(with sessions)", async function(): Promise< - void - > { - await eachTest(TestClientType.PartitionedQueueWithSessions, false); - }); + should.equal(unexpectedError, undefined, unexpectedError && unexpectedError.message); - it("Partitioned Subscription: abandon() retains message with incremented deliveryCount(with sessions)", async function(): Promise< - void - > { - await eachTest(TestClientType.PartitionedSubscriptionWithSessions, false); - }); + await createReceiverForTests(testClientType); - it("UnPartitioned Queue: abandon() retains message with incremented deliveryCount(with sessions)", async function(): Promise< - void - > { - await eachTest(TestClientType.UnpartitionedQueueWithSessions, false); - }); + const batch = await receiverClient.receiveBatch(1); + const receivedMsgs = batch.messages; + should.equal(receivedMsgs.length, 1, "Unexpected number of messages"); + should.equal( + receivedMsgs[0].messageId, + testMessage.messageId, + "MessageId is different than expected" + ); + should.equal(receivedMsgs[0].deliveryCount, 1, "DeliveryCount is different than expected"); + await batch.context.complete(receivedMsgs[0]); + await testPeekMsgsLength(receiverClient, 0); + } + it("Partitioned Queue: abandon() retains message with incremented deliveryCount(with sessions)", async function(): Promise< + void + > { + await eachTest(TestClientType.PartitionedQueueWithSessions, false); + }); - it("UnPartitioned Subscription: abandon() retains message with incremented deliveryCount(with sessions)", async function(): Promise< - void - > { - await eachTest(TestClientType.UnpartitionedSubscriptionWithSessions, false); - }); + it("Partitioned Subscription: abandon() retains message with incremented deliveryCount(with sessions)", async function(): Promise< + void + > { + await eachTest(TestClientType.PartitionedSubscriptionWithSessions, false); + }); - it("Partitioned Queue with autoComplete: abandon() retains message with incremented deliveryCount(with sessions)", async function(): Promise< - void - > { - await eachTest(TestClientType.PartitionedQueueWithSessions, true); - }); + it("UnPartitioned Queue: abandon() retains message with incremented deliveryCount(with sessions)", async function(): Promise< + void + > { + await eachTest(TestClientType.UnpartitionedQueueWithSessions, false); + }); - it("Partitioned Subscription with autoComplete: abandon() retains message with incremented deliveryCount(with sessions)", async function(): Promise< - void - > { - await eachTest(TestClientType.PartitionedSubscriptionWithSessions, true); - }); + it("UnPartitioned Subscription: abandon() retains message with incremented deliveryCount(with sessions)", async function(): Promise< + void + > { + await eachTest(TestClientType.UnpartitionedSubscriptionWithSessions, false); + }); - it("UnPartitioned Queue with autoComplete: abandon() retains message with incremented deliveryCount(with sessions)", async function(): Promise< - void - > { - await eachTest(TestClientType.UnpartitionedQueueWithSessions, true); - }); + it("Partitioned Queue with autoComplete: abandon() retains message with incremented deliveryCount(with sessions)", async function(): Promise< + void + > { + await eachTest(TestClientType.PartitionedQueueWithSessions, true); + }); - it("UnPartitioned Subscription with autoComplete: abandon() retains message with incremented deliveryCount(with sessions)", async function(): Promise< - void - > { - await eachTest(TestClientType.UnpartitionedSubscriptionWithSessions, true); - }); -}); + it("Partitioned Subscription with autoComplete: abandon() retains message with incremented deliveryCount(with sessions)", async function(): Promise< + void + > { + await eachTest(TestClientType.PartitionedSubscriptionWithSessions, true); + }); + + it("UnPartitioned Queue with autoComplete: abandon() retains message with incremented deliveryCount(with sessions)", async function(): Promise< + void + > { + await eachTest(TestClientType.UnpartitionedQueueWithSessions, true); + }); -describe("Sessions Streaming - Defer message", function(): void { - afterEach(async () => { - await afterEachTest(); + it("UnPartitioned Subscription with autoComplete: abandon() retains message with incremented deliveryCount(with sessions)", async function(): Promise< + void + > { + await eachTest(TestClientType.UnpartitionedSubscriptionWithSessions, true); + }); }); - async function testDefer(autoComplete: boolean): Promise { - const testMessage = TestMessage.getSessionSample(); - await senderClient.send(testMessage); + describe("Sessions Streaming - Defer message", function(): void { + afterEach(async () => { + await afterEachTest(); + }); - let sequenceNum: any = 0; - receiverClient.subscribe( - { - async processMessage(msg: ReceivedMessage, context: ContextWithSettlement) { - await context.defer(msg); - sequenceNum = msg.sequenceNumber; + async function testDefer(autoComplete: boolean): Promise { + const testMessage = TestMessage.getSessionSample(); + await senderClient.send(testMessage); + + let sequenceNum: any = 0; + receiverClient.subscribe( + { + async processMessage(msg: ReceivedMessage, context: ContextWithSettlement) { + await context.defer(msg); + sequenceNum = msg.sequenceNumber; + }, + processError }, - processError - }, - { autoComplete } - ); - - const sequenceNumCheck = await checkWithTimeout(() => sequenceNum !== 0); - should.equal( - sequenceNumCheck, - true, - "Either the message is not received or observed an unexpected SequenceNumber." - ); + { autoComplete } + ); - should.equal(unexpectedError, undefined, unexpectedError && unexpectedError.message); + const sequenceNumCheck = await checkWithTimeout(() => sequenceNum !== 0); + should.equal( + sequenceNumCheck, + true, + "Either the message is not received or observed an unexpected SequenceNumber." + ); - const deferredMsg = await receiverClient.receiveDeferredMessage(sequenceNum); - if (!deferredMsg) { - throw "No message received for sequence number"; - } + should.equal(unexpectedError, undefined, unexpectedError && unexpectedError.message); - should.equal(deferredMsg.body, testMessage.body, "MessageBody is different than expected"); - should.equal( - deferredMsg.messageId, - testMessage.messageId, - "MessageId is different than expected" - ); - should.equal(deferredMsg.deliveryCount, 1, "DeliveryCount is different than expected"); + const deferredMsg = await receiverClient.receiveDeferredMessage(sequenceNum); + if (!deferredMsg) { + throw "No message received for sequence number"; + } - await deferredMsg.complete(); - await testPeekMsgsLength(receiverClient, 0); - } - it("Partitioned Queue: defer() moves message to deferred queue(with sessions)", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedQueueWithSessions); - await testDefer(false); - }); + should.equal(deferredMsg.body, testMessage.body, "MessageBody is different than expected"); + should.equal( + deferredMsg.messageId, + testMessage.messageId, + "MessageId is different than expected" + ); + should.equal(deferredMsg.deliveryCount, 1, "DeliveryCount is different than expected"); - it("Partitioned Subscription: defer() moves message to deferred queue(with sessions)", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); - await testDefer(false); - }); + await deferredMsg.complete(); + await testPeekMsgsLength(receiverClient, 0); + } + it("Partitioned Queue: defer() moves message to deferred queue(with sessions)", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedQueueWithSessions); + await testDefer(false); + }); - it("UnPartitioned Queue: defer() moves message to deferred queue(with sessions)", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); - await testDefer(false); - }); + it("Partitioned Subscription: defer() moves message to deferred queue(with sessions)", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); + await testDefer(false); + }); - it("UnPartitioned Subscription: defer() moves message to deferred queue(with sessions)", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedSubscriptionWithSessions); - await testDefer(false); - }); + it("UnPartitioned Queue: defer() moves message to deferred queue(with sessions)", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); + await testDefer(false); + }); - it("Partitioned Queue with autoComplete: defer() moves message to deferred queue(with sessions)", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedQueueWithSessions); - await testDefer(true); - }); + it("UnPartitioned Subscription: defer() moves message to deferred queue(with sessions)", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedSubscriptionWithSessions); + await testDefer(false); + }); - it("Partitioned Subscription with autoComplete: defer() moves message to deferred queue(with sessions)", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); - await testDefer(true); - }); + it("Partitioned Queue with autoComplete: defer() moves message to deferred queue(with sessions)", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedQueueWithSessions); + await testDefer(true); + }); - it("UnPartitioned Queue with autoComplete: defer() moves message to deferred queue(with sessions)", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); - await testDefer(true); - }); + it("Partitioned Subscription with autoComplete: defer() moves message to deferred queue(with sessions)", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); + await testDefer(true); + }); - it("UnPartitioned Subscription with autoComplete: defer() moves message to deferred queue(with sessions)", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedSubscriptionWithSessions); - await testDefer(true); - }); -}); + it("UnPartitioned Queue with autoComplete: defer() moves message to deferred queue(with sessions)", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); + await testDefer(true); + }); -describe("Sessions Streaming - Deadletter message", function(): void { - afterEach(async () => { - await afterEachTest(); + it("UnPartitioned Subscription with autoComplete: defer() moves message to deferred queue(with sessions)", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedSubscriptionWithSessions); + await testDefer(true); + }); }); - async function testDeadletter(autoComplete: boolean): Promise { - const testMessage = TestMessage.getSessionSample(); - await senderClient.send(testMessage); + describe("Sessions Streaming - Deadletter message", function(): void { + afterEach(async () => { + await afterEachTest(); + }); - let msgCount = 0; - receiverClient.subscribe( - { - async processMessage(msg: ReceivedMessage, context: ContextWithSettlement) { - await msg.deadLetter(); - msgCount++; + async function testDeadletter(autoComplete: boolean): Promise { + const testMessage = TestMessage.getSessionSample(); + await senderClient.send(testMessage); + + let msgCount = 0; + receiverClient.subscribe( + { + async processMessage(msg: ReceivedMessage, context: ContextWithSettlement) { + await msg.deadLetter(); + msgCount++; + }, + processError }, - processError - }, - { autoComplete } - ); + { autoComplete } + ); - const msgsCheck = await checkWithTimeout(() => msgCount === 1); - should.equal(msgsCheck, true, `Expected 1, received ${msgCount} messages`); - - should.equal(unexpectedError, undefined, unexpectedError && unexpectedError.message); - should.equal(msgCount, 1, "Unexpected number of messages"); - await testPeekMsgsLength(receiverClient, 0); - - const batch = await deadLetterClient.receiveBatch(1); - const deadLetterMsgs = batch.messages; - should.equal(Array.isArray(deadLetterMsgs), true, "`ReceivedMessages` is not an array"); - should.equal(deadLetterMsgs.length, 1, "Unexpected number of messages"); - should.equal( - deadLetterMsgs[0].messageId, - testMessage.messageId, - "MessageId is different than expected" - ); + const msgsCheck = await checkWithTimeout(() => msgCount === 1); + should.equal(msgsCheck, true, `Expected 1, received ${msgCount} messages`); + + should.equal(unexpectedError, undefined, unexpectedError && unexpectedError.message); + should.equal(msgCount, 1, "Unexpected number of messages"); + await testPeekMsgsLength(receiverClient, 0); + + const batch = await deadLetterClient.receiveBatch(1); + const deadLetterMsgs = batch.messages; + should.equal(Array.isArray(deadLetterMsgs), true, "`ReceivedMessages` is not an array"); + should.equal(deadLetterMsgs.length, 1, "Unexpected number of messages"); + should.equal( + deadLetterMsgs[0].messageId, + testMessage.messageId, + "MessageId is different than expected" + ); - await batch.context.complete(deadLetterMsgs[0]); - await testPeekMsgsLength(deadLetterClient, 0); - } + await batch.context.complete(deadLetterMsgs[0]); + await testPeekMsgsLength(deadLetterClient, 0); + } - it("Partitioned Queue: deadLetter() moves message to deadletter queue(with sessions)", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedQueueWithSessions); - await testDeadletter(false); - }); + it("Partitioned Queue: deadLetter() moves message to deadletter queue(with sessions)", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedQueueWithSessions); + await testDeadletter(false); + }); - it("Partitioned Subscription: deadLetter() moves message to deadletter queue(with sessions)", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); - await testDeadletter(false); - }); + it("Partitioned Subscription: deadLetter() moves message to deadletter queue(with sessions)", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); + await testDeadletter(false); + }); - it("UnPartitioned Queue: deadLetter() moves message to deadletter queue(with sessions)", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); - await testDeadletter(false); - }); + it("UnPartitioned Queue: deadLetter() moves message to deadletter queue(with sessions)", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); + await testDeadletter(false); + }); - it("UnPartitioned Subscription: deadLetter() moves message to deadletter queue(with sessions)", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedSubscriptionWithSessions); - await testDeadletter(false); - }); + it("UnPartitioned Subscription: deadLetter() moves message to deadletter queue(with sessions)", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedSubscriptionWithSessions); + await testDeadletter(false); + }); - it("Partitioned Queue with autoComplete: deadLetter() moves message to deadletter queue(with sessions)", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedQueueWithSessions); - await testDeadletter(true); - }); + it("Partitioned Queue with autoComplete: deadLetter() moves message to deadletter queue(with sessions)", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedQueueWithSessions); + await testDeadletter(true); + }); - it("Partitioned Subscription with autoComplete: deadLetter() moves message to deadletter(with sessions)", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); - await testDeadletter(true); - }); + it("Partitioned Subscription with autoComplete: deadLetter() moves message to deadletter(with sessions)", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); + await testDeadletter(true); + }); - it("UnPartitioned Queue with autoComplete: deadLetter() moves message to deadletter queue(with sessions)", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); - await testDeadletter(true); - }); + it("UnPartitioned Queue with autoComplete: deadLetter() moves message to deadletter queue(with sessions)", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); + await testDeadletter(true); + }); - it("UnPartitioned Subscription with autoComplete: deadLetter() moves message to deadletter queue(with sessions)", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedSubscriptionWithSessions); - await testDeadletter(true); + it("UnPartitioned Subscription with autoComplete: deadLetter() moves message to deadletter queue(with sessions)", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedSubscriptionWithSessions); + await testDeadletter(true); + }); }); -}); -describe("Sessions Streaming - Multiple Receive Operations", function(): void { - afterEach(async () => { - await afterEachTest(); - }); + describe("Sessions Streaming - Multiple Receive Operations", function(): void { + afterEach(async () => { + await afterEachTest(); + }); - async function testMultipleReceiveCalls(): Promise { - let errorMessage; - const expectedErrorMessage = getAlreadyReceivingErrorMsg( - receiverClient.entityPath, - TestMessage.sessionId - ); - receiverClient.subscribe({ - async processMessage(msg: ReceivedMessage, context: ContextWithSettlement) { - return context.complete(msg); - }, - processError - }); - await delay(5000); - try { + async function testMultipleReceiveCalls(): Promise { + let errorMessage; + const expectedErrorMessage = getAlreadyReceivingErrorMsg( + receiverClient.entityPath, + TestMessage.sessionId + ); receiverClient.subscribe({ - async processMessage() { - return Promise.resolve(); + async processMessage(msg: ReceivedMessage, context: ContextWithSettlement) { + return context.complete(msg); }, processError }); - } catch (err) { - errorMessage = err && err.message; - } - should.equal( - errorMessage, - expectedErrorMessage, - "Unexpected error message for registerMessageHandler" - ); - should.equal(unexpectedError, undefined, unexpectedError && unexpectedError.message); - - errorMessage = ""; - try { - await receiverClient.receiveBatch(1); - } catch (err) { - errorMessage = err && err.message; + await delay(5000); + try { + receiverClient.subscribe({ + async processMessage() { + return Promise.resolve(); + }, + processError + }); + } catch (err) { + errorMessage = err && err.message; + } + should.equal( + errorMessage, + expectedErrorMessage, + "Unexpected error message for registerMessageHandler" + ); + should.equal(unexpectedError, undefined, unexpectedError && unexpectedError.message); + + errorMessage = ""; + try { + await receiverClient.receiveBatch(1); + } catch (err) { + errorMessage = err && err.message; + } + should.equal( + errorMessage, + expectedErrorMessage, + "Unexpected error message for receiveMessages" + ); } - should.equal( - errorMessage, - expectedErrorMessage, - "Unexpected error message for receiveMessages" - ); - } - it("UnPartitioned Queue: Second receive operation should fail if the first streaming receiver is not stopped(with sessions) #RunInBrowser", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); - await testMultipleReceiveCalls(); - }); -}); - -describe("Sessions Streaming - Settle an already Settled message throws error", () => { - afterEach(async () => { - await afterEachTest(); + it("UnPartitioned Queue: Second receive operation should fail if the first streaming receiver is not stopped(with sessions) #RunInBrowser", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); + await testMultipleReceiveCalls(); + }); }); - const testError = (err: Error, operation: DispositionType): void => { - should.equal( - err.message, - `Failed to ${operation} the message as this message is already settled.`, - "ErrorMessage is different than expected" - ); - errorWasThrown = true; - }; - - async function testSettlement(operation: DispositionType): Promise { - const testMessage = TestMessage.getSessionSample(); - await senderClient.send(testMessage); - - const receivedMsgs: ReceivedMessage[] = []; - let contextToSettle: ContextWithSettlement; - receiverClient.subscribe({ - async processMessage(msg: ReceivedMessage, context: ContextWithSettlement) { - receivedMsgs.push(msg); - contextToSettle = context; - return Promise.resolve(); - }, - processError - }); - - const msgsCheck = await checkWithTimeout( - () => receivedMsgs.length === 1 && receivedMsgs[0].delivery.remote_settled === true - ); - should.equal( - msgsCheck, - true, - receivedMsgs.length !== 1 - ? `Expected 1, received ${receivedMsgs.length} messages` - : "Message didnt get auto-completed in time" - ); - should.equal(unexpectedError, undefined, unexpectedError && unexpectedError.message); - - should.equal(receivedMsgs.length, 1, "Unexpected number of messages"); - should.equal(receivedMsgs[0].body, testMessage.body, "MessageBody is different than expected"); - should.equal( - receivedMsgs[0].messageId, - testMessage.messageId, - "MessageId is different than expected" - ); + describe("Sessions Streaming - Settle an already Settled message throws error", () => { + afterEach(async () => { + await afterEachTest(); + }); - await testPeekMsgsLength(receiverClient, 0); + const testError = (err: Error, operation: DispositionType): void => { + should.equal( + err.message, + `Failed to ${operation} the message as this message is already settled.`, + "ErrorMessage is different than expected" + ); + errorWasThrown = true; + }; - if (operation === DispositionType.complete) { - await contextToSettle!.complete(receivedMsgs[0]).catch((err) => testError(err, operation)); - } else if (operation === DispositionType.abandon) { - await contextToSettle!.abandon(receivedMsgs[0]).catch((err) => testError(err, operation)); - } else if (operation === DispositionType.deadletter) { - await contextToSettle!.deadLetter(receivedMsgs[0]).catch((err) => testError(err, operation)); - } else if (operation === DispositionType.defer) { - await contextToSettle!.defer(receivedMsgs[0]).catch((err) => testError(err, operation)); - } + async function testSettlement(operation: DispositionType): Promise { + const testMessage = TestMessage.getSessionSample(); + await senderClient.send(testMessage); - should.equal(errorWasThrown, true, "Error thrown flag must be true"); - } + const receivedMsgs: ReceivedMessage[] = []; + let contextToSettle: ContextWithSettlement; + receiverClient.subscribe({ + async processMessage(msg: ReceivedMessage, context: ContextWithSettlement) { + receivedMsgs.push(msg); + contextToSettle = context; + return Promise.resolve(); + }, + processError + }); - it("UnPartitioned Queue: complete() throws error(with sessions) #RunInBrowser", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); - await testSettlement(DispositionType.complete); - }); + const msgsCheck = await checkWithTimeout( + () => receivedMsgs.length === 1 && receivedMsgs[0].delivery.remote_settled === true + ); + should.equal( + msgsCheck, + true, + receivedMsgs.length !== 1 + ? `Expected 1, received ${receivedMsgs.length} messages` + : "Message didnt get auto-completed in time" + ); + should.equal(unexpectedError, undefined, unexpectedError && unexpectedError.message); - it("UnPartitioned Subscription: complete() throws error(with sessions)", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedSubscriptionWithSessions); - await testSettlement(DispositionType.complete); - }); + should.equal(receivedMsgs.length, 1, "Unexpected number of messages"); + should.equal( + receivedMsgs[0].body, + testMessage.body, + "MessageBody is different than expected" + ); + should.equal( + receivedMsgs[0].messageId, + testMessage.messageId, + "MessageId is different than expected" + ); - it("UnPartitioned Queue: abandon() throws error(with sessions) #RunInBrowser", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); - await testSettlement(DispositionType.abandon); - }); + await testPeekMsgsLength(receiverClient, 0); + + if (operation === DispositionType.complete) { + await contextToSettle!.complete(receivedMsgs[0]).catch((err) => testError(err, operation)); + } else if (operation === DispositionType.abandon) { + await contextToSettle!.abandon(receivedMsgs[0]).catch((err) => testError(err, operation)); + } else if (operation === DispositionType.deadletter) { + await contextToSettle! + .deadLetter(receivedMsgs[0]) + .catch((err) => testError(err, operation)); + } else if (operation === DispositionType.defer) { + await contextToSettle!.defer(receivedMsgs[0]).catch((err) => testError(err, operation)); + } + + should.equal(errorWasThrown, true, "Error thrown flag must be true"); + } - it("UnPartitioned Subscription: abandon() throws error(with sessions)", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedSubscriptionWithSessions); - await testSettlement(DispositionType.abandon); - }); + it("UnPartitioned Queue: complete() throws error(with sessions) #RunInBrowser", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); + await testSettlement(DispositionType.complete); + }); - it("UnPartitioned Queue: defer() throws error(with sessions) #RunInBrowser", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); - await testSettlement(DispositionType.defer); - }); + it("UnPartitioned Subscription: complete() throws error(with sessions)", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedSubscriptionWithSessions); + await testSettlement(DispositionType.complete); + }); - it("UnPartitioned Subscription: defer() throws error(with sessions)", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedSubscriptionWithSessions); - await testSettlement(DispositionType.defer); - }); + it("UnPartitioned Queue: abandon() throws error(with sessions) #RunInBrowser", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); + await testSettlement(DispositionType.abandon); + }); - it("UnPartitioned Queue: deadLetter() throws error(with sessions) #RunInBrowser", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); - await testSettlement(DispositionType.deadletter); - }); + it("UnPartitioned Subscription: abandon() throws error(with sessions)", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedSubscriptionWithSessions); + await testSettlement(DispositionType.abandon); + }); - it("UnPartitioned Subscription: deadLetter() throws error(with sessions)", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedSubscriptionWithSessions); - await testSettlement(DispositionType.deadletter); - }); -}); + it("UnPartitioned Queue: defer() throws error(with sessions) #RunInBrowser", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); + await testSettlement(DispositionType.defer); + }); -describe("Sessions Streaming - User Error", function(): void { - afterEach(async () => { - await afterEachTest(); - }); + it("UnPartitioned Subscription: defer() throws error(with sessions)", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedSubscriptionWithSessions); + await testSettlement(DispositionType.defer); + }); - async function testUserError(): Promise { - const testMessage = TestMessage.getSessionSample(); - await senderClient.send(testMessage); - const errorMessage = "Will we see this error message?"; + it("UnPartitioned Queue: deadLetter() throws error(with sessions) #RunInBrowser", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); + await testSettlement(DispositionType.deadletter); + }); - const receivedMsgs: ReceivedMessage[] = []; - receiverClient.subscribe({ - async processMessage(msg: ReceivedMessage, context: ContextWithSettlement) { - await context.complete(msg); - receivedMsgs.push(msg); - throw new Error(errorMessage); - }, - processError + it("UnPartitioned Subscription: deadLetter() throws error(with sessions)", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedSubscriptionWithSessions); + await testSettlement(DispositionType.deadletter); }); + }); - const msgsCheck = await checkWithTimeout(() => receivedMsgs.length === 1); + describe("Sessions Streaming - User Error", function(): void { + afterEach(async () => { + await afterEachTest(); + }); - should.equal(msgsCheck, true, `Expected 1, received ${receivedMsgs.length} messages.`); - await receiverClient.close(); + async function testUserError(): Promise { + const testMessage = TestMessage.getSessionSample(); + await senderClient.send(testMessage); + const errorMessage = "Will we see this error message?"; - should.equal( - unexpectedError && unexpectedError.message, - errorMessage, - "User error did not surface." - ); - should.equal(receivedMsgs.length, 1, "Unexpected number of messages"); - } + const receivedMsgs: ReceivedMessage[] = []; + receiverClient.subscribe({ + async processMessage(msg: ReceivedMessage, context: ContextWithSettlement) { + await context.complete(msg); + receivedMsgs.push(msg); + throw new Error(errorMessage); + }, + processError + }); - it("UnPartitioned Queue: onError handler is called for user error(with sessions) #RunInBrowser", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); - await testUserError(); - }); -}); + const msgsCheck = await checkWithTimeout(() => receivedMsgs.length === 1); -describe("Sessions Streaming - maxConcurrentCalls", function(): void { - afterEach(async () => { - await afterEachTest(); - }); + should.equal(msgsCheck, true, `Expected 1, received ${receivedMsgs.length} messages.`); + await receiverClient.close(); - async function testConcurrency(maxConcurrentCalls?: number): Promise { - if ( - typeof maxConcurrentCalls === "number" && - (maxConcurrentCalls < 1 || maxConcurrentCalls > 2) - ) { - chai.assert.fail( - "Sorry, the tests here only support cases when maxConcurrentCalls is set to 1 or 2" + should.equal( + unexpectedError && unexpectedError.message, + errorMessage, + "User error did not surface." ); + should.equal(receivedMsgs.length, 1, "Unexpected number of messages"); } - const testMessages = [TestMessage.getSessionSample(), TestMessage.getSessionSample()]; - await senderClient.sendBatch(testMessages); + it("UnPartitioned Queue: onError handler is called for user error(with sessions) #RunInBrowser", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); + await testUserError(); + }); + }); - const settledMsgs: ReceivedMessage[] = []; - const receivedMsgs: ReceivedMessage[] = []; + describe("Sessions Streaming - maxConcurrentCalls", function(): void { + afterEach(async () => { + await afterEachTest(); + }); - receiverClient.subscribe( - { - async processMessage(msg: ReceivedMessage, context: ContextWithSettlement) { - if (receivedMsgs.length === 1) { - if ((!maxConcurrentCalls || maxConcurrentCalls === 1) && settledMsgs.length === 0) { - throw new Error( - "onMessage for the second message should not have been called before the first message got settled" - ); + async function testConcurrency(maxConcurrentCalls?: number): Promise { + if ( + typeof maxConcurrentCalls === "number" && + (maxConcurrentCalls < 1 || maxConcurrentCalls > 2) + ) { + chai.assert.fail( + "Sorry, the tests here only support cases when maxConcurrentCalls is set to 1 or 2" + ); + } + + const testMessages = [TestMessage.getSessionSample(), TestMessage.getSessionSample()]; + await senderClient.sendBatch(testMessages); + + const settledMsgs: ReceivedMessage[] = []; + const receivedMsgs: ReceivedMessage[] = []; + + receiverClient.subscribe( + { + async processMessage(msg: ReceivedMessage, context: ContextWithSettlement) { + if (receivedMsgs.length === 1) { + if ((!maxConcurrentCalls || maxConcurrentCalls === 1) && settledMsgs.length === 0) { + throw new Error( + "onMessage for the second message should not have been called before the first message got settled" + ); + } + } else { + if (maxConcurrentCalls === 2 && settledMsgs.length !== 0) { + throw new Error( + "onMessage for the second message should have been called before the first message got settled" + ); + } } - } else { - if (maxConcurrentCalls === 2 && settledMsgs.length !== 0) { - throw new Error( - "onMessage for the second message should have been called before the first message got settled" - ); - } - } - receivedMsgs.push(msg); - await delay(2000); - await context.complete(msg); - settledMsgs.push(msg); + receivedMsgs.push(msg); + await delay(2000); + await context.complete(msg); + settledMsgs.push(msg); + }, + processError }, - processError - }, - maxConcurrentCalls ? { maxConcurrentCalls } : {} - ); + maxConcurrentCalls ? { maxConcurrentCalls } : {} + ); - await checkWithTimeout(() => settledMsgs.length === 2); - await receiverClient.close(); + await checkWithTimeout(() => settledMsgs.length === 2); + await receiverClient.close(); - should.equal(unexpectedError, undefined, unexpectedError && unexpectedError.message); - should.equal(settledMsgs.length, 2, `Expected 2, received ${settledMsgs.length} messages.`); - } + should.equal(unexpectedError, undefined, unexpectedError && unexpectedError.message); + should.equal(settledMsgs.length, 2, `Expected 2, received ${settledMsgs.length} messages.`); + } - it("Partitioned Queue: no maxConcurrentCalls passed(with sessions)", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedQueueWithSessions); - await testConcurrency(); - }); + it("Partitioned Queue: no maxConcurrentCalls passed(with sessions)", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedQueueWithSessions); + await testConcurrency(); + }); - it("Partitioned Queue: pass 1 for maxConcurrentCalls(with sessions)", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedQueueWithSessions); - await testConcurrency(); - }); + it("Partitioned Queue: pass 1 for maxConcurrentCalls(with sessions)", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedQueueWithSessions); + await testConcurrency(); + }); - it("Partitioned Queue: pass 2 for maxConcurrentCalls(with sessions)", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedQueueWithSessions); - await testConcurrency(); - }); + it("Partitioned Queue: pass 2 for maxConcurrentCalls(with sessions)", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedQueueWithSessions); + await testConcurrency(); + }); - it("Unpartitioned Queue: no maxConcurrentCalls passed(with sessions) #RunInBrowser", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); - await testConcurrency(); - }); + it("Unpartitioned Queue: no maxConcurrentCalls passed(with sessions) #RunInBrowser", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); + await testConcurrency(); + }); - it("Unpartitioned Queue: pass 1 for maxConcurrentCalls(with sessions) #RunInBrowser", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); - await testConcurrency(); - }); + it("Unpartitioned Queue: pass 1 for maxConcurrentCalls(with sessions) #RunInBrowser", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); + await testConcurrency(); + }); - it("Unpartitioned Queue: pass 2 for maxConcurrentCalls(with sessions) #RunInBrowser", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); - await testConcurrency(); - }); + it("Unpartitioned Queue: pass 2 for maxConcurrentCalls(with sessions) #RunInBrowser", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); + await testConcurrency(); + }); - it("Partitioned Subscription: no maxConcurrentCalls passed(with sessions)", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); - await testConcurrency(); - }); + it("Partitioned Subscription: no maxConcurrentCalls passed(with sessions)", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); + await testConcurrency(); + }); - it("Partitioned Queue: pass 1 for maxConcurrentCalls(with sessions)", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); - await testConcurrency(1); - }); + it("Partitioned Queue: pass 1 for maxConcurrentCalls(with sessions)", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); + await testConcurrency(1); + }); - it("Partitioned Queue: pass 2 for maxConcurrentCalls(with sessions)", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); - await testConcurrency(2); - }); + it("Partitioned Queue: pass 2 for maxConcurrentCalls(with sessions)", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); + await testConcurrency(2); + }); - it("Unpartitioned Subscription: no maxConcurrentCalls passed(with sessions)", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedSubscriptionWithSessions); - await testConcurrency(); - }); + it("Unpartitioned Subscription: no maxConcurrentCalls passed(with sessions)", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedSubscriptionWithSessions); + await testConcurrency(); + }); - it("Unpartitioned Subscription: pass 1 for maxConcurrentCalls(with sessions)", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedSubscriptionWithSessions); - await testConcurrency(1); - }); + it("Unpartitioned Subscription: pass 1 for maxConcurrentCalls(with sessions)", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedSubscriptionWithSessions); + await testConcurrency(1); + }); - it("Unpartitioned Subscription: pass 2 for maxConcurrentCalls(with sessions)", async function(): Promise< - void - > { - await beforeEachTest(TestClientType.UnpartitionedSubscriptionWithSessions); - await testConcurrency(2); + it("Unpartitioned Subscription: pass 2 for maxConcurrentCalls(with sessions)", async function(): Promise< + void + > { + await beforeEachTest(TestClientType.UnpartitionedSubscriptionWithSessions); + await testConcurrency(2); + }); }); + // #RevisitCommentedTestsAfterTheSingleClientAPI + // Cannot close the receiver manually with the current 2-client API + // Re-visit after the top level client is implemented + // describe("Sessions Streaming - Not receive messages after receiver is closed", function(): void { + // afterEach(async () => { + // await afterEachTest(); + // }); + + // async function testReceiveMessages(): Promise { + // const totalNumOfMessages = 5; + // let num = 1; + // const messages = []; + // while (num <= totalNumOfMessages) { + // const message = { + // messageId: num, + // body: "test", + // label: `${num}`, + // sessionId: TestMessage.sessionId, + // partitionKey: "dummy" // Ensures all messages go to same parition to make peek work reliably + // }; + // num++; + // messages.push(message); + // } + // await senderClient.sendBatch(messages); + + // const receivedMsgs: ReceivedMessage[] = []; + + // receiverClient.subscribe( + // { + // async processMessage(brokeredMessage: ReceivedMessage, context: ContextWithSettlement) { + // receivedMsgs.push(brokeredMessage); + // await context.complete(brokeredMessage); + // }, + // processError + // }, + // { + // autoComplete: false + // } + // ); + // await receiverClient.close(); + + // await delay(5000); + // should.equal( + // receivedMsgs.length, + // 0, + // `Expected 0 messages, but received ${receivedMsgs.length}` + // ); + // await testPeekMsgsLength(receiverClient, totalNumOfMessages); + // } + + // it("UnPartitioned Queue: Not receive messages after receiver is closed #RunInBrowser", async function(): Promise< + // void + // > { + // await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); + // await testReceiveMessages(); + // }); + + // it("UnPartitioned Queue: (Receive And Delete mode) Not receive messages after receiver is closed #RunInBrowser", async function(): Promise< + // void + // > { + // await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions, "receiveAndDelete"); + // await testReceiveMessages(); + // }); + // }); }); -// #RevisitCommentedTestsAfterTheSingleClientAPI -// Cannot close the receiver manually with the current 2-client API -// Re-visit after the top level client is implemented -// describe("Sessions Streaming - Not receive messages after receiver is closed", function(): void { -// afterEach(async () => { -// await afterEachTest(); -// }); - -// async function testReceiveMessages(): Promise { -// const totalNumOfMessages = 5; -// let num = 1; -// const messages = []; -// while (num <= totalNumOfMessages) { -// const message = { -// messageId: num, -// body: "test", -// label: `${num}`, -// sessionId: TestMessage.sessionId, -// partitionKey: "dummy" // Ensures all messages go to same parition to make peek work reliably -// }; -// num++; -// messages.push(message); -// } -// await senderClient.sendBatch(messages); - -// const receivedMsgs: ReceivedMessage[] = []; - -// receiverClient.subscribe( -// { -// async processMessage(brokeredMessage: ReceivedMessage, context: ContextWithSettlement) { -// receivedMsgs.push(brokeredMessage); -// await context.complete(brokeredMessage); -// }, -// processError -// }, -// { -// autoComplete: false -// } -// ); -// await receiverClient.close(); - -// await delay(5000); -// should.equal( -// receivedMsgs.length, -// 0, -// `Expected 0 messages, but received ${receivedMsgs.length}` -// ); -// await testPeekMsgsLength(receiverClient, totalNumOfMessages); -// } - -// it("UnPartitioned Queue: Not receive messages after receiver is closed #RunInBrowser", async function(): Promise< -// void -// > { -// await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); -// await testReceiveMessages(); -// }); - -// it("UnPartitioned Queue: (Receive And Delete mode) Not receive messages after receiver is closed #RunInBrowser", async function(): Promise< -// void -// > { -// await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions, "receiveAndDelete"); -// await testReceiveMessages(); -// }); -// }); diff --git a/sdk/servicebus/service-bus/test/topicFilters.spec.ts b/sdk/servicebus/service-bus/test/topicFilters.spec.ts index 9d6796e4223a..7c0c2e224ad6 100644 --- a/sdk/servicebus/service-bus/test/topicFilters.spec.ts +++ b/sdk/servicebus/service-bus/test/topicFilters.spec.ts @@ -9,508 +9,488 @@ import { ServiceBusMessage, SendableMessageInfo, CorrelationFilter, - ServiceBusSenderClient + ContextWithSettlement } from "../src"; +import { TestClientType, checkWithTimeout } from "./utils/testUtils"; +import { Receiver, SubscriptionRuleManagement } from "../src/receivers/receiver"; +import { Sender } from "../src/sender"; import { - getSenderReceiverClients, - TestClientType, - purge, - checkWithTimeout, - ReceiverClientTypeForUserT -} from "./utils/testUtils"; -import { - SubscriptionRuleManagement, - NonSessionReceiver, - SessionReceiver -} from "../src/serviceBusReceiverClient"; - -// We need to remove rules before adding one because otherwise the existing default rule will let in all messages. -async function removeAllRules(client: SubscriptionRuleManagement): Promise { - const rules = await client.getRules(); - for (let i = 0; i < rules.length; i++) { - const rule = rules[i]; - await client.removeRule(rule.name); - } -} - -async function testPeekMsgsLength( - client: ReceiverClientTypeForUserT<"peekLock">, - expectedPeekLength: number -): Promise { - const peekedMsgs = await client.diagnostics.peek(expectedPeekLength + 1); - should.equal( - peekedMsgs.length, - expectedPeekLength, - "Unexpected number of msgs found when peeking" - ); -} - -let subscriptionClient: ( - | NonSessionReceiver<"peekLock" | "receiveAndDelete"> - | SessionReceiver<"peekLock" | "receiveAndDelete"> -) & - SubscriptionRuleManagement; -let topicClient: ServiceBusSenderClient; - -async function beforeEachTest(receiverType: TestClientType): Promise { - // The tests in this file expect the env variables to contain the connection string and - // the names of empty queue/topic/subscription that are to be tested - - const clients = await getSenderReceiverClients( - TestClientType.TopicFilterTestSubscription, - "peekLock" - ); - topicClient = clients.senderClient; - subscriptionClient = clients.receiverClient as ( - | NonSessionReceiver<"peekLock" | "receiveAndDelete"> - | SessionReceiver<"peekLock" | "receiveAndDelete"> - ) & - SubscriptionRuleManagement; - - await purge(subscriptionClient); - const peekedSubscriptionMsg = await subscriptionClient.diagnostics.peek(); - if (peekedSubscriptionMsg.length) { - chai.assert.fail("Please use an empty Subscription for integration testing"); - } - if (receiverType === TestClientType.TopicFilterTestSubscription) { - await removeAllRules(subscriptionClient); - } -} + ServiceBusClientForTests, + createServiceBusClientForTests, + testPeekMsgsLength +} from "./utils/testutils2"; -async function afterEachTest(clearRules: boolean = true): Promise { - if (clearRules) { - await removeAllRules(subscriptionClient); - await subscriptionClient.addRule("DefaultFilter", true); +describe("topic filters", () => { + let subscriptionClient: Receiver & SubscriptionRuleManagement; + let topicClient: Sender; + let serviceBusClient: ServiceBusClientForTests; - const rules = await subscriptionClient.getRules(); - should.equal(rules.length, 1, "Unexpected number of rules"); - should.equal(rules[0].name, "DefaultFilter", "RuleName is different than expected"); - } - await topicClient.close(); - await subscriptionClient.close(); -} - -const data = [ - { Color: "blue", Quantity: 5, Priority: "low" }, - { Color: "red", Quantity: 10, Priority: "high" }, - { Color: "yellow", Quantity: 5, Priority: "low" }, - { Color: "blue", Quantity: 10, Priority: "low" }, - { Color: "blue", Quantity: 5, Priority: "high" }, - { Color: "blue", Quantity: 10, Priority: "low" }, - { Color: "red", Quantity: 5, Priority: "low" }, - { Color: "red", Quantity: 10, Priority: "low" }, - { Color: "red", Quantity: 5, Priority: "low" }, - { Color: "yellow", Quantity: 10, Priority: "high" }, - { Color: "yellow", Quantity: 5, Priority: "low" }, - { Color: "yellow", Quantity: 10, Priority: "low" } -]; - -async function sendOrders(): Promise { - for (let index = 0; index < data.length; index++) { - const element = data[index]; - const message: SendableMessageInfo = { - body: "", - messageId: `messageId: ${Math.random()}`, - correlationId: `${element.Priority}`, - label: `${element.Color}`, - userProperties: { - color: `${element.Color}`, - quantity: element.Quantity, - priority: `${element.Priority}` - }, - partitionKey: "dummy" // Ensures all messages go to same parition to make peek work reliably - }; - await topicClient.send(message); - } -} - -async function receiveOrders( - client: ( - | NonSessionReceiver<"peekLock" | "receiveAndDelete"> - | SessionReceiver<"peekLock" | "receiveAndDelete"> - ) & - SubscriptionRuleManagement, - expectedMessageCount: number -): Promise { - let errorFromErrorHandler: Error | undefined; - const receivedMsgs: ServiceBusMessage[] = []; - client.subscribe({ - async processMessage(msg: ServiceBusMessage) { - await msg.complete(); - receivedMsgs.push(msg); - }, - async processError(err: Error) { - if (err) { - errorFromErrorHandler = err; - } - } + before(() => { + serviceBusClient = createServiceBusClientForTests(); }); - const msgsCheck = await checkWithTimeout(() => receivedMsgs.length === expectedMessageCount); - should.equal( - msgsCheck, - true, - `Expected ${expectedMessageCount}, but received ${receivedMsgs.length} messages` - ); - - should.equal( - errorFromErrorHandler, - undefined, - errorFromErrorHandler && errorFromErrorHandler.message - ); - should.equal(receivedMsgs.length, expectedMessageCount, "Unexpected number of messages"); - - return receivedMsgs; -} - -async function addRules( - ruleName: string, - filter: boolean | string | CorrelationFilter, - sqlRuleActionExpression?: string -): Promise { - await subscriptionClient.addRule(ruleName, filter, sqlRuleActionExpression); - - const rules = await subscriptionClient.getRules(); - should.equal(rules.length, 1, "Unexpected number of rules"); - should.equal(rules[0].name, ruleName, "Expected Rule not found"); - - if (sqlRuleActionExpression) { - should.equal(rules[0].action!, sqlRuleActionExpression, "Action not set on the rule."); + after(async () => { + // TODO:this test apparently has always "held onto" the receivers during the entire run + // TODO: want to look more at this to see if we can more cleanly close/reopen the $management link. + // to see the issue move this .afterEach call into the actual afterEachTest() function below. + await serviceBusClient.test.afterEach(); + return serviceBusClient.test.after(); + }); + + async function beforeEachTest(receiverType: TestClientType): Promise { + // The tests in this file expect the env variables to contain the connection string and + // the names of empty queue/topic/subscription that are to be tested + + serviceBusClient = createServiceBusClientForTests(); + + const entityNames = await serviceBusClient.test.createTestEntities(receiverType); + + subscriptionClient = await serviceBusClient.test.getSubscriptionPeekLockReceiver(entityNames); + topicClient = serviceBusClient.test.addToCleanup( + serviceBusClient.getSender(entityNames.topic!) + ); + + if (receiverType === TestClientType.TopicFilterTestSubscription) { + await removeAllRules(subscriptionClient); + } } -} -describe("addRule() #RunInBrowser", function(): void { - beforeEach(async () => { - await beforeEachTest(TestClientType.TopicFilterTestSubscription); - }); + async function afterEachTest(clearRules: boolean = true): Promise { + if (clearRules) { + await removeAllRules(subscriptionClient); + await subscriptionClient.addRule("DefaultFilter", true); - afterEach(async () => { - await afterEachTest(); - }); + const rules = await subscriptionClient.getRules(); + should.equal(rules.length, 1, "Unexpected number of rules"); + should.equal(rules[0].name, "DefaultFilter", "RuleName is different than expected"); + } - async function BooleanFilter(bool: boolean): Promise { - await subscriptionClient.addRule("BooleanFilter", bool); - const rules = await subscriptionClient.getRules(); - should.equal(rules.length, 1, "Unexpected number of rules"); - should.equal(rules[0].name, "BooleanFilter", "RuleName is different than expected"); + // RJP: tearing down the management link causes a failure? + // await serviceBusClient.test.afterEach(); } - it("Add True Filter", async function(): Promise { - await BooleanFilter(true); - }); + // We need to remove rules before adding one because otherwise the existing default rule will let in all messages. + async function removeAllRules(client: SubscriptionRuleManagement): Promise { + const rules = await client.getRules(); + for (let i = 0; i < rules.length; i++) { + const rule = rules[i]; + await client.removeRule(rule.name); + } + } - it("Add False Filter", async function(): Promise { - await BooleanFilter(false); - }); + const data = [ + { Color: "blue", Quantity: 5, Priority: "low" }, + { Color: "red", Quantity: 10, Priority: "high" }, + { Color: "yellow", Quantity: 5, Priority: "low" }, + { Color: "blue", Quantity: 10, Priority: "low" }, + { Color: "blue", Quantity: 5, Priority: "high" }, + { Color: "blue", Quantity: 10, Priority: "low" }, + { Color: "red", Quantity: 5, Priority: "low" }, + { Color: "red", Quantity: 10, Priority: "low" }, + { Color: "red", Quantity: 5, Priority: "low" }, + { Color: "yellow", Quantity: 10, Priority: "high" }, + { Color: "yellow", Quantity: 5, Priority: "low" }, + { Color: "yellow", Quantity: 10, Priority: "low" } + ]; + + async function sendOrders(): Promise { + for (let index = 0; index < data.length; index++) { + const element = data[index]; + const message: SendableMessageInfo = { + body: "", + messageId: `messageId: ${Math.random()}`, + correlationId: `${element.Priority}`, + label: `${element.Color}`, + userProperties: { + color: `${element.Color}`, + quantity: element.Quantity, + priority: `${element.Priority}` + }, + partitionKey: "dummy" // Ensures all messages go to same parition to make peek work reliably + }; + await topicClient.send(message); + } + } - it("Add SQL filter", async function(): Promise { - await subscriptionClient.addRule( - "Priority_1", - "(priority = 1 OR priority = 2) AND (sys.label LIKE '%String2')" + async function receiveOrders( + client: Receiver & SubscriptionRuleManagement, + expectedMessageCount: number + ): Promise { + let errorFromErrorHandler: Error | undefined; + const receivedMsgs: ServiceBusMessage[] = []; + client.subscribe({ + async processMessage(msg: ServiceBusMessage) { + await msg.complete(); + receivedMsgs.push(msg); + }, + async processError(err: Error) { + if (err) { + errorFromErrorHandler = err; + } + } + }); + + const msgsCheck = await checkWithTimeout(() => receivedMsgs.length === expectedMessageCount); + should.equal( + msgsCheck, + true, + `Expected ${expectedMessageCount}, but received ${receivedMsgs.length} messages` ); - const rules = await subscriptionClient.getRules(); - should.equal(rules.length, 1, "Unexpected number of rules"); - should.equal(rules[0].name, "Priority_1", "RuleName is different than expected"); - }); - it("Add SQL filter and action", async function(): Promise { - await subscriptionClient.addRule( - "Priority_1", - "(priority = 1 OR priority = 3) AND (sys.label LIKE '%String1')", - "SET sys.label = 'MessageX'" + should.equal( + errorFromErrorHandler, + undefined, + errorFromErrorHandler && errorFromErrorHandler.message ); + should.equal(receivedMsgs.length, expectedMessageCount, "Unexpected number of messages"); + + return receivedMsgs; + } + + async function addRules( + ruleName: string, + filter: boolean | string | CorrelationFilter, + sqlRuleActionExpression?: string + ): Promise { + await subscriptionClient.addRule(ruleName, filter, sqlRuleActionExpression); + const rules = await subscriptionClient.getRules(); should.equal(rules.length, 1, "Unexpected number of rules"); - should.equal(rules[0].name, "Priority_1", "RuleName is different than expected"); - }); + should.equal(rules[0].name, ruleName, "Expected Rule not found"); - it("Add Correlation filter", async function(): Promise { - await subscriptionClient.addRule("Correlationfilter", { - label: "red", - correlationId: "high" + if (sqlRuleActionExpression) { + should.equal(rules[0].action!, sqlRuleActionExpression, "Action not set on the rule."); + } + } + + describe("addRule() #RunInBrowser", function(): void { + beforeEach(async () => { + await beforeEachTest(TestClientType.TopicFilterTestSubscription); }); - const rules = await subscriptionClient.getRules(); - should.equal(rules.length, 1, "Unexpected number of rules"); - should.equal(rules[0].name, "Correlationfilter", "RuleName is different than expected"); - }); - it("Add rule with a name which matches with existing rule", async function(): Promise { - await subscriptionClient.addRule("Priority_1", "priority = 1"); - let errorWasThrown = false; - try { - await subscriptionClient.addRule("Priority_1", "priority = 2"); - } catch (error) { - errorWasThrown = true; - should.equal( - !error.message.search("Priority_1' already exists."), - false, - "ErrorMessage is different than expected" - ); - should.equal( - error.code, - "MessagingEntityAlreadyExistsError", - "Error code is different than expected" - ); + afterEach(async () => { + await afterEachTest(); + }); + + async function BooleanFilter(bool: boolean): Promise { + await subscriptionClient.addRule("BooleanFilter", bool); + const rules = await subscriptionClient.getRules(); + should.equal(rules.length, 1, "Unexpected number of rules"); + should.equal(rules[0].name, "BooleanFilter", "RuleName is different than expected"); } - should.equal(errorWasThrown, true, "Error thrown flag must be true"); - }); -}); -describe("removeRule()", function(): void { - beforeEach(async () => { - await beforeEachTest(TestClientType.TopicFilterTestSubscription); - }); + it("Add True Filter", async function(): Promise { + await BooleanFilter(true); + }); - afterEach(async () => { - await afterEachTest(); - }); + it("Add False Filter", async function(): Promise { + await BooleanFilter(false); + }); - it("Removing non existing rule on a subscription that doesnt have any rules should throw error", async function(): Promise< - void - > { - let errorWasThrown = false; - try { - await subscriptionClient.removeRule("Priority_5"); - } catch (error) { - should.equal( - !error.message.search("Priority_5' could not be found"), - false, - "ErrorMessage is different than expected" + it("Add SQL filter", async function(): Promise { + await subscriptionClient.addRule( + "Priority_1", + "(priority = 1 OR priority = 2) AND (sys.label LIKE '%String2')" ); - should.equal( - error.code, - "MessagingEntityNotFoundError", - "Error code is different than expected" + const rules = await subscriptionClient.getRules(); + should.equal(rules.length, 1, "Unexpected number of rules"); + should.equal(rules[0].name, "Priority_1", "RuleName is different than expected"); + }); + + it("Add SQL filter and action", async function(): Promise { + await subscriptionClient.addRule( + "Priority_1", + "(priority = 1 OR priority = 3) AND (sys.label LIKE '%String1')", + "SET sys.label = 'MessageX'" ); - errorWasThrown = true; - } - should.equal(errorWasThrown, true, "Error thrown flag must be true"); - }); + const rules = await subscriptionClient.getRules(); + should.equal(rules.length, 1, "Unexpected number of rules"); + should.equal(rules[0].name, "Priority_1", "RuleName is different than expected"); + }); + + it("Add Correlation filter", async function(): Promise { + await subscriptionClient.addRule("Correlationfilter", { + label: "red", + correlationId: "high" + }); + const rules = await subscriptionClient.getRules(); + should.equal(rules.length, 1, "Unexpected number of rules"); + should.equal(rules[0].name, "Correlationfilter", "RuleName is different than expected"); + }); - it("Removing non existing rule on a subscription that has other rules should throw error", async function(): Promise< - void - > { - let errorWasThrown = false; - try { + it("Add rule with a name which matches with existing rule", async function(): Promise { await subscriptionClient.addRule("Priority_1", "priority = 1"); - await subscriptionClient.removeRule("Priority_5"); - } catch (error) { - errorWasThrown = true; - } - should.equal(errorWasThrown, true, "Error thrown flag must be true"); + let errorWasThrown = false; + try { + await subscriptionClient.addRule("Priority_1", "priority = 2"); + } catch (error) { + errorWasThrown = true; + should.equal( + !error.message.search("Priority_1' already exists."), + false, + "ErrorMessage is different than expected" + ); + should.equal( + error.code, + "MessagingEntityAlreadyExistsError", + "Error code is different than expected" + ); + } + should.equal(errorWasThrown, true, "Error thrown flag must be true"); + }); }); -}); -describe("getRules() #RunInBrowser", function(): void { - beforeEach(async () => { - await beforeEachTest(TestClientType.TopicFilterTestSubscription); - }); + describe("removeRule()", function(): void { + beforeEach(async () => { + await beforeEachTest(TestClientType.TopicFilterTestSubscription); + }); - afterEach(async () => { - await afterEachTest(); - }); + afterEach(async () => { + await afterEachTest(); + }); - it("Subscription with 0/1/multiple rules returns rules as expected", async function(): Promise< - void - > { - let rules = await subscriptionClient.getRules(); - should.equal(rules.length, 0, "Unexpected number of rules"); + it("Removing non existing rule on a subscription that doesnt have any rules should throw error", async function(): Promise< + void + > { + let errorWasThrown = false; + try { + await subscriptionClient.removeRule("Priority_5"); + } catch (error) { + should.equal( + !error.message.search("Priority_5' could not be found"), + false, + "ErrorMessage is different than expected" + ); + should.equal( + error.code, + "MessagingEntityNotFoundError", + "Error code is different than expected" + ); + errorWasThrown = true; + } + should.equal(errorWasThrown, true, "Error thrown flag must be true"); + }); - const expr1 = "(priority = 1)"; - await subscriptionClient.addRule("Priority_1", expr1); - rules = await subscriptionClient.getRules(); - should.equal(rules.length, 1, "Unexpected number of rules"); - should.equal(rules[0].name, "Priority_1", "RuleName is different than expected"); - should.equal(rules[0].filter, expr1, "Filter-expression is different than expected"); - - const expr2 = "(priority = 1 OR priority = 3) AND (sys.label LIKE '%String1')"; - await subscriptionClient.addRule("Priority_2", expr2); - rules = await subscriptionClient.getRules(); - should.equal(rules.length, 2, "Unexpected number of rules"); - should.equal(rules[0].name, "Priority_1", "RuleName is different than expected"); - should.equal(rules[0].filter, expr1, "Filter-expression is different than expected"); - should.equal(rules[1].name, "Priority_2", "RuleName is different than expected"); - should.equal(rules[1].filter, expr2, "Filter-expression is different than expected"); + it("Removing non existing rule on a subscription that has other rules should throw error", async function(): Promise< + void + > { + let errorWasThrown = false; + try { + await subscriptionClient.addRule("Priority_1", "priority = 1"); + await subscriptionClient.removeRule("Priority_5"); + } catch (error) { + errorWasThrown = true; + } + should.equal(errorWasThrown, true, "Error thrown flag must be true"); + }); }); - it("Rule with SQL filter and action returns expected filter and action expression", async function(): Promise< - void - > { - await subscriptionClient.addRule( - "Priority_1", - "(priority = 1 OR priority = 3) AND (sys.label LIKE '%String1')", - "SET sys.label = 'MessageX'" - ); - const rules = await subscriptionClient.getRules(); - should.equal(rules[0].name, "Priority_1", "RuleName is different than expected"); - }); + describe("getRules() #RunInBrowser", function(): void { + beforeEach(async () => { + await beforeEachTest(TestClientType.TopicFilterTestSubscription); + }); - it("Rule with Correlation filter returns expected filter", async function(): Promise { - await subscriptionClient.addRule("Correlationfilter", { - label: "red", - correlationId: "high" + afterEach(async () => { + await afterEachTest(); }); - const rules = await subscriptionClient.getRules(); - should.equal(rules[0].name, "Correlationfilter", "RuleName is different than expected"); - const expectedFilter = { - correlationId: "high", - label: "red", - userProperties: [] - }; - should.equal(rules.length, 1, "Unexpected number of rules"); - rules.forEach((rule) => { - should.equal( - (rule.filter).correlationId, - expectedFilter.correlationId, - "MessageId is different than expected" - ); - should.equal( - (rule.filter).label, - expectedFilter.label, - "Filter-label is different than expected" - ); - const userProperties = (rule.filter).userProperties; - should.equal(Array.isArray(userProperties), true, "`ReceivedMessages` is not an array"); - should.equal(userProperties.length, 0, "Unexpected number of messages"); + + it("Subscription with 0/1/multiple rules returns rules as expected", async function(): Promise< + void + > { + let rules = await subscriptionClient.getRules(); + should.equal(rules.length, 0, "Unexpected number of rules"); + + const expr1 = "(priority = 1)"; + await subscriptionClient.addRule("Priority_1", expr1); + rules = await subscriptionClient.getRules(); + should.equal(rules.length, 1, "Unexpected number of rules"); + should.equal(rules[0].name, "Priority_1", "RuleName is different than expected"); + should.equal(rules[0].filter, expr1, "Filter-expression is different than expected"); + + const expr2 = "(priority = 1 OR priority = 3) AND (sys.label LIKE '%String1')"; + await subscriptionClient.addRule("Priority_2", expr2); + rules = await subscriptionClient.getRules(); + should.equal(rules.length, 2, "Unexpected number of rules"); + should.equal(rules[0].name, "Priority_1", "RuleName is different than expected"); + should.equal(rules[0].filter, expr1, "Filter-expression is different than expected"); + should.equal(rules[1].name, "Priority_2", "RuleName is different than expected"); + should.equal(rules[1].filter, expr2, "Filter-expression is different than expected"); }); - }); -}); -describe("Default Rule - Send/Receive", function(): void { - beforeEach(async () => { - await beforeEachTest(TestClientType.TopicFilterTestDefaultSubscription); - }); + it("Rule with SQL filter and action returns expected filter and action expression", async function(): Promise< + void + > { + await subscriptionClient.addRule( + "Priority_1", + "(priority = 1 OR priority = 3) AND (sys.label LIKE '%String1')", + "SET sys.label = 'MessageX'" + ); + const rules = await subscriptionClient.getRules(); + should.equal(rules[0].name, "Priority_1", "RuleName is different than expected"); + }); - afterEach(async () => { - await afterEachTest(false); + it("Rule with Correlation filter returns expected filter", async function(): Promise { + await subscriptionClient.addRule("Correlationfilter", { + label: "red", + correlationId: "high" + }); + const rules = await subscriptionClient.getRules(); + should.equal(rules[0].name, "Correlationfilter", "RuleName is different than expected"); + const expectedFilter = { + correlationId: "high", + label: "red", + userProperties: [] + }; + should.equal(rules.length, 1, "Unexpected number of rules"); + rules.forEach((rule) => { + should.equal( + (rule.filter).correlationId, + expectedFilter.correlationId, + "MessageId is different than expected" + ); + should.equal( + (rule.filter).label, + expectedFilter.label, + "Filter-label is different than expected" + ); + const userProperties = (rule.filter).userProperties; + should.equal(Array.isArray(userProperties), true, "`ReceivedMessages` is not an array"); + should.equal(userProperties.length, 0, "Unexpected number of messages"); + }); + }); }); - it("Default rule is returned for the subscription for which no rules were added", async function(): Promise< - void - > { - const rules = await subscriptionClient.getRules(); - should.equal(rules.length, 1, "Unexpected number of rules"); - should.equal(rules[0].name, "$Default", "RuleName is different than expected"); - }); + describe("Default Rule - Send/Receive", function(): void { + beforeEach(async () => { + await beforeEachTest(TestClientType.TopicFilterTestDefaultSubscription); + }); - it("Subscription with default filter receives all messages", async function(): Promise { - await sendOrders(); - await receiveOrders(subscriptionClient, data.length); + afterEach(async () => { + await afterEachTest(false); + }); - await testPeekMsgsLength(subscriptionClient, 0); - }); -}); + it("Default rule is returned for the subscription for which no rules were added", async function(): Promise< + void + > { + const rules = await subscriptionClient.getRules(); + should.equal(rules.length, 1, "Unexpected number of rules"); + should.equal(rules[0].name, "$Default", "RuleName is different than expected"); + }); -describe("Boolean Filter - Send/Receive", function(): void { - beforeEach(async () => { - await beforeEachTest(TestClientType.TopicFilterTestSubscription); - }); + it("Subscription with default filter receives all messages", async function(): Promise { + await sendOrders(); + await receiveOrders(subscriptionClient, data.length); - afterEach(async () => { - await afterEachTest(); + await testPeekMsgsLength(subscriptionClient, 0); + }); }); - async function addFilterAndReceiveOrders( - bool: boolean, - client: ( - | NonSessionReceiver<"peekLock" | "receiveAndDelete"> - | SessionReceiver<"peekLock" | "receiveAndDelete"> - ) & - SubscriptionRuleManagement, - expectedMessageCount: number - ): Promise { - await subscriptionClient.addRule("BooleanFilter", bool); - const rules = await subscriptionClient.getRules(); - should.equal(rules.length, 1, "Unexpected number of rules"); - should.equal(rules[0].name, "BooleanFilter", "RuleName is different than expected"); - - await sendOrders(); - await receiveOrders(client, expectedMessageCount); - await testPeekMsgsLength(client, 0); - } + describe("Boolean Filter - Send/Receive", function(): void { + beforeEach(async () => { + await beforeEachTest(TestClientType.TopicFilterTestSubscription); + }); - it("True boolean filter receives all messages", async function(): Promise { - await addFilterAndReceiveOrders(true, subscriptionClient, data.length); - }); + afterEach(async () => { + await afterEachTest(); + }); - it("False boolean filter does not receive any messages", async function(): Promise { - await addFilterAndReceiveOrders(false, subscriptionClient, 0); - }); -}); + async function addFilterAndReceiveOrders( + bool: boolean, + client: Receiver & SubscriptionRuleManagement, + expectedMessageCount: number + ): Promise { + await subscriptionClient.addRule("BooleanFilter", bool); + const rules = await subscriptionClient.getRules(); + should.equal(rules.length, 1, "Unexpected number of rules"); + should.equal(rules[0].name, "BooleanFilter", "RuleName is different than expected"); + + await sendOrders(); + await receiveOrders(client, expectedMessageCount); + await testPeekMsgsLength(client, 0); + } -describe("Sql Filter - Send/Receive", function(): void { - beforeEach(async () => { - await beforeEachTest(TestClientType.TopicFilterTestSubscription); - }); + it("True boolean filter receives all messages", async function(): Promise { + await addFilterAndReceiveOrders(true, subscriptionClient, data.length); + }); - afterEach(async () => { - await afterEachTest(); + it("False boolean filter does not receive any messages", async function(): Promise { + await addFilterAndReceiveOrders(false, subscriptionClient, 0); + }); }); - it("SQL rule filter on the message properties", async function(): Promise { - await addRules("SQLMsgPropertyRule", "sys.label = 'red'"); + describe("Sql Filter - Send/Receive", function(): void { + beforeEach(async () => { + await beforeEachTest(TestClientType.TopicFilterTestSubscription); + }); - await sendOrders(); - const dataLength = data.filter((x) => x.Color === "red").length; - await receiveOrders(subscriptionClient, dataLength); + afterEach(async () => { + await afterEachTest(); + }); - await testPeekMsgsLength(subscriptionClient, 0); - }); + it("SQL rule filter on the message properties", async function(): Promise { + await addRules("SQLMsgPropertyRule", "sys.label = 'red'"); - it("SQL rule filter on the custom properties", async function(): Promise { - await addRules("SQLCustomRule", "color = 'red'"); + await sendOrders(); + const dataLength = data.filter((x) => x.Color === "red").length; + await receiveOrders(subscriptionClient, dataLength); - await sendOrders(); - const dataLength = data.filter((x) => x.Color === "red").length; - await receiveOrders(subscriptionClient, dataLength); + await testPeekMsgsLength(subscriptionClient, 0); + }); - await testPeekMsgsLength(subscriptionClient, 0); - }); + it("SQL rule filter on the custom properties", async function(): Promise { + await addRules("SQLCustomRule", "color = 'red'"); - it("SQL rule filter using AND operator ", async function(): Promise { - await addRules("SqlRuleWithAND", "color = 'blue' and quantity = 10"); + await sendOrders(); + const dataLength = data.filter((x) => x.Color === "red").length; + await receiveOrders(subscriptionClient, dataLength); - await sendOrders(); - const dataLength = data.filter((x) => x.Color === "blue" && x.Quantity === 10).length; - await receiveOrders(subscriptionClient, dataLength); + await testPeekMsgsLength(subscriptionClient, 0); + }); - await testPeekMsgsLength(subscriptionClient, 0); - }); + it("SQL rule filter using AND operator ", async function(): Promise { + await addRules("SqlRuleWithAND", "color = 'blue' and quantity = 10"); - it("SQL rule filter using OR operator ", async function(): Promise { - await addRules("SqlRuleWithOR", "color = 'blue' OR quantity = 10"); + await sendOrders(); + const dataLength = data.filter((x) => x.Color === "blue" && x.Quantity === 10).length; + await receiveOrders(subscriptionClient, dataLength); - await sendOrders(); - const dataLength = data.filter((x) => x.Color === "blue" || x.Quantity === 10).length; - await receiveOrders(subscriptionClient, dataLength); + await testPeekMsgsLength(subscriptionClient, 0); + }); - await testPeekMsgsLength(subscriptionClient, 0); - }); + it("SQL rule filter using OR operator ", async function(): Promise { + await addRules("SqlRuleWithOR", "color = 'blue' OR quantity = 10"); - it("SQL rule filter with action with custom properties", async function(): Promise { - await addRules("SqlRuleWithAction", "color='blue'", "SET priority = 'High'"); + await sendOrders(); + const dataLength = data.filter((x) => x.Color === "blue" || x.Quantity === 10).length; + await receiveOrders(subscriptionClient, dataLength); - await sendOrders(); - const dataLength = data.filter((x) => x.Color === "blue").length; - const receivedMsgs = await receiveOrders(subscriptionClient, dataLength); + await testPeekMsgsLength(subscriptionClient, 0); + }); - if (receivedMsgs[0].userProperties) { - should.equal( - receivedMsgs[0].userProperties.priority, - "High", - "Priority of the receivedMessage is different than expected" - ); - } else { - chai.assert.fail("Received message doesnt have user properties"); - } - await testPeekMsgsLength(subscriptionClient, 0); - }); + it("SQL rule filter with action with custom properties", async function(): Promise { + await addRules("SqlRuleWithAction", "color='blue'", "SET priority = 'High'"); + + await sendOrders(); + const dataLength = data.filter((x) => x.Color === "blue").length; + const receivedMsgs = await receiveOrders(subscriptionClient, dataLength); + + if (receivedMsgs[0].userProperties) { + should.equal( + receivedMsgs[0].userProperties.priority, + "High", + "Priority of the receivedMessage is different than expected" + ); + } else { + chai.assert.fail("Received message doesnt have user properties"); + } + await testPeekMsgsLength(subscriptionClient, 0); + }); - // Standard subscription : Update message properties in random order. - // Premium subscription : Update message properties only first time when you create new subscription. + // Standard subscription : Update message properties in random order. + // Premium subscription : Update message properties only first time when you create new subscription. - /* it("SQL rule filter with action with message properties", async function(): Promise { + /* it("SQL rule filter with action with message properties", async function(): Promise { await addRules("SqlRuleWithAction", "color='blue'", "SET sys.label = 'color blue'"); await sendOrders(); @@ -525,68 +505,69 @@ describe("Sql Filter - Send/Receive", function(): void { } await testPeekMsgsLength(subscriptionClient, 0); });*/ -}); - -describe("Correlation Filter - Send/Receive", function(): void { - beforeEach(async () => { - await beforeEachTest(TestClientType.TopicFilterTestSubscription); }); - afterEach(async () => { - await afterEachTest(); - }); + describe("Correlation Filter - Send/Receive", function(): void { + beforeEach(async () => { + await beforeEachTest(TestClientType.TopicFilterTestSubscription); + }); - it("Correlation filter on the message properties", async function(): Promise { - await addRules("CorrelationMsgPropertyRule", { - label: "red" + afterEach(async () => { + await afterEachTest(); }); - await sendOrders(); - const dataLength = data.filter((x) => x.Color === "red").length; - await receiveOrders(subscriptionClient, dataLength); + it("Correlation filter on the message properties", async function(): Promise { + await addRules("CorrelationMsgPropertyRule", { + label: "red" + }); - await testPeekMsgsLength(subscriptionClient, 0); - }); + await sendOrders(); + const dataLength = data.filter((x) => x.Color === "red").length; + await receiveOrders(subscriptionClient, dataLength); - it("Correlation filter on the custom properties", async function(): Promise { - await addRules("CorrelationCustomRule", { - userProperties: { - color: "red" - } + await testPeekMsgsLength(subscriptionClient, 0); }); - await sendOrders(); - const dataLength = data.filter((x) => x.Color === "red").length; - await receiveOrders(subscriptionClient, dataLength); - - await testPeekMsgsLength(subscriptionClient, 0); - }); - - it("Correlation filter with SQL action", async function(): Promise { - await addRules( - "CorrelationRuleWithAction", - { + it("Correlation filter on the custom properties", async function(): Promise { + await addRules("CorrelationCustomRule", { userProperties: { - color: "blue" + color: "red" } - }, - "SET priority = 'High'" - ); + }); - await sendOrders(); - const dataLength = data.filter((x) => x.Color === "blue").length; - const receivedMsgs = await receiveOrders(subscriptionClient, dataLength); + await sendOrders(); + const dataLength = data.filter((x) => x.Color === "red").length; + await receiveOrders(subscriptionClient, dataLength); - if (receivedMsgs[0].userProperties) { - should.equal( - receivedMsgs[0].userProperties.priority, - "High", - "Priority of the receivedMessage is different than expected" + await testPeekMsgsLength(subscriptionClient, 0); + }); + + it("Correlation filter with SQL action", async function(): Promise { + await addRules( + "CorrelationRuleWithAction", + { + userProperties: { + color: "blue" + } + }, + "SET priority = 'High'" ); - } else { - chai.assert.fail("Received message doesnt have user properties"); - } - await testPeekMsgsLength(subscriptionClient, 0); + await sendOrders(); + const dataLength = data.filter((x) => x.Color === "blue").length; + const receivedMsgs = await receiveOrders(subscriptionClient, dataLength); + + if (receivedMsgs[0].userProperties) { + should.equal( + receivedMsgs[0].userProperties.priority, + "High", + "Priority of the receivedMessage is different than expected" + ); + } else { + chai.assert.fail("Received message doesnt have user properties"); + } + + await testPeekMsgsLength(subscriptionClient, 0); + }); }); }); diff --git a/sdk/servicebus/service-bus/test/track2.samples.spec.ts b/sdk/servicebus/service-bus/test/track2.samples.spec.ts index 3ef212c2a1f4..de1590b1619a 100644 --- a/sdk/servicebus/service-bus/test/track2.samples.spec.ts +++ b/sdk/servicebus/service-bus/test/track2.samples.spec.ts @@ -1,603 +1,412 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -import { - SessionConnections, - ReceivedMessage, - ContextWithSettlement as ContextWithSettlementMethods, - QueueAuth, - SubscriptionAuth, - isQueueAuth -} from "../src/models"; -import { - ServiceBusReceiverClient, - NonSessionReceiver, - SessionReceiver -} from "../src/serviceBusReceiverClient"; -import { ServiceBusSenderClient, delay, SendableMessageInfo } from "../src"; -import { EnvVarNames, getEnvVars } from "./utils/envVarUtils"; -import { EntityNames } from "./utils/testUtils"; +import { ReceivedMessage, ContextWithSettlement } from "../src/models"; +import { delay, SendableMessageInfo } from "../src"; +import { TestClientType } from "./utils/testUtils"; import chai from "chai"; import chaiAsPromised from "chai-as-promised"; import { createConnectionContext, getEntityNameFromConnectionString } from "../src/constructorHelpers"; +import { createServiceBusClientForTests, ServiceBusClientForTests } from "./utils/testutils2"; +import { Sender } from "../src/sender"; chai.use(chaiAsPromised); const assert = chai.assert; describe("Sample scenarios for track 2", () => { - let senderClient: ServiceBusSenderClient | undefined; - let closeables: { close(): Promise }[]; - const connectionString = getEnvVars()[EnvVarNames.SERVICEBUS_CONNECTION_STRING]!; + let serviceBusClient: ServiceBusClientForTests; before(async () => { - assert.ok( - connectionString, - `${EnvVarNames.SERVICEBUS_CONNECTION_STRING} needs to be set in the environment` - ); - - const nonSessionPurges = [ - { - connectionString: connectionString, - queueName: EntityNames.QUEUE_NAME_NO_PARTITION - }, - { - connectionString: connectionString, - topicName: EntityNames.TOPIC_NAME_NO_PARTITION, - subscriptionName: EntityNames.SUBSCRIPTION_NAME_NO_PARTITION - } - ].map((auth) => purge(auth)); - - const sessionPurge = purge( - { - connectionString: connectionString, - queueName: EntityNames.QUEUE_NAME_NO_PARTITION_SESSION - }, - "my-session" - ); - - await Promise.all([...nonSessionPurges, sessionPurge]); + serviceBusClient = createServiceBusClientForTests(); }); - beforeEach(() => { - closeables = []; + after(() => { + return serviceBusClient.test.after(); }); - afterEach(async () => { - if (senderClient != null) { - closeables.push(senderClient); - senderClient = undefined; - } + describe("queues (no sessions)", async () => { + let queueName: string; + let sender: Sender; - await Promise.all(closeables.map((closable) => closable.close())); - }); - - it("Queue, peek/lock", async () => { - const receiverClient = new ServiceBusReceiverClient( - { - connectionString: connectionString, - queueName: EntityNames.QUEUE_NAME_NO_PARTITION - }, - "peekLock" - ); - - closeables.push(receiverClient); - - senderClient = new ServiceBusSenderClient( - connectionString, - EntityNames.QUEUE_NAME_NO_PARTITION - ); - - await sendSampleMessage("Queue, peek/lock"); - - const errors: string[] = []; - const receivedBodies: string[] = []; - - receiverClient.subscribe({ - async processMessage( - message: ReceivedMessage, - context: ContextWithSettlementMethods - ): Promise { - await context.complete(message); - receivedBodies.push(message.body); - }, - async processError(err: Error): Promise { - errors.push(err.message); - } + before(async () => { + const { queue } = await serviceBusClient.test.createTestEntities( + TestClientType.UnpartitionedQueue + ); + queueName = queue!; }); - await waitAndValidate("Queue, peek/lock", receivedBodies, errors, receiverClient); - }); - - it("Queue, peek/lock, receiveBatch", async () => { - const receiverClient = new ServiceBusReceiverClient( - { - connectionString: connectionString, - queueName: EntityNames.QUEUE_NAME_NO_PARTITION - }, - "receiveAndDelete" - ); - - closeables.push(receiverClient); + beforeEach(() => { + sender = serviceBusClient.test.addToCleanup(serviceBusClient.getSender(queueName)); + }); - senderClient = new ServiceBusSenderClient( - connectionString, - EntityNames.QUEUE_NAME_NO_PARTITION - ); + afterEach(async () => { + return serviceBusClient.test.afterEach(); + }); - await sendSampleMessage("Queue, peek/lock, receiveBatch"); + it("Queue, peek/lock", async () => { + const receiver = serviceBusClient.test.addToCleanup( + serviceBusClient.getReceiver(queueName, "peekLock") + ); + + await sendSampleMessage(sender, "Queue, peek/lock"); + + const errors: string[] = []; + const receivedBodies: string[] = []; + + receiver.subscribe({ + async processMessage( + message: ReceivedMessage, + context: ContextWithSettlement + ): Promise { + await context.complete(message); + receivedBodies.push(message.body); + }, + async processError(err: Error): Promise { + errors.push(err.message); + } + }); - const receivedBodies: string[] = []; + await waitAndValidate("Queue, peek/lock", receivedBodies, errors, receiver); + }); - for (const message of (await receiverClient.receiveBatch(1, 5)).messages) { - receivedBodies.push(message.body); - } + it("Queue, peek/lock, receiveBatch", async () => { + const receiver = serviceBusClient.test.addToCleanup( + serviceBusClient.getReceiver(queueName, "receiveAndDelete") + ); - // TODO: this isn't the greatest re-use... - await waitAndValidate("Queue, peek/lock, receiveBatch", receivedBodies, [], receiverClient); - }); + await sendSampleMessage(sender, "Queue, peek/lock, receiveBatch"); - it("Queue, peek/lock, iterate messages", async () => { - const receiverClient = new ServiceBusReceiverClient( - { - connectionString: connectionString, - queueName: EntityNames.QUEUE_NAME_NO_PARTITION - }, - "peekLock" - ); - - senderClient = new ServiceBusSenderClient( - connectionString, - EntityNames.QUEUE_NAME_NO_PARTITION - ); - - await sendSampleMessage("Queue, peek/lock, iterate messages"); - - closeables.push(receiverClient); - - // etc... - // receiverClient.getRules(); - const errors: string[] = []; - const receivedBodies: string[] = []; - - // TODO: error handling? Does the iterate just terminate? - for await (const { message, context } of receiverClient.iterateMessages()) { - if (message == null) { - // user has the option of handling "no messages arrived by the maximum wait time" - console.log(`No message arrived within our max wait time`); - continue; - } + const receivedBodies: string[] = []; - try { - await context.complete(message); + for (const message of (await receiver.receiveBatch(1, 5)).messages) { receivedBodies.push(message.body); - break; - } catch (err) { - await context.abandon(message); - throw err; } - } - - await waitAndValidate( - "Queue, peek/lock, iterate messages", - receivedBodies, - errors, - receiverClient - ); - }); - - it("Queue, receive and delete", async () => { - const receiverClient = new ServiceBusReceiverClient( - { - connectionString: connectionString, - queueName: EntityNames.QUEUE_NAME_NO_PARTITION - }, - "receiveAndDelete" - ); - - closeables.push(receiverClient); - - senderClient = new ServiceBusSenderClient( - connectionString, - EntityNames.QUEUE_NAME_NO_PARTITION - ); - await sendSampleMessage("Queue, receiveAndDelete"); - - const errors: string[] = []; - const receivedBodies: string[] = []; + // TODO: this isn't the greatest re-use... + await waitAndValidate("Queue, peek/lock, receiveBatch", receivedBodies, [], receiver); + }); - receiverClient.subscribe({ - async processMessage(message: ReceivedMessage, context: {}): Promise { - receivedBodies.push(message.body); - }, - async processError(err: Error): Promise { - errors.push(err.message); + it("Queue, peek/lock, iterate messages", async () => { + const receiver = serviceBusClient.test.addToCleanup( + serviceBusClient.getReceiver(queueName, "peekLock") + ); + + await sendSampleMessage(sender, "Queue, peek/lock, iterate messages"); + + // etc... + // receiverClient.getRules(); + const errors: string[] = []; + const receivedBodies: string[] = []; + + // TODO: error handling? Does the iterate just terminate? + for await (const { message, context } of receiver.getMessageIterator()) { + if (message == null) { + // user has the option of handling "no messages arrived by the maximum wait time" + console.log(`No message arrived within our max wait time`); + continue; + } + + try { + await context.complete(message); + receivedBodies.push(message.body); + break; + } catch (err) { + await context.abandon(message); + throw err; + } } + + await waitAndValidate("Queue, peek/lock, iterate messages", receivedBodies, errors, receiver); }); - await waitAndValidate("Queue, receiveAndDelete", receivedBodies, errors, receiverClient); - }); + it("Queue, receive and delete", async () => { + const receiver = serviceBusClient.test.addToCleanup( + serviceBusClient.getReceiver(queueName, "receiveAndDelete") + ); - it("Queue, receive and delete, iterate messages", async () => { - const receiverClient = new ServiceBusReceiverClient( - { - connectionString: connectionString, - queueName: EntityNames.QUEUE_NAME_NO_PARTITION - }, - "receiveAndDelete" - ); - - senderClient = new ServiceBusSenderClient( - connectionString, - EntityNames.QUEUE_NAME_NO_PARTITION - ); - - await sendSampleMessage("Queue, peek/lock, iterate messages"); - - closeables.push(receiverClient); - - // etc... - // receiverClient.getRules(); - const errors: string[] = []; - const receivedBodies: string[] = []; - - // TODO: error handling? Does the iterate just terminate? - for await (const { message, context } of receiverClient.iterateMessages()) { - assert.notOk((context as any).complete); - - if (message == null) { - // user has the option of handling "no messages arrived by the maximum wait time" - console.log(`No message arrived within our max wait time`); - continue; - } + await sendSampleMessage(sender, "Queue, receiveAndDelete"); - try { - receivedBodies.push(message.body); - break; - } catch (err) { - throw err; - } - } + const errors: string[] = []; + const receivedBodies: string[] = []; - await waitAndValidate( - "Queue, peek/lock, iterate messages", - receivedBodies, - errors, - receiverClient - ); - }); + receiver.subscribe({ + async processMessage(message: ReceivedMessage): Promise { + receivedBodies.push(message.body); + }, + async processError(err: Error): Promise { + errors.push(err.message); + } + }); - it("Queue, peek/lock, iterate messages", async () => { - const receiverClient = new ServiceBusReceiverClient( - { - connectionString: connectionString, - queueName: EntityNames.QUEUE_NAME_NO_PARTITION - }, - "peekLock" - ); - - senderClient = new ServiceBusSenderClient( - connectionString, - EntityNames.QUEUE_NAME_NO_PARTITION - ); - - await sendSampleMessage("Queue, peek/lock, iterate messages"); - - closeables.push(receiverClient); - - // etc... - // receiverClient.getRules(); - const errors: string[] = []; - const receivedBodies: string[] = []; - - // TODO: error handling? Does the iterate just terminate? - for await (const { message, context } of receiverClient.iterateMessages()) { - if (message == null) { - // user has the option of handling "no messages arrived by the maximum wait time" - console.log(`No message arrived within our max wait time`); - continue; - } + await waitAndValidate("Queue, receiveAndDelete", receivedBodies, errors, receiver); + }); - try { - await context.complete(message); - receivedBodies.push(message.body); - break; - } catch (err) { - await context.abandon(message); - throw err; + it("Queue, receive and delete, iterate messages", async () => { + const receiver = serviceBusClient.test.addToCleanup( + serviceBusClient.getReceiver(queueName, "receiveAndDelete") + ); + + await sendSampleMessage(sender, "Queue, receive and delete, iterate messages"); + + // etc... + // receiverClient.getRules(); + const errors: string[] = []; + const receivedBodies: string[] = []; + + // TODO: error handling? Does the iterate just terminate? + for await (const { message, context } of receiver.getMessageIterator()) { + assert.notOk((context as any).complete); + + if (message == null) { + // user has the option of handling "no messages arrived by the maximum wait time" + console.log(`No message arrived within our max wait time`); + continue; + } + + try { + receivedBodies.push(message.body); + break; + } catch (err) { + throw err; + } } - } - - await waitAndValidate( - "Queue, peek/lock, iterate messages", - receivedBodies, - errors, - receiverClient - ); - }); - it("Subscription, peek/lock", async () => { - const receiverClient = new ServiceBusReceiverClient( - { - connectionString: connectionString, - topicName: EntityNames.TOPIC_NAME_NO_PARTITION, - subscriptionName: EntityNames.SUBSCRIPTION_NAME_NO_PARTITION - }, - "peekLock" - ); - - senderClient = new ServiceBusSenderClient( - connectionString, - EntityNames.TOPIC_NAME_NO_PARTITION - ); - - await sendSampleMessage("Subscription, peek/lock"); - - closeables.push(receiverClient); - - // etc... - // receiverClient.getRules(); - const errors: string[] = []; - const receivedBodies: string[] = []; - - receiverClient.subscribe({ - async processMessage( - message: ReceivedMessage, - context: ContextWithSettlementMethods - ): Promise { - await context.complete(message); - receivedBodies.push(message.body); - }, - async processError(err: Error): Promise { - errors.push(err.message); - } + await waitAndValidate( + "Queue, receive and delete, iterate messages", + receivedBodies, + errors, + receiver + ); }); - - await waitAndValidate("Subscription, peek/lock", receivedBodies, errors, receiverClient); }); - it("Subscription, receive and delete", async () => { - const receiverClient = new ServiceBusReceiverClient( - { - connectionString: connectionString, - topicName: EntityNames.TOPIC_NAME_NO_PARTITION, - subscriptionName: EntityNames.SUBSCRIPTION_NAME_NO_PARTITION - }, - "receiveAndDelete" - ); + describe("subscriptions (no sessions)", () => { + let sender: Sender; + let topic: string; + let subscription: string; - senderClient = new ServiceBusSenderClient( - connectionString, - EntityNames.TOPIC_NAME_NO_PARTITION - ); + before(async () => { + const entity = await serviceBusClient.test.createTestEntities( + TestClientType.UnpartitionedSubscription + ); - await sendSampleMessage("Subscription, receive and delete"); - - closeables.push(receiverClient); + topic = entity.topic!; + subscription = entity.subscription!; + }); - // etc... - // receiverClient.getRules(); - const errors: string[] = []; - const receivedBodies: string[] = []; + beforeEach(() => { + sender = serviceBusClient.test.addToCleanup(serviceBusClient.getSender(topic)); + }); - receiverClient.subscribe({ - async processMessage(message: ReceivedMessage, context: {}): Promise { - receivedBodies.push(message.body); - }, - async processError(err: Error): Promise { - errors.push(err.message); - } + afterEach(async () => { + return serviceBusClient.test.afterEach(); }); - await waitAndValidate( - "Subscription, receive and delete", - receivedBodies, - errors, - receiverClient - ); - }); + it("Subscription, peek/lock", async () => { + const receiver = serviceBusClient.test.addToCleanup( + serviceBusClient.getReceiver(topic, subscription, "peekLock") + ); + + await sendSampleMessage(sender, "Subscription, peek/lock"); + + // etc... + // receiverClient.getRules(); + const errors: string[] = []; + const receivedBodies: string[] = []; + + receiver.subscribe({ + async processMessage( + message: ReceivedMessage, + context: ContextWithSettlement + ): Promise { + await context.complete(message); + receivedBodies.push(message.body); + }, + async processError(err: Error): Promise { + errors.push(err.message); + } + }); - it("Subscription, peek/lock, iterate messages", async () => { - const receiverClient = new ServiceBusReceiverClient( - { - connectionString: connectionString, - topicName: EntityNames.TOPIC_NAME_NO_PARTITION, - subscriptionName: EntityNames.SUBSCRIPTION_NAME_NO_PARTITION - }, - "peekLock" - ); - - closeables.push(receiverClient); - - senderClient = new ServiceBusSenderClient( - connectionString, - EntityNames.TOPIC_NAME_NO_PARTITION - ); - - await sendSampleMessage("Subscription, peek/lock, iterate messages"); - - // etc... - // receiverClient.getRules(); - const errors: string[] = []; - const receivedBodies: string[] = []; - - // TODO: error handling? Does the iterate just terminate? - for await (const { message, context } of receiverClient.iterateMessages()) { - if (message == null) { - // user has the option of handling "no messages arrived by the maximum wait time" - console.log(`No message arrived within our max wait time`); - continue; - } + await waitAndValidate("Subscription, peek/lock", receivedBodies, errors, receiver); + }); - try { - await context.complete(message); - receivedBodies.push(message.body); - break; - } catch (err) { - await context.abandon(message); - throw err; - } - } + it("Subscription, receive and delete", async () => { + const receiver = serviceBusClient.test.addToCleanup( + serviceBusClient.getReceiver(topic, subscription, "receiveAndDelete") + ); + + await sendSampleMessage(sender, "Subscription, receive and delete"); + + // etc... + // receiverClient.getRules(); + const errors: string[] = []; + const receivedBodies: string[] = []; + + receiver.subscribe({ + async processMessage(message: ReceivedMessage): Promise { + receivedBodies.push(message.body); + }, + async processError(err: Error): Promise { + errors.push(err.message); + } + }); - await waitAndValidate( - "Subscription, peek/lock, iterate messages", - receivedBodies, - errors, - receiverClient - ); - }); + await waitAndValidate("Subscription, receive and delete", receivedBodies, errors, receiver); + }); - it("Subscription, receive and delete, iterate messages", async () => { - const receiverClient = new ServiceBusReceiverClient( - { - connectionString: connectionString, - topicName: EntityNames.TOPIC_NAME_NO_PARTITION, - subscriptionName: EntityNames.SUBSCRIPTION_NAME_NO_PARTITION - }, - "receiveAndDelete" - ); - - closeables.push(receiverClient); - - senderClient = new ServiceBusSenderClient( - connectionString, - EntityNames.TOPIC_NAME_NO_PARTITION - ); - - await sendSampleMessage("Subscription, receive and delete, iterate messages"); - - // etc... - // receiverClient.getRules(); - const errors: string[] = []; - const receivedBodies: string[] = []; - - // TODO: error handling? Does the iterate just terminate? - for await (const { message, context } of receiverClient.iterateMessages()) { - assert.notOk((context as any).complete); - - if (message == null) { - // user has the option of handling "no messages arrived by the maximum wait time" - console.log(`No message arrived within our max wait time`); - continue; + it("Subscription, peek/lock, iterate messages", async () => { + const receiver = serviceBusClient.test.addToCleanup( + serviceBusClient.getReceiver(topic, subscription, "peekLock") + ); + + await sendSampleMessage(sender, "Subscription, peek/lock, iterate messages"); + + // etc... + // receiverClient.getRules(); + const errors: string[] = []; + const receivedBodies: string[] = []; + + // TODO: error handling? Does the iterate just terminate? + for await (const { message, context } of receiver.getMessageIterator()) { + if (message == null) { + // user has the option of handling "no messages arrived by the maximum wait time" + console.log(`No message arrived within our max wait time`); + continue; + } + + try { + await context.complete(message); + receivedBodies.push(message.body); + break; + } catch (err) { + await context.abandon(message); + throw err; + } } - try { - receivedBodies.push(message.body); - break; - } catch (err) { - throw err; + await waitAndValidate( + "Subscription, peek/lock, iterate messages", + receivedBodies, + errors, + receiver + ); + }); + + it("Subscription, receive and delete, iterate messages", async () => { + const receiver = serviceBusClient.test.addToCleanup( + serviceBusClient.getReceiver(topic, subscription, "receiveAndDelete") + ); + + await sendSampleMessage(sender, "Subscription, receive and delete, iterate messages"); + + // etc... + // receiverClient.getRules(); + const errors: string[] = []; + const receivedBodies: string[] = []; + + // TODO: error handling? Does the iterate just terminate? + for await (const { message, context } of receiver.getMessageIterator()) { + assert.notOk((context as any).complete); + + if (message == null) { + // user has the option of handling "no messages arrived by the maximum wait time" + console.log(`No message arrived within our max wait time`); + continue; + } + + try { + receivedBodies.push(message.body); + break; + } catch (err) { + throw err; + } } - } - await waitAndValidate( - "Subscription, receive and delete, iterate messages", - receivedBodies, - errors, - receiverClient - ); + await waitAndValidate( + "Subscription, receive and delete, iterate messages", + receivedBodies, + errors, + receiver + ); + }); }); - it("Queue, receive and delete, sessions", async () => { - const sessionConnections = new SessionConnections(); - - const receiverClient = new ServiceBusReceiverClient( - { connectionString, queueName: EntityNames.QUEUE_NAME_NO_PARTITION_SESSION }, - "receiveAndDelete", - { - id: "my-session", - // the thinking is that users will (unlike queues or topics) open up - // lots of individual sessions, so keeping track of and sharing connections - // is a way to prevent a possible port/connection explosion. - connections: sessionConnections - } - ); + describe("queues (with sessions)", () => { + let sender: Sender; + let queue: string; - closeables.push(receiverClient); + before(async () => { + const entities = await serviceBusClient.test.createTestEntities( + TestClientType.UnpartitionedQueueWithSessions + ); + queue = entities.queue!; + sender = serviceBusClient.test.addToCleanup(serviceBusClient.getSender(queue)); + }); - senderClient = new ServiceBusSenderClient( - connectionString, - EntityNames.QUEUE_NAME_NO_PARTITION_SESSION - ); + it("Queue, receive and delete, sessions", async () => { + const sessionId = Date.now().toString(); + const receiver = serviceBusClient.test.addToCleanup( + serviceBusClient.getSessionReceiver(queue, "receiveAndDelete", { sessionId }) + ); - sendSampleMessage("Queue, receive and delete, sessions", "my-session"); + sendSampleMessage(sender, "Queue, receive and delete, sessions", sessionId); - // note that this method is now available - only shows up in auto-complete - // if you construct this object with a session. - await receiverClient.renewSessionLock(); + // note that this method is now available - only shows up in auto-complete + // if you construct this object with a session. + await receiver.renewSessionLock(); - const errors: string[] = []; - const receivedBodies: string[] = []; + const errors: string[] = []; + const receivedBodies: string[] = []; - receiverClient.subscribe({ - async processMessage(message: ReceivedMessage, context: {}): Promise { - receivedBodies.push(message.body); - }, - async processError(err: Error): Promise { - errors.push(err.message); - } - }); + receiver.subscribe({ + async processMessage(message: ReceivedMessage): Promise { + receivedBodies.push(message.body); + }, + async processError(err: Error): Promise { + errors.push(err.message); + } + }); - await waitAndValidate( - "Queue, receive and delete, sessions", - receivedBodies, - errors, - receiverClient - ); - }); + await waitAndValidate( + "Queue, receive and delete, sessions", + receivedBodies, + errors, + receiver + ); + }); - it("Queue, peek/lock, sessions", async () => { - const sessionConnections = new SessionConnections(); - - const receiverClient = new ServiceBusReceiverClient( - { - connectionString: connectionString, - queueName: EntityNames.QUEUE_NAME_NO_PARTITION_SESSION - }, - "peekLock", - { - id: "my-session", - // the thinking is that users will (unlike queues or topics) open up - // lots of individual sessions, so keeping track of and sharing connections - // is a way to prevent a possible port/connection explosion. - connections: sessionConnections - } - ); + it("Queue, peek/lock, sessions", async () => { + const sessionId = Date.now().toString(); - closeables.push(receiverClient); + const receiver = serviceBusClient.test.addToCleanup( + serviceBusClient.getSessionReceiver(queue, "peekLock", { sessionId }) + ); - senderClient = new ServiceBusSenderClient( - connectionString, - EntityNames.QUEUE_NAME_NO_PARTITION_SESSION - ); + sendSampleMessage(sender, "Queue, peek/lock, sessions", sessionId); - sendSampleMessage("Queue, peek/lock, sessions", "my-session"); + // note that this method is now available - only shows up in auto-complete + // if you construct this object with a session. + await receiver.renewSessionLock(); - // note that this method is now available - only shows up in auto-complete - // if you construct this object with a session. - await receiverClient.renewSessionLock(); + const errors: string[] = []; + const receivedBodies: string[] = []; - const errors: string[] = []; - const receivedBodies: string[] = []; + receiver.subscribe({ + async processMessage(message: ReceivedMessage): Promise { + receivedBodies.push(message.body); + }, + async processError(err: Error): Promise { + errors.push(err.message); + } + }); - receiverClient.subscribe({ - async processMessage(message: ReceivedMessage, context: {}): Promise { - receivedBodies.push(message.body); - }, - async processError(err: Error): Promise { - errors.push(err.message); - } + await waitAndValidate("Queue, peek/lock, sessions", receivedBodies, errors, receiver); }); - - await waitAndValidate("Queue, peek/lock, sessions", receivedBodies, errors, receiverClient); }); - async function sendSampleMessage(body: string, sessionId?: string) { - if (senderClient == null) { - throw new Error("Can't send a sample message w/o a client"); - } - + async function sendSampleMessage(senderClient: Sender, body: string, sessionId?: string) { const message: SendableMessageInfo = { body }; @@ -622,66 +431,6 @@ describe("ConstructorHelpers for track 2", () => { sentinel: "test token credential" }; - it("createConnectionContext for queues", () => { - const queueAuths: QueueAuth[] = [ - { connectionString: entityConnectionString, queueName: "myentity" }, - { connectionString: serviceBusConnectionString, queueName: "myentity" }, - { queueConnectionString: entityConnectionString }, - { tokenCredential: fakeTokenCredential, host: "ahost", queueName: "myentity" } - ]; - - for (const queueAuth of queueAuths) { - const contextAndEntityPath = createConnectionContext(queueAuth, {}); - assert.equal("myentity", contextAndEntityPath.entityPath); - - if ((queueAuth as any).tokenCredential) { - assert.equal( - "test token credential", - (contextAndEntityPath.context.tokenCredential as any).sentinel - ); - } else { - assert.equal( - "SharedKeyCredential", - contextAndEntityPath.context.tokenCredential.constructor.name - ); - } - } - }); - - it("createConnectionContext for subscriptions", () => { - const subscriptionAuths: SubscriptionAuth[] = [ - { - connectionString: serviceBusConnectionString, - topicName: "myentity", - subscriptionName: "mysubscription" - }, - { topicConnectionString: entityConnectionString, subscriptionName: "mysubscription" }, - { - tokenCredential: fakeTokenCredential, - host: "ahost", - topicName: "myentity", - subscriptionName: "mysubscription" - } - ]; - - for (const subAuth of subscriptionAuths) { - const contextAndEntityPath = createConnectionContext(subAuth, {}); - assert.equal("myentity/Subscriptions/mysubscription", contextAndEntityPath.entityPath); - - if ((subAuth as any).tokenCredential) { - assert.equal( - "test token credential", - (contextAndEntityPath.context.tokenCredential as any).sentinel - ); - } else { - assert.equal( - "SharedKeyCredential", - contextAndEntityPath.context.tokenCredential.constructor.name - ); - } - } - }); - const badAuths = [ // missing required fields { connectionString: serviceBusConnectionString }, @@ -767,37 +516,3 @@ async function waitAndValidate( assert.isEmpty(remainingMessages); assert.deepEqual([expectedMessage], receivedBodies); } - -async function purge(auth: QueueAuth | SubscriptionAuth, sessionId?: string): Promise { - let receiverClient: NonSessionReceiver<"receiveAndDelete"> | SessionReceiver<"receiveAndDelete">; - - if (sessionId) { - if (isQueueAuth(auth)) { - receiverClient = new ServiceBusReceiverClient(auth, "receiveAndDelete", { - id: sessionId, - connections: new SessionConnections() - }); - } else { - receiverClient = new ServiceBusReceiverClient(auth, "receiveAndDelete", { - id: sessionId, - connections: new SessionConnections() - }); - } - } else { - if (isQueueAuth(auth)) { - receiverClient = new ServiceBusReceiverClient(auth, "receiveAndDelete"); - } else { - receiverClient = new ServiceBusReceiverClient(auth, "receiveAndDelete"); - } - } - - while (true) { - const messages = await receiverClient.receiveBatch(10, 1); - - if (messages.messages.length === 0) { - break; - } - } - - await receiverClient.close(); -} diff --git a/sdk/servicebus/service-bus/test/utils/testUtils.ts b/sdk/servicebus/service-bus/test/utils/testUtils.ts index 913f1e92d419..7ffe85df2649 100644 --- a/sdk/servicebus/service-bus/test/utils/testUtils.ts +++ b/sdk/servicebus/service-bus/test/utils/testUtils.ts @@ -2,42 +2,10 @@ // Licensed under the MIT License. import chai from "chai"; -import { - SendableMessageInfo, - delay, - MessagingError, - ContextWithSettlement, - ReceivedMessage, - ServiceBusClientOptions, - Session -} from "../../src"; -import { EnvVarNames, getEnvVars } from "./envVarUtils"; -import { recreateQueue, recreateSubscription, recreateTopic } from "./managementUtils"; +import { SendableMessageInfo, delay, MessagingError, ReceivedMessage } from "../../src"; import * as dotenv from "dotenv"; -import { ServiceBusSenderClient } from "../../src/serviceBusSenderClient"; -import { - ServiceBusReceiverClient, - NonSessionReceiver, - SessionReceiver, - SubscriptionRuleManagement -} from "../../src/serviceBusReceiverClient"; dotenv.config(); -const defaultLockDuration = "PT30S"; // 30 seconds in ISO 8601 FORMAT - equivalent to "P0Y0M0DT0H0M30S" -const env = getEnvVars(); - -export type ReceiverClientTypeForUser = - | NonSessionReceiver<"peekLock" | "receiveAndDelete"> - | (NonSessionReceiver<"peekLock" | "receiveAndDelete"> & SubscriptionRuleManagement) - | SessionReceiver<"peekLock" | "receiveAndDelete"> - | (SessionReceiver<"peekLock" | "receiveAndDelete"> & SubscriptionRuleManagement); - -export type ReceiverClientTypeForUserT = - | NonSessionReceiver - | (NonSessionReceiver & SubscriptionRuleManagement) - | SessionReceiver - | (SessionReceiver & SubscriptionRuleManagement); - export class TestMessage { static sessionId: string = "my-session"; @@ -174,242 +142,6 @@ export enum TestClientType { TopicFilterTestSubscription } -export function isSessionfulEntity(clientType: TestClientType) { - return ( - clientType >= TestClientType.PartitionedQueueWithSessions && - clientType <= TestClientType.UnpartitionedSubscriptionWithSessions - ); -} - -async function manageResourcesAndCreateClients( - entity: { type: "queue" | "subscription" } & { - session: boolean; - partitioned: boolean; - }, - connectionString: string, - receiveMode: "peekLock" | "receiveAndDelete", - senderOptions?: ServiceBusClientOptions, - receiverOptions?: ServiceBusClientOptions & Session, - // If freshResource flag is false, sender/receiver clients are provided without creating a new resource - freshResource: boolean = true -): Promise<{ - senderClient: ServiceBusSenderClient; - receiverClient: ReceiverClientTypeForUser; -}> { - const prefix = entity.partitioned ? "partitioned-" : "unpartitioned-"; - const suffix = entity.session ? "-sessions" : ""; - let auth: any; - let entityName: string; - if (entity.type === "queue") { - const queueName = prefix + "queue" + suffix; - if (freshResource) { - await recreateQueue(queueName, { - lockDuration: defaultLockDuration, - enableBatchedOperations: true, - enablePartitioning: entity.partitioned, - requiresSession: entity.session - }); - } - auth = { - connectionString, - queueName: queueName - }; - entityName = queueName; - } else { - const topicName = prefix + "topic" + suffix; - const subscriptionName = prefix + "topic-subscription" + suffix; - if (freshResource) { - await recreateTopic(topicName, { - enablePartitioning: entity.partitioned, - enableBatchedOperations: true - }); - await recreateSubscription(topicName, subscriptionName, { - lockDuration: defaultLockDuration, - enableBatchedOperations: true, - requiresSession: entity.session - }); - } - auth = { - connectionString, - topicName: topicName, - subscriptionName: subscriptionName - }; - entityName = topicName; - } - - const returnReceiverClient = () => { - if (entity.session) { - if (receiveMode === "peekLock") { - return new ServiceBusReceiverClient( - auth, - receiveMode, - { - id: receiverOptions?.id, - maxSessionAutoRenewLockDurationInSeconds: - receiverOptions?.maxSessionAutoRenewLockDurationInSeconds - }, - receiverOptions - ); - } else { - return new ServiceBusReceiverClient( - auth, - receiveMode, - { - id: receiverOptions?.id, - maxSessionAutoRenewLockDurationInSeconds: - receiverOptions?.maxSessionAutoRenewLockDurationInSeconds - }, - receiverOptions - ); - } - } else { - if (receiveMode === "peekLock") { - return new ServiceBusReceiverClient(auth, receiveMode, receiverOptions); - } else { - return new ServiceBusReceiverClient(auth, receiveMode, receiverOptions); - } - } - }; - return { - senderClient: new ServiceBusSenderClient(connectionString, entityName, senderOptions), - receiverClient: returnReceiverClient() - }; -} - -export async function getSenderReceiverClients( - entityType: TestClientType, - receiveMode: "peekLock" | "receiveAndDelete", - senderOptions?: ServiceBusClientOptions, - receiverOptions?: ServiceBusClientOptions & Session, - // If freshResource flag is false, sender/receiver clients are provided without creating a new resource - freshResource: boolean = true -): Promise<{ - senderClient: ServiceBusSenderClient; - receiverClient: ReceiverClientTypeForUser; -}> { - const connectionString = env[EnvVarNames.SERVICEBUS_CONNECTION_STRING]; - let type: "queue" | "subscription"; - let partitioned: boolean; - let session: boolean; - switch (entityType) { - case TestClientType.PartitionedQueue: { - type = "queue"; - partitioned = true; - session = false; - break; - } - - case TestClientType.PartitionedSubscription: { - type = "subscription"; - partitioned = true; - session = false; - break; - } - - case TestClientType.UnpartitionedQueue: { - type = "queue"; - partitioned = false; - session = false; - break; - } - - case TestClientType.UnpartitionedSubscription: - case TestClientType.TopicFilterTestDefaultSubscription: - case TestClientType.TopicFilterTestSubscription: { - type = "subscription"; - partitioned = false; - session = false; - break; - } - - case TestClientType.PartitionedQueueWithSessions: { - type = "queue"; - partitioned = true; - session = true; - break; - } - - case TestClientType.PartitionedSubscriptionWithSessions: { - type = "subscription"; - partitioned = true; - session = true; - break; - } - - case TestClientType.UnpartitionedQueueWithSessions: { - type = "queue"; - partitioned = false; - session = true; - break; - } - - case TestClientType.UnpartitionedSubscriptionWithSessions: { - type = "subscription"; - partitioned = false; - session = true; - break; - } - - default: - type = "queue"; - partitioned = false; - session = false; - break; - } - - return manageResourcesAndCreateClients( - { - type: type, - partitioned: partitioned, - session: session - }, - connectionString, - receiveMode, - senderOptions, - receiverOptions, - freshResource - ); -} - -/** - * Purges the content in the Queue/Subscription corresponding to the receiverClient - * @param receiverClient - */ -export async function purge(receiverClient: ReceiverClientTypeForUser): Promise { - let isEmpty = false; - - while (!isEmpty) { - const peekedMsgs = await receiverClient.diagnostics.peek(10); - if (peekedMsgs.length === 0) { - isEmpty = true; - } else { - const msgsAndContext = await receiverClient.receiveBatch(peekedMsgs.length); - for (let index = 0; index < msgsAndContext.messages.length; index++) { - if (msgsAndContext.messages[index]) { - await (msgsAndContext.context as ContextWithSettlement).complete( - msgsAndContext.messages[index] - ); - } - } - } - } -} - -/** - * Purges the content in the Queue/Subscription corresponding to the entityType: TestClientType - * @param receiverClient - */ -export async function purgeEntity( - entityType: TestClientType, - sessionId: string | undefined = undefined -): Promise { - const newReceiverClient = ( - await getSenderReceiverClients(entityType, "peekLock", undefined, { id: sessionId }, false) - ).receiverClient; - await purge(newReceiverClient); - await newReceiverClient.close(); -} - /** * Maximum wait duration for the expected event to happen = `10000 ms`(default value is 10 seconds)(= maxWaitTimeInMilliseconds) * Keep checking whether the predicate is true after every `1000 ms`(default value is 1 second) (= delayBetweenRetriesInMilliseconds) diff --git a/sdk/servicebus/service-bus/test/utils/testutils2.ts b/sdk/servicebus/service-bus/test/utils/testutils2.ts new file mode 100644 index 000000000000..5ab36b34bf03 --- /dev/null +++ b/sdk/servicebus/service-bus/test/utils/testutils2.ts @@ -0,0 +1,365 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +import { TestClientType, TestMessage } from "./testUtils"; +import { getEnvVars, EnvVarNames } from "./envVarUtils"; +import * as dotenv from "dotenv"; +import { recreateQueue, recreateTopic, recreateSubscription } from "./managementUtils"; +import { SessionReceiver } from "../../src/receivers/sessionReceiver"; +import { Receiver, SubscriptionRuleManagement } from "../../src/receivers/receiver"; +import { ServiceBusClient } from "../../src/serviceBusClient"; +import { ServiceBusClientOptions, ContextWithSettlement } from "../../src"; +import chai from "chai"; +import { GetSessionReceiverOptions } from "../../src/models"; + +dotenv.config(); +const env = getEnvVars(); +const should = chai.should(); + +const defaultLockDuration = "PT30S"; // 30 seconds in ISO 8601 FORMAT - equivalent to "P0Y0M0DT0H0M30S" + +function getEntityNames( + testClientType: TestClientType +): { + queue?: string; + topic?: string; + subscription?: string; + usesSessions: boolean; + isPartitioned: boolean; +} { + const name = TestClientType[testClientType]; + let prefix = ""; + let isPartitioned = false; + + if (name.indexOf("Partitioned") !== -1) { + prefix = "partitioned-"; + isPartitioned = true; + } else if (name.indexOf("Unpartitioned") !== -1) { + prefix = "unpartitioned-"; + } else { + // there are some topic/rule based ones that don't care. + prefix = "unpartitioned-"; + } + + let suffix = ""; + let usesSessions = false; + + if (name.endsWith("WithSessions")) { + suffix = "-sessions"; + usesSessions = true; + } + + if (name.indexOf("Queue") !== -1) { + return { + queue: prefix + "queue" + suffix, + usesSessions, + isPartitioned + }; + } else if (name.indexOf("Subscription") !== -1) { + return { + topic: prefix + "topic" + suffix, + subscription: prefix + "topic-subscription" + suffix, + usesSessions, + isPartitioned + }; + } else if (name.indexOf("Topic") !== -1) { + return { + topic: prefix + "topic" + suffix, + usesSessions, + isPartitioned + }; + } else { + throw new Error(`No idea what the entity type is for ${name}`); + } +} + +async function createTestEntities( + testClientType: TestClientType +): Promise> { + const relatedEntities = getEntityNames(testClientType); + + if (relatedEntities.queue) { + await recreateQueue(relatedEntities.queue, { + lockDuration: defaultLockDuration, + enableBatchedOperations: true, + enablePartitioning: relatedEntities.isPartitioned, + requiresSession: relatedEntities.usesSessions + }); + } + + if (relatedEntities.topic) { + await recreateTopic(relatedEntities.topic, { + enablePartitioning: relatedEntities.isPartitioned, + enableBatchedOperations: true + }); + } + + if (relatedEntities.topic && relatedEntities.subscription) { + await recreateSubscription(relatedEntities.topic, relatedEntities.subscription, { + lockDuration: defaultLockDuration, + enableBatchedOperations: true, + requiresSession: relatedEntities.usesSessions + }); + } + + return relatedEntities; +} + +export async function drainAllMessages(receiver: Receiver<{}>): Promise { + while (true) { + const messages = await receiver.receiveBatch(10, 1); + + if (messages.messages.length === 0) { + break; + } + } + + await receiver.close(); +} + +export interface ServiceBusClientForTests extends ServiceBusClient { + test: ServiceBusTestHelpers; +} + +export class ServiceBusTestHelpers { + constructor(private _serviceBusClient: ServiceBusClient) {} + + addToCleanup }>(v: T): T { + this._closeables.push(v); + return v; + } + + async afterEach(): Promise { + const closePromises = this._closeables.map((c) => c.close()); + this._closeables.length = 0; + await Promise.all(closePromises); + } + + async after(): Promise { + // TODO: purge any of the dynamically created entities created in `createTestEntities` + await this._serviceBusClient.close(); + } + + async purgeForClientType(...testClientTypes: TestClientType[]): Promise { + await Promise.all( + testClientTypes.map((tct) => purgeForTestClientType(this._serviceBusClient, tct)) + ); + } + + async createTestEntities( + testClientType: TestClientType + ): Promise> { + // TODO: for now these aren't randomly named. This is prep so we can + // do that soon. + let entityValues = this._testClientEntities.get(testClientType); + + if (entityValues == null) { + entityValues = await createTestEntities(testClientType); + this._testClientEntities.set(testClientType, entityValues); + } + + return entityValues; + } + + getTestEntities(testClientType: TestClientType): ReturnType { + const entityValues = this._testClientEntities.get(testClientType); + + if (entityValues == null) { + throw new Error(`createTestEntities was never called for ${testClientType}`); + } + + return entityValues; + } + + /** + * Gets a peek/lock receiver for the specified `TestClientType` + * NOTE: the underlying receiver may be a `SessionReceiverImpl`. It will target `TestMessage.sessionId`. + * + * The receiver created by this method will be cleaned up by `afterEach()` + */ + getPeekLockReceiver( + entityNames: ReturnType + ): Receiver { + try { + // if you're creating a receiver this way then you'll just use the default + // session ID for your receiver. + // if you want to get more specific use the `getPeekLockSessionReceiver` method + // instead. + return this.getSessionPeekLockReceiver(entityNames, { + sessionId: TestMessage.sessionId + }); + } catch (err) { + if (!(err instanceof TypeError)) { + throw err; + } + } + + return this.addToCleanup( + entityNames.queue + ? this._serviceBusClient.getReceiver(entityNames.queue, "peekLock") + : this._serviceBusClient.getReceiver( + entityNames.topic!, + entityNames.subscription!, + "peekLock" + ) + ); + } + + getSubscriptionPeekLockReceiver( + entityNames: ReturnType + ): Receiver & SubscriptionRuleManagement { + if (entityNames.topic == null || entityNames.subscription == null) { + throw new TypeError("Not subscription entity - can't create a subscription receiver for it"); + } + + return this.addToCleanup( + this._serviceBusClient.getReceiver(entityNames.topic!, entityNames.subscription!, "peekLock") + ); + } + + getSessionPeekLockReceiver( + entityNames: ReturnType, + getSessionReceiverOptions?: GetSessionReceiverOptions + ): SessionReceiver { + if (!entityNames.usesSessions) { + throw new TypeError( + "Not a session-full entity - can't create a session receiver type for it" + ); + } + + return this.addToCleanup( + entityNames.queue + ? this._serviceBusClient.getSessionReceiver( + entityNames.queue, + "peekLock", + getSessionReceiverOptions + ) + : this._serviceBusClient.getSessionReceiver( + entityNames.topic!, + entityNames.subscription!, + "peekLock", + getSessionReceiverOptions + ) + ); + } + + /** + * Gets a receiveAndDelete receiver for the specified `TestClientType` + * NOTE: the underlying receiver may be a `SessionReceiverImpl` + * + * The receiver created by this method will be cleaned up by `afterEach()` + */ + getReceiveAndDeleteReceiver(entityNames: ReturnType): Receiver<{}> { + // TODO: we should generate a random ID here - there's no harm in + // creating as many sessions as we wish. Some tests will need to change. + const sessionId = TestMessage.sessionId; + + if (entityNames.usesSessions) { + return this.addToCleanup( + entityNames.queue + ? this._serviceBusClient.getSessionReceiver(entityNames.queue, "receiveAndDelete", { + sessionId + }) + : this._serviceBusClient.getSessionReceiver( + entityNames.topic!, + entityNames.subscription!, + "receiveAndDelete", + { + sessionId + } + ) + ); + } else { + return this.addToCleanup( + entityNames.queue + ? this._serviceBusClient.getReceiver(entityNames.queue, "receiveAndDelete") + : this._serviceBusClient.getReceiver( + entityNames.topic!, + entityNames.subscription!, + "receiveAndDelete" + ) + ); + } + } + + private _closeables: { close(): Promise }[] = []; + private _testClientEntities: Map> = new Map(); +} + +async function purgeForTestClientType( + serviceBusClient: ServiceBusClient, + testClientType: TestClientType +): Promise { + let receiver: Receiver<{}> | SessionReceiver<{}> | undefined; + let entityPaths = getEntityNames(testClientType); + + if (entityPaths.queue) { + receiver = serviceBusClient.getReceiver(entityPaths.queue, "receiveAndDelete"); + } else if (entityPaths.topic && entityPaths.subscription) { + receiver = serviceBusClient.getReceiver( + entityPaths.topic, + entityPaths.subscription, + "receiveAndDelete" + ); + } + + if (receiver == null) { + throw new Error(`Unsupported TestClientType for purge: ${testClientType}`); + } + + await Promise.all([ + drainReceiveAndDeleteReceiver(receiver), + drainReceiveAndDeleteReceiver( + serviceBusClient.getReceiver(receiver.getDeadLetterPath(), "receiveAndDelete") + ) + ]); +} + +export function createServiceBusClientForTests( + options?: ServiceBusClientOptions +): ServiceBusClientForTests { + const serviceBusClient = new ServiceBusClient( + connectionString(), + options + ) as ServiceBusClientForTests; + + serviceBusClient.test = new ServiceBusTestHelpers(serviceBusClient); + return serviceBusClient; +} + +export async function drainReceiveAndDeleteReceiver(receiver: Receiver<{}>): Promise { + try { + while (true) { + const messages = await receiver.receiveBatch(10, 1); + + if (messages.messages.length === 0) { + break; + } + } + } finally { + await receiver.close(); + } +} + +function connectionString() { + if (env[EnvVarNames.SERVICEBUS_CONNECTION_STRING] == null) { + throw new Error( + `No service bus connection string defined in ${EnvVarNames.SERVICEBUS_CONNECTION_STRING}` + ); + } + + return env[EnvVarNames.SERVICEBUS_CONNECTION_STRING]; +} + +export async function testPeekMsgsLength( + peekableReceiver: Pick, "diagnostics">, + expectedPeekLength: number +): Promise { + const peekedMsgs = await peekableReceiver.diagnostics.peek(expectedPeekLength + 1); + + should.equal( + peekedMsgs.length, + expectedPeekLength, + "Unexpected number of msgs found when peeking" + ); +} From 23d8e5c44918c1052fedfd6eddf51ab832f6c3c7 Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Sat, 14 Mar 2020 15:44:39 -0700 Subject: [PATCH 11/28] Smoke Test: use configurable authority host (#7795) * add support for authority host and pipelines * add direct @azure/identity dependency * set AZURE_AUTHORITY_HOST * smoke tests use connection string instead of account/key/url combination --- common/smoke-test/BlobStorage.ts | 21 +++--- common/smoke-test/KeyVault.ts | 12 +++- common/smoke-test/package.json | 1 + common/smoke-test/smoke-tests.yml | 66 +++++++++++++++---- .../jobs/archetype-sdk-integration.yml | 32 +++++---- 5 files changed, 90 insertions(+), 42 deletions(-) diff --git a/common/smoke-test/BlobStorage.ts b/common/smoke-test/BlobStorage.ts index ccf26ffcbecc..1715a4e16141 100644 --- a/common/smoke-test/BlobStorage.ts +++ b/common/smoke-test/BlobStorage.ts @@ -2,11 +2,7 @@ // Copyright(c) Microsoft Corporation. // Licensed under the MIT License. // ------------------------------------ -import { - StorageSharedKeyCredential, - BlobServiceClient, - ContainerClient -} from "@azure/storage-blob"; +import { BlobServiceClient, ContainerClient } from "@azure/storage-blob"; const uuidv1 = require("uuid/v1"); @@ -23,18 +19,17 @@ export class BlobStorage { 2) Delete Blob (Clean up the resource) `); - const account = - process.env["STORAGE_ACCOUNT_NAME"] || ""; - const accountKey = - process.env["STORAGE_ACCOUNT_KEY"] || ""; + const storageConnectionString = + process.env["AZURE_STORAGE_CONNECTION_STRING"] || + ""; + const containerName = "mycontainer"; BlobStorage.blobName = `JSNewBlob-${uuidv1()}.txt`; - const credential = new StorageSharedKeyCredential(account, accountKey); - const serviceClient = new BlobServiceClient( - `https://${account}.blob.core.windows.net`, - credential + const serviceClient = BlobServiceClient.fromConnectionString( + storageConnectionString ); + BlobStorage.ContainerClient = serviceClient.getContainerClient( containerName ); diff --git a/common/smoke-test/KeyVault.ts b/common/smoke-test/KeyVault.ts index 1abff80c3903..578363266242 100644 --- a/common/smoke-test/KeyVault.ts +++ b/common/smoke-test/KeyVault.ts @@ -2,7 +2,7 @@ // Copyright(c) Microsoft Corporation. // Licensed under the MIT License. // ------------------------------------ -import { EnvironmentCredential } from "@azure/identity"; +import { DefaultAzureCredential } from "@azure/identity"; import { SecretClient } from "@azure/keyvault-secrets"; const uuidv1 = require("uuid/v1"); @@ -23,11 +23,17 @@ export class KeyVaultSecrets { 3) Delete that secret (Clean up the resource) `); - // EnvironmentCredential expects the following three environment variables: + const authorityHost = + process.env["AZURE_AUTHORITY_HOST"] || + "https://login.microsoftonline.com"; + + // DefaultAzureCredential expects the following three environment variables: // * AZURE_TENANT_ID: The tenant ID in Azure Active Directory // * AZURE_CLIENT_ID: The application (client) ID registered in the AAD tenant // * AZURE_CLIENT_SECRET: The client secret for the registered application - const credential = new EnvironmentCredential(); + const credential = new DefaultAzureCredential({ + authorityHost: authorityHost + }); const url = process.env["AZURE_PROJECT_URL"] || ""; KeyVaultSecrets.client = new SecretClient(url, credential); diff --git a/common/smoke-test/package.json b/common/smoke-test/package.json index e945fb002784..47412103eef7 100644 --- a/common/smoke-test/package.json +++ b/common/smoke-test/package.json @@ -9,6 +9,7 @@ "author": "", "license": "ISC", "dependencies": { + "@azure/identity": "dev", "@azure/cosmos": "dev", "@azure/event-hubs": "dev", "@azure/keyvault-secrets": "dev", diff --git a/common/smoke-test/smoke-tests.yml b/common/smoke-test/smoke-tests.yml index 90361e0bd3de..77ecd69e4927 100644 --- a/common/smoke-test/smoke-tests.yml +++ b/common/smoke-test/smoke-tests.yml @@ -2,12 +2,21 @@ jobs: - job: SmokeTest strategy: matrix: - Linux: + Linux (Public): OSVmImage: "ubuntu-18.04" - Windows: + CloudType: public + Windows (Public): OSVmImage: "windows-2019" - Mac: + CloudType: public + Mac (Public): OSVmImage: "macOS-10.14" + CloudType: public + Linux (Gov): + OSVmImage: "ubuntu-18.04" + CloudType: gov + Windows (Gov): + OSVmImage: "windows-2019" + CloudType: gov pool: vmImage: $(OSVmImage) @@ -45,15 +54,46 @@ jobs: - script: tsc -p ./common/smoke-test/ displayName: "Compile TS" + # Set secret environment variables for different clouds + - pwsh: | + $variables = @{ + AZURE_PROJECT_URL='$(smoke-tests-key-vault-project-url)' + EVENT_HUBS_CONNECTION_STRING='$(smoke-tests-event-hubs-connection-string)' + AZURE_STORAGE_CONNECTION_STRING='$(smoke-tests-storage-connection-string)' + AZURE_CLIENT_SECRET='$(aad-azure-sdk-test-client-secret)' + AZURE_TENANT_ID='$(aad-azure-sdk-test-tenant-id)' + AZURE_CLIENT_ID='$(aad-azure-sdk-test-client-id)' + AZURE_AUTHORITY_HOST='$(aad-azure-sdk-test-authority-uri)' + COSMOS_KEY='$(smoke-tests-cosmos-key)' + COSMOS_ENDPOINT='$(smoke-tests-cosmos-endpoint)' + }; + foreach ($key in $variables.Keys) { + Write-Host "Setting variable '$key'" + Write-Host "##vso[task.setvariable variable=_$key;issecret=true;]$($variables[$key])" + Write-Host "##vso[task.setvariable variable=$key;]$($variables[$key])" + } + displayName: Set secrets for public cloud + condition: and(succeeded(), eq(variables['CloudType'], 'public')) + + - pwsh: | + $variables = @{ + AZURE_PROJECT_URL='$(smoke-tests-key-vault-project-url-gov)' + EVENT_HUBS_CONNECTION_STRING='$(smoke-tests-event-hubs-connection-string-gov)' + AZURE_STORAGE_CONNECTION_STRING='$(smoke-tests-storage-connection-string-gov)' + AZURE_CLIENT_SECRET='$(aad-azure-sdk-test-client-secret-gov)' + AZURE_TENANT_ID='$(aad-azure-sdk-test-tenant-id-gov)' + AZURE_CLIENT_ID='$(aad-azure-sdk-test-client-id-gov)' + AZURE_AUTHORITY_HOST='$(aad-azure-sdk-test-authority-uri-gov)' + COSMOS_KEY='$(smoke-tests-cosmos-key-gov)' + COSMOS_ENDPOINT='$(smoke-tests-cosmos-endpoint-gov)' + }; + foreach ($key in $variables.Keys) { + Write-Host "Setting variable '$key'" + Write-Host "##vso[task.setvariable variable=_$key;issecret=true;]$($variables[$key])" + Write-Host "##vso[task.setvariable variable=$key;]$($variables[$key])" + } + displayName: Set secrets for gov cloud + condition: and(succeeded(), eq(variables['CloudType'], 'gov')) + - script: node ./common/smoke-test/app.js displayName: Run Smoke Test - env: - AZURE_PROJECT_URL: $(smoke-tests-key-vault-project-url) - EVENT_HUBS_CONNECTION_STRING: $(smoke-tests-event-hubs-connection-string) - STORAGE_ACCOUNT_NAME: $(smoke-tests-storage-account-name) - STORAGE_ACCOUNT_KEY: $(smoke-tests-storage-account-key) - AZURE_CLIENT_SECRET: $(aad-azure-sdk-test-client-secret) - AZURE_TENANT_ID: $(aad-azure-sdk-test-tenant-id) - AZURE_CLIENT_ID: $(aad-azure-sdk-test-client-id) - COSMOS_KEY: $(smoke-tests-cosmos-key) - COSMOS_ENDPOINT: $(smoke-tests-cosmos-endpoint) diff --git a/eng/pipelines/templates/jobs/archetype-sdk-integration.yml b/eng/pipelines/templates/jobs/archetype-sdk-integration.yml index b692533d4417..f91c9326f89f 100644 --- a/eng/pipelines/templates/jobs/archetype-sdk-integration.yml +++ b/eng/pipelines/templates/jobs/archetype-sdk-integration.yml @@ -8,26 +8,32 @@ parameters: TimeoutInMinutes: 60 PublishCodeCoverage: false Matrix: - Linux_Node10: + Linux_Node10 (Public): OSVmImage: "ubuntu-16.04" TestType: "node" + CloudType: public PublishCodeCoverage: true - Windows_Node10: + Windows_Node10 (Public): OSVmImage: "windows-2019" TestType: "node" - macOS_Node10: + CloudType: public + macOS_Node10 (Public): OSVmImage: "macOS-10.15" TestType: "node" - Browser_Linux_Node10: + CloudType: public + Browser_Linux_Node10 (Public): OSVmImage: "ubuntu-16.04" TestType: "browser" + CloudType: public PublishCodeCoverage: true - Browser_Windows_Node10: + Browser_Windows_Node10 (Public): OSVmImage: "windows-2019" TestType: "browser" - Browser_macOS_Node10: + CloudType: public + Browser_macOS_Node10 (Public): OSVmImage: "macOS-10.15" TestType: "browser" + CloudType: public jobs: - job: "IntegrationTest" @@ -107,7 +113,7 @@ jobs: - script: | node common/scripts/install-run-rush.js integration-test:$(TestType) -t "${{parameters.PackageName}}" --verbose -p max displayName: "Integration test libraries" - env: + env: TEST_MODE: "live" ${{ insert }}: ${{ parameters.EnvVars }} condition: ne(variables['TestType'],'sample') @@ -118,7 +124,7 @@ jobs: - script: | node common/scripts/npm-run-project.js "${{parameters.PackageName}}" execute:samples displayName: "Execute Samples" - env: + env: TEST_MODE: "live" ${{ insert }}: ${{ parameters.EnvVars }} condition: eq(variables['TestType'],'sample') @@ -137,7 +143,7 @@ jobs: continueOnError: true - script: | - npm install + npm install workingDirectory: $(Build.SourcesDirectory)/eng/tools/versioning displayName: "Install script dependencies" @@ -147,19 +153,19 @@ jobs: displayName: "Get package path" - task: PublishCodeCoverageResults@1 - displayName: 'Publish NodeJs Code Coverage to DevOps' + displayName: "Publish NodeJs Code Coverage to DevOps" continueOnError: true condition: and(succeededOrFailed(),eq(variables['TestType'], 'node'),eq(variables['PublishCodeCoverage'], true)) inputs: codeCoverageTool: Cobertura - summaryFileLocation: '$(PackagePath)/coverage/cobertura-coverage.xml' + summaryFileLocation: "$(PackagePath)/coverage/cobertura-coverage.xml" - task: PublishPipelineArtifact@1 - displayName: 'Publish Browser Code Coverage Report Artifact' + displayName: "Publish Browser Code Coverage Report Artifact" continueOnError: true condition: and(succeededOrFailed(),eq(variables['TestType'], 'browser'),eq(variables['PublishCodeCoverage'], true)) inputs: - path: '$(PackagePath)/coverage-browser' + path: "$(PackagePath)/coverage-browser" artifact: BrowserCodeCoverageReport # Unlink node_modules folders to significantly improve performance of subsequent tasks From 5ebd2f5fe60483ff531ced5f3a211d9ab4102293 Mon Sep 17 00:00:00 2001 From: Richard Park <51494936+richardpark-msft@users.noreply.github.com> Date: Mon, 16 Mar 2020 11:57:20 -0700 Subject: [PATCH 12/28] [service-bus] track2 - fixing issues with the public surface (ServiceBusClient) (#7827) * Fixing some facepalm moments with the public surface: - `ServiceBusClient` wasn't exported - `Sender` the concrete class was exported (should have been an interface) - Some of the older non-used code was still being exported (QueueAuth, etc...) --- .../service-bus/review/service-bus.api.md | 69 +++++--- .../service-bus/src/constructorHelpers.ts | 108 ------------ sdk/servicebus/service-bus/src/index.ts | 9 +- sdk/servicebus/service-bus/src/models.ts | 56 ------ .../service-bus/src/receivers/receiver.ts | 9 + .../src/receivers/sessionReceiver.ts | 4 + sdk/servicebus/service-bus/src/sender.ts | 160 ++++++++++-------- .../service-bus/src/serviceBusClient.ts | 15 +- .../service-bus/test/track2.samples.spec.ts | 62 +------ .../service-bus/test/utils/testutils2.ts | 14 +- 10 files changed, 177 insertions(+), 329 deletions(-) diff --git a/sdk/servicebus/service-bus/review/service-bus.api.md b/sdk/servicebus/service-bus/review/service-bus.api.md index 7489dd73819c..97467a71e873 100644 --- a/sdk/servicebus/service-bus/review/service-bus.api.md +++ b/sdk/servicebus/service-bus/review/service-bus.api.md @@ -82,6 +82,12 @@ export type EntityStatus = "Active" | "Creating" | "Deleting" | "ReceiveDisabled export interface GetMessageIteratorOptions extends OperationOptions { } +// @public +export interface GetSessionReceiverOptions extends OperationOptions { + maxSessionAutoRenewLockDurationInSeconds?: number; + sessionId?: string; +} + export { HttpOperationResponse } // @public @@ -127,18 +133,6 @@ export interface OnMessage { (message: ServiceBusMessage): Promise; } -// @public -export type QueueAuth = { - connectionString: string; - queueName: string; -} | { - queueConnectionString: string; -} | { - tokenCredential: TokenCredential; - host: string; - queueName: string; -}; - // @public export interface QueueDetails { accessedOn?: string; @@ -287,6 +281,34 @@ export interface SendableMessageInfo { viaPartitionKey?: string; } +// @public +export interface Sender { + cancelScheduledMessage(sequenceNumber: Long): Promise; + cancelScheduledMessages(sequenceNumbers: Long[]): Promise; + close(): Promise; + isClosed: boolean; + scheduleMessage(scheduledEnqueueTimeUtc: Date, message: SendableMessageInfo): Promise; + scheduleMessages(scheduledEnqueueTimeUtc: Date, messages: SendableMessageInfo[]): Promise; + send(message: SendableMessageInfo): Promise; + sendBatch(messages: SendableMessageInfo[]): Promise; +} + +// @public +export class ServiceBusClient { + constructor(connectionString: string, options?: ServiceBusClientOptions); + constructor(hostName: string, tokenCredential: TokenCredential, options?: ServiceBusClientOptions); + close(): Promise; + getReceiver(queueName: string, receiveMode: "peekLock"): Receiver; + getReceiver(queueName: string, receiveMode: "receiveAndDelete"): Receiver<{}>; + getReceiver(topicName: string, subscriptionName: string, receiveMode: "peekLock"): Receiver & SubscriptionRuleManagement; + getReceiver(topicName: string, subscriptionName: string, receiveMode: "receiveAndDelete"): Receiver<{}> & SubscriptionRuleManagement; + getSender(queueOrTopicName: string): Sender; + getSessionReceiver(queueName: string, receiveMode: "peekLock", options?: GetSessionReceiverOptions): SessionReceiver; + getSessionReceiver(queueName: string, receiveMode: "receiveAndDelete", options?: GetSessionReceiverOptions): SessionReceiver<{}>; + getSessionReceiver(topicName: string, subscriptionName: string, receiveMode: "peekLock", options?: GetSessionReceiverOptions): SessionReceiver & SubscriptionRuleManagement; + getSessionReceiver(topicName: string, subscriptionName: string, receiveMode: "receiveAndDelete", options?: GetSessionReceiverOptions): SessionReceiver<"receiveAndDelete"> & SubscriptionRuleManagement; +} + // @public export interface ServiceBusClientOptions { dataTransformer?: DataTransformer; @@ -391,21 +413,6 @@ export type SqlParameter = { export interface SubscribeOptions extends OperationOptions, MessageHandlerOptions { } -// @public -export type SubscriptionAuth = { - connectionString: string; - topicName: string; - subscriptionName: string; -} | { - topicConnectionString: string; - subscriptionName: string; -} | { - tokenCredential: TokenCredential; - host: string; - topicName: string; - subscriptionName: string; -}; - // @public export interface SubscriptionDetails { accessedOn?: string; @@ -450,6 +457,14 @@ export interface SubscriptionOptions { userMetadata?: string; } +// @public +export interface SubscriptionRuleManagement { + addRule(ruleName: string, filter: boolean | string | CorrelationFilter, sqlRuleActionExpression?: string): Promise; + readonly defaultRuleName: string; + getRules(): Promise; + removeRule(ruleName: string): Promise; +} + export { TokenCredential } export { TokenType } diff --git a/sdk/servicebus/service-bus/src/constructorHelpers.ts b/sdk/servicebus/service-bus/src/constructorHelpers.ts index 04de8d75987a..5647d8f75bbf 100644 --- a/sdk/servicebus/service-bus/src/constructorHelpers.ts +++ b/sdk/servicebus/service-bus/src/constructorHelpers.ts @@ -1,10 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -import { QueueAuth, SubscriptionAuth } from "./models"; import { ReceiveMode } from "./serviceBusMessage"; import { - isTokenCredential, TokenCredential, ConnectionConfig, SharedKeyCredential, @@ -95,74 +93,6 @@ export function getEntityNameFromConnectionString(connectionString: string): str } } -/** - * Attempts to generically figure out what the entity path is from the grab bag of string parameters - * we get in our constructors. - * - * @param auth Authentication information using connection strings or a TokenCredential. - * @param options Options for the service bus client itself. - * @internal - * @ignore - */ -export function createConnectionContext( - auth: QueueAuth | SubscriptionAuth, - options: ServiceBusClientOptions -): { context: ConnectionContext; entityPath: string } { - if (hasTokenCredentialAndHost(auth)) { - let entityPath: string; - - if (hasQueueName(auth)) { - entityPath = auth.queueName; - } else if (hasTopicName(auth) && hasSubscriptionName(auth)) { - entityPath = `${auth.topicName}/Subscriptions/${auth.subscriptionName}`; - } else { - throw new TypeError("Missing fields when using TokenCredential authentication"); - } - - return { - context: createConnectionContextForTokenCredential(auth.tokenCredential, auth.host, options), - entityPath: entityPath - }; - } else if (hasQueueConnectionString(auth)) { - // connection string based authentication - const queueName = getEntityNameFromConnectionString(auth.queueConnectionString); - - return { - context: createConnectionContextForConnectionString(auth.queueConnectionString, options), - entityPath: queueName - }; - } else if (hasTopicConnectionString(auth)) { - const topicName = getEntityNameFromConnectionString(auth.topicConnectionString); - - if (hasSubscriptionName(auth)) { - // topic (from connection string) + sub - return { - context: createConnectionContextForConnectionString(auth.topicConnectionString, options), - entityPath: `${topicName}/Subscriptions/${auth.subscriptionName as string}` - }; - } else { - throw new TypeError("Missing subscription name, required as part of connecting to a topic"); - } - } else if (hasConnectionString(auth)) { - let entityPath: string; - - if (hasQueueName(auth)) { - entityPath = auth.queueName; - } else if (hasTopicName(auth) && hasSubscriptionName(auth)) { - entityPath = `${auth.topicName}/Subscriptions/${auth.subscriptionName}`; - } else { - throw new TypeError("Missing fields when using TokenCredential authentication"); - } - - return { - context: createConnectionContextForConnectionString(auth.connectionString, options), - entityPath: entityPath - }; - } else { - throw new TypeError("Unhandled set of parameters"); - } -} - /** * Temporary bit of conversion code until we can eliminate external usage of this * enum. @@ -180,41 +110,3 @@ export function convertToInternalReceiveMode( return ReceiveMode.receiveAndDelete; } } - -function hasHost(auth: any): auth is { host: string } { - return auth.host && typeof auth.host === "string"; -} - -function hasTokenCredentialAndHost( - auth: any -): auth is { tokenCredential: TokenCredential; host: string } { - return isTokenCredential(auth.tokenCredential) && hasHost(auth); -} - -function hasSubscriptionName(auth: any): auth is { subscriptionName: string } { - return auth.subscriptionName && typeof auth.subscriptionName === "string"; -} - -function hasQueueName(auth: any): auth is { queueName: string } { - return auth.queueName && typeof auth.queueName === "string"; -} - -function hasTopicName(auth: any): auth is { topicName: string } { - return auth.topicName && typeof auth.topicName === "string"; -} - -function hasQueueConnectionString(auth: any): auth is { queueConnectionString: string } { - return auth.queueConnectionString && typeof auth.queueConnectionString === "string"; -} - -function hasConnectionString(auth: any): auth is { connectionString: string } { - return auth.connectionString && typeof auth.connectionString === "string"; -} - -function hasTopicConnectionString( - auth: any -): auth is { - topicConnectionString: string; -} { - return auth.topicConnectionString && typeof auth.topicConnectionString === "string"; -} diff --git a/sdk/servicebus/service-bus/src/index.ts b/sdk/servicebus/service-bus/src/index.ts index bfbfe0d3becb..1d77f1cc8f07 100644 --- a/sdk/servicebus/service-bus/src/index.ts +++ b/sdk/servicebus/service-bus/src/index.ts @@ -52,8 +52,6 @@ export { ReceivedMessage, ContextWithSettlement, Session, - QueueAuth, - SubscriptionAuth, GetMessageIteratorOptions, ReceiveBatchOptions, SubscribeOptions, @@ -62,8 +60,11 @@ export { ContextType, Closeable, MessageAndContext, - MessageIterator + MessageIterator, + GetSessionReceiverOptions } from "./models"; -export { Receiver } from "./receivers/receiver"; +export { Receiver, SubscriptionRuleManagement } from "./receivers/receiver"; export { SessionReceiver } from "./receivers/sessionReceiver"; +export { Sender } from "./sender"; +export { ServiceBusClient } from "./serviceBusClient"; diff --git a/sdk/servicebus/service-bus/src/models.ts b/sdk/servicebus/service-bus/src/models.ts index 8a73307a5e18..b3e15cc60b71 100644 --- a/sdk/servicebus/service-bus/src/models.ts +++ b/sdk/servicebus/service-bus/src/models.ts @@ -2,7 +2,6 @@ // Licensed under the MIT License. import { ServiceBusMessage, DeadLetterOptions } from "./serviceBusMessage"; -import { TokenCredential } from "@azure/core-amqp"; import { OperationOptions } from "@azure/core-auth"; /** @@ -154,61 +153,6 @@ export type ContextType = LockModeT extends "peekLock" ? {} : never; -/** - * Authentication methods for queues. - * TODO: consider inlining inside constructors - */ -export type QueueAuth = - | { - /** - * A connection string that points to a service bus (ie: does not contain an EntityName value). - */ - connectionString: string; - /** - * The name of the queue to connect to. - */ - queueName: string; - } - | { - /** - * A connection string that points to a queue (contains EntityName=). - */ - queueConnectionString: string; - } - | { - tokenCredential: TokenCredential; - host: string; - queueName: string; - }; - -export function isQueueAuth( - possibleQueueAuth: QueueAuth | SubscriptionAuth -): possibleQueueAuth is QueueAuth { - const queueAuth = possibleQueueAuth as any; - return queueAuth.queueName != null || queueAuth.queueConnectionString != null; -} - -/** - * Authentication methods for subscriptions. - * TODO: consider inlining inside constructors - */ -export type SubscriptionAuth = - | { - connectionString: string; - topicName: string; - subscriptionName: string; - } - | { - topicConnectionString: string; - subscriptionName: string; - } - | { - tokenCredential: TokenCredential; - host: string; - topicName: string; - subscriptionName: string; - }; - /** * Options when receiving a batch of messages from Service Bus. */ diff --git a/sdk/servicebus/service-bus/src/receivers/receiver.ts b/sdk/servicebus/service-bus/src/receivers/receiver.ts index a222bfe7c910..ff74faecc22a 100644 --- a/sdk/servicebus/service-bus/src/receivers/receiver.ts +++ b/sdk/servicebus/service-bus/src/receivers/receiver.ts @@ -223,9 +223,18 @@ export interface SubscriptionRuleManagement { filter: boolean | string | CorrelationFilter, sqlRuleActionExpression?: string ): Promise; + + /** + * @readonly + * @property The name of the default rule on the subscription. + */ readonly defaultRuleName: string; } +/** + * @internal + * @ignore + */ export class ReceiverImpl implements Receiver, SubscriptionRuleManagement { /** * @property Describes the amqp connection context for the QueueClient. diff --git a/sdk/servicebus/service-bus/src/receivers/sessionReceiver.ts b/sdk/servicebus/service-bus/src/receivers/sessionReceiver.ts index 419df9799295..cd6a3077d051 100644 --- a/sdk/servicebus/service-bus/src/receivers/sessionReceiver.ts +++ b/sdk/servicebus/service-bus/src/receivers/sessionReceiver.ts @@ -111,6 +111,10 @@ export interface SessionReceiver setState(state: any): Promise; } +/** + * @internal + * @ignore + */ export class SessionReceiverImpl implements SessionReceiver { public entityPath: string; diff --git a/sdk/servicebus/service-bus/src/sender.ts b/sdk/servicebus/service-bus/src/sender.ts index 2987693e0bd8..abcb6e17851f 100644 --- a/sdk/servicebus/service-bus/src/sender.ts +++ b/sdk/servicebus/service-bus/src/sender.ts @@ -15,13 +15,102 @@ import { } from "./util/errors"; /** - * The Sender class can be used to send messages, schedule messages to be sent at a later time + * A Sender can be used to send messages, schedule messages to be sent at a later time * and cancel such scheduled messages. - * Use the `createSender` function on the QueueClient or TopicClient to instantiate a Sender. + * Use the `createSender` function on the ServiceBusClient instantiate a Sender. * The Sender class is an abstraction over the underlying AMQP sender link. - * @class Sender */ -export class Sender { +export interface Sender { + /** + * Sends the given message after creating an AMQP Sender link if it doesnt already exists. + * + * To send a message to a `session` and/or `partition` enabled Queue/Topic, set the `sessionId` + * and/or `partitionKey` properties respectively on the message. + * + * @param message - Message to send. + * @returns Promise + * @throws Error if the underlying connection, client or sender is closed. + * @throws MessagingError if the service returns an error while sending messages to the service. + */ + send(message: SendableMessageInfo): Promise; + + /** + * Sends the given messages in a single batch i.e. in a single AMQP message after creating an AMQP + * Sender link if it doesnt already exists. + * + * - To send messages to a `session` and/or `partition` enabled Queue/Topic, set the `sessionId` + * and/or `partitionKey` properties respectively on the messages. + * - When doing so, all + * messages in the batch should have the same `sessionId` (if using sessions) and the same + * `parititionKey` (if using paritions). + * + * @param messages - An array of SendableMessageInfo objects to be sent in a Batch message. + * @return Promise + * @throws Error if the underlying connection, client or sender is closed. + * @throws MessagingError if the service returns an error while sending messages to the service. + */ + sendBatch(messages: SendableMessageInfo[]): Promise; + /** + * @property Returns `true` if either the sender or the client that created it has been closed + * @readonly + */ + isClosed: boolean; + /** + * Schedules given message to appear on Service Bus Queue/Subscription at a later time. + * + * @param scheduledEnqueueTimeUtc - The UTC time at which the message should be enqueued. + * @param message - The message that needs to be scheduled. + * @returns Promise - The sequence number of the message that was scheduled. + * You will need the sequence number if you intend to cancel the scheduling of the message. + * Save the `Long` type as-is in your application without converting to number. Since JavaScript + * only supports 53 bit numbers, converting the `Long` to number will cause loss in precision. + * @throws Error if the underlying connection, client or sender is closed. + * @throws MessagingError if the service returns an error while scheduling a message. + */ + scheduleMessage(scheduledEnqueueTimeUtc: Date, message: SendableMessageInfo): Promise; + + /** + * Schedules given messages to appear on Service Bus Queue/Subscription at a later time. + * + * @param scheduledEnqueueTimeUtc - The UTC time at which the messages should be enqueued. + * @param messages - Array of Messages that need to be scheduled. + * @returns Promise - The sequence numbers of messages that were scheduled. + * You will need the sequence number if you intend to cancel the scheduling of the messages. + * Save the `Long` type as-is in your application without converting to number. Since JavaScript + * only supports 53 bit numbers, converting the `Long` to number will cause loss in precision. + * @throws Error if the underlying connection, client or sender is closed. + * @throws MessagingError if the service returns an error while scheduling messages. + */ + scheduleMessages(scheduledEnqueueTimeUtc: Date, messages: SendableMessageInfo[]): Promise; + + /** + * Cancels a message that was scheduled to appear on a ServiceBus Queue/Subscription. + * @param sequenceNumber - The sequence number of the message to be cancelled. + * @returns Promise + * @throws Error if the underlying connection, client or sender is closed. + * @throws MessagingError if the service returns an error while canceling a scheduled message. + */ + cancelScheduledMessage(sequenceNumber: Long): Promise; + /** + * Cancels multiple messages that were scheduled to appear on a ServiceBus Queue/Subscription. + * @param sequenceNumbers - An Array of sequence numbers of the messages to be cancelled. + * @returns Promise + * @throws Error if the underlying connection, client or sender is closed. + * @throws MessagingError if the service returns an error while canceling scheduled messages. + */ + cancelScheduledMessages(sequenceNumbers: Long[]): Promise; + + /** + * Closes the underlying AMQP sender link. + * Once closed, the sender cannot be used for any further operations. + * Use the `createSender` function on the QueueClient or TopicClient to instantiate a new Sender + * + * @returns {Promise} + */ + close(): Promise; +} + +export class SenderImpl implements Sender { /** * @property Describes the amqp connection context for the Client. */ @@ -54,25 +143,10 @@ export class Sender { } } - /** - * @property Returns `true` if either the sender or the client that created it has been closed - * @readonly - */ public get isClosed(): boolean { return this._isClosed || this._context.isClosed; } - /** - * Sends the given message after creating an AMQP Sender link if it doesnt already exists. - * - * To send a message to a `session` and/or `partition` enabled Queue/Topic, set the `sessionId` - * and/or `partitionKey` properties respectively on the message. - * - * @param message - Message to send. - * @returns Promise - * @throws Error if the underlying connection, client or sender is closed. - * @throws MessagingError if the service returns an error while sending messages to the service. - */ async send(message: SendableMessageInfo): Promise { this._throwIfSenderOrConnectionClosed(); throwTypeErrorIfParameterMissing(this._context.namespace.connectionId, "message", message); @@ -80,21 +154,6 @@ export class Sender { return sender.send(message); } - /** - * Sends the given messages in a single batch i.e. in a single AMQP message after creating an AMQP - * Sender link if it doesnt already exists. - * - * - To send messages to a `session` and/or `partition` enabled Queue/Topic, set the `sessionId` - * and/or `partitionKey` properties respectively on the messages. - * - When doing so, all - * messages in the batch should have the same `sessionId` (if using sessions) and the same - * `parititionKey` (if using paritions). - * - * @param messages - An array of SendableMessageInfo objects to be sent in a Batch message. - * @return Promise - * @throws Error if the underlying connection, client or sender is closed. - * @throws MessagingError if the service returns an error while sending messages to the service. - */ async sendBatch(messages: SendableMessageInfo[]): Promise { this._throwIfSenderOrConnectionClosed(); throwTypeErrorIfParameterMissing(this._context.namespace.connectionId, "messages", messages); @@ -137,18 +196,6 @@ export class Sender { return result[0]; } - /** - * Schedules given messages to appear on Service Bus Queue/Subscription at a later time. - * - * @param scheduledEnqueueTimeUtc - The UTC time at which the messages should be enqueued. - * @param messages - Array of Messages that need to be scheduled. - * @returns Promise - The sequence numbers of messages that were scheduled. - * You will need the sequence number if you intend to cancel the scheduling of the messages. - * Save the `Long` type as-is in your application without converting to number. Since JavaScript - * only supports 53 bit numbers, converting the `Long` to number will cause loss in precision. - * @throws Error if the underlying connection, client or sender is closed. - * @throws MessagingError if the service returns an error while scheduling messages. - */ async scheduleMessages( scheduledEnqueueTimeUtc: Date, messages: SendableMessageInfo[] @@ -167,13 +214,6 @@ export class Sender { return this._context.managementClient!.scheduleMessages(scheduledEnqueueTimeUtc, messages); } - /** - * Cancels a message that was scheduled to appear on a ServiceBus Queue/Subscription. - * @param sequenceNumber - The sequence number of the message to be cancelled. - * @returns Promise - * @throws Error if the underlying connection, client or sender is closed. - * @throws MessagingError if the service returns an error while canceling a scheduled message. - */ async cancelScheduledMessage(sequenceNumber: Long): Promise { this._throwIfSenderOrConnectionClosed(); throwTypeErrorIfParameterMissing( @@ -190,13 +230,6 @@ export class Sender { return this._context.managementClient!.cancelScheduledMessages([sequenceNumber]); } - /** - * Cancels multiple messages that were scheduled to appear on a ServiceBus Queue/Subscription. - * @param sequenceNumbers - An Array of sequence numbers of the messages to be cancelled. - * @returns Promise - * @throws Error if the underlying connection, client or sender is closed. - * @throws MessagingError if the service returns an error while canceling scheduled messages. - */ async cancelScheduledMessages(sequenceNumbers: Long[]): Promise { this._throwIfSenderOrConnectionClosed(); throwTypeErrorIfParameterMissing( @@ -216,13 +249,6 @@ export class Sender { return this._context.managementClient!.cancelScheduledMessages(sequenceNumbers); } - /** - * Closes the underlying AMQP sender link. - * Once closed, the sender cannot be used for any further operations. - * Use the `createSender` function on the QueueClient or TopicClient to instantiate a new Sender - * - * @returns {Promise} - */ async close(): Promise { try { this._isClosed = true; diff --git a/sdk/servicebus/service-bus/src/serviceBusClient.ts b/sdk/servicebus/service-bus/src/serviceBusClient.ts index 8e88c84a7875..bf273f924417 100644 --- a/sdk/servicebus/service-bus/src/serviceBusClient.ts +++ b/sdk/servicebus/service-bus/src/serviceBusClient.ts @@ -2,7 +2,7 @@ // Licensed under the MIT License. import { generate_uuid } from "rhea-promise"; -import { isTokenCredential, TokenCredential } from "@azure/core-auth"; +import { isTokenCredential, TokenCredential } from "@azure/core-amqp"; import { ServiceBusClientOptions, createConnectionContextForTokenCredential, @@ -11,11 +11,15 @@ import { import { ConnectionContext } from "./connectionContext"; import { ClientEntityContext } from "./clientEntityContext"; import { ClientType } from "./client"; -import { Sender } from "./sender"; +import { SenderImpl, Sender } from "./sender"; import { GetSessionReceiverOptions, ContextWithSettlement } from "./models"; import { Receiver, ReceiverImpl, SubscriptionRuleManagement } from "./receivers/receiver"; import { SessionReceiver, SessionReceiverImpl } from "./receivers/sessionReceiver"; +/** + * A client that can create Sender instances for sending messages to queues and + * topics as well as Receiver instances to receive messages from queus and subscriptions. + */ export class ServiceBusClient { private _connectionContext: ConnectionContext; @@ -256,9 +260,14 @@ export class ServiceBusClient { `${queueOrTopicName}/${generate_uuid()}` ); - return new Sender(clientEntityContext); + return new SenderImpl(clientEntityContext); } + /** + * Closes the underlying AMQP connection. + * NOTE: this will also disconnect any Receiver or Sender instances created from this + * instance. + */ close(): Promise { return ConnectionContext.close(this._connectionContext); } diff --git a/sdk/servicebus/service-bus/test/track2.samples.spec.ts b/sdk/servicebus/service-bus/test/track2.samples.spec.ts index de1590b1619a..fdb16fe7b066 100644 --- a/sdk/servicebus/service-bus/test/track2.samples.spec.ts +++ b/sdk/servicebus/service-bus/test/track2.samples.spec.ts @@ -6,16 +6,13 @@ import { delay, SendableMessageInfo } from "../src"; import { TestClientType } from "./utils/testUtils"; import chai from "chai"; import chaiAsPromised from "chai-as-promised"; -import { - createConnectionContext, - getEntityNameFromConnectionString -} from "../src/constructorHelpers"; +import { getEntityNameFromConnectionString } from "../src/constructorHelpers"; import { createServiceBusClientForTests, ServiceBusClientForTests } from "./utils/testutils2"; import { Sender } from "../src/sender"; chai.use(chaiAsPromised); const assert = chai.assert; -describe("Sample scenarios for track 2", () => { +describe("Sample scenarios for track 2 #RunInBrowser", () => { let serviceBusClient: ServiceBusClientForTests; before(async () => { @@ -426,61 +423,6 @@ describe("ConstructorHelpers for track 2", () => { const serviceBusConnectionString = "Endpoint=sb://host/;SharedAccessKeyName=queueall;SharedAccessKey=thesharedkey="; - const fakeTokenCredential = { - getToken: async () => null, - sentinel: "test token credential" - }; - - const badAuths = [ - // missing required fields - { connectionString: serviceBusConnectionString }, - { topicConnectionString: entityConnectionString }, - { tokenCredential: fakeTokenCredential } as any, - - // wrong types - { connectionString: 4, topicName: "myentity", subscriptionName: "mysubscription" }, - { - connectionString: serviceBusConnectionString, - topicName: 4, - subscriptionName: "mysubscription" - }, - { connectionString: serviceBusConnectionString, topicName: "myentity", subscriptionName: 4 }, - { connectionString: "", topicName: "myentity", subscriptionName: "mysubscription" }, - { - connectionString: serviceBusConnectionString, - topicName: "", - subscriptionName: "mysubscription" - }, - { connectionString: serviceBusConnectionString, topicName: "myentity", subscriptionName: "" }, - { connectionString: 4, queueName: "myentity" }, - { connectionString: serviceBusConnectionString, queueName: 4 }, - { queueConnectionString: 4 }, - { queueConnectionString: "" }, - { topicConnectionString: 4, subscriptionName: "mysubscription" }, - { topicConnectionString: entityConnectionString, subscriptionName: 4 }, - { topicConnectionString: "", subscriptionName: "mysubscription" }, - { topicConnectionString: entityConnectionString, subscriptionName: "" }, - - // no entity name present for entity connection string types - { - topicConnectionString: - "Endpoint=sb://host/;SharedAccessKeyName=queueall;SharedAccessKey=thesharedkey=", - subscriptionName: "mysubscription" - }, - { - queueConnectionString: - "Endpoint=sb://host/;SharedAccessKeyName=queueall;SharedAccessKey=thesharedkey=" - } - ]; - - badAuths.forEach((badAuth) => { - it(`createConnectionContext - bad auth ${JSON.stringify(badAuth)}`, () => { - assert.throws(() => { - createConnectionContext(badAuth, {}); - }); - }); - }); - it("getEntityNameFromConnectionString", () => { assert.equal("myentity", getEntityNameFromConnectionString(entityConnectionString)); assert.throws(() => getEntityNameFromConnectionString(serviceBusConnectionString)); diff --git a/sdk/servicebus/service-bus/test/utils/testutils2.ts b/sdk/servicebus/service-bus/test/utils/testutils2.ts index 5ab36b34bf03..ae9efef1f534 100644 --- a/sdk/servicebus/service-bus/test/utils/testutils2.ts +++ b/sdk/servicebus/service-bus/test/utils/testutils2.ts @@ -1,16 +1,22 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +// Anything we expect to be available to users should come from this import +// as a simple sanity check that we've exported things properly. +import { + ServiceBusClient, + SessionReceiver, + Receiver, + SubscriptionRuleManagement, + GetSessionReceiverOptions +} from "../../src"; + import { TestClientType, TestMessage } from "./testUtils"; import { getEnvVars, EnvVarNames } from "./envVarUtils"; import * as dotenv from "dotenv"; import { recreateQueue, recreateTopic, recreateSubscription } from "./managementUtils"; -import { SessionReceiver } from "../../src/receivers/sessionReceiver"; -import { Receiver, SubscriptionRuleManagement } from "../../src/receivers/receiver"; -import { ServiceBusClient } from "../../src/serviceBusClient"; import { ServiceBusClientOptions, ContextWithSettlement } from "../../src"; import chai from "chai"; -import { GetSessionReceiverOptions } from "../../src/models"; dotenv.config(); const env = getEnvVars(); From 7be88dfc54416b602f294f5c47881140de18e055 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 16 Mar 2020 20:59:00 -0700 Subject: [PATCH 13/28] Make ResourceResponse.resource optional (#7820) Currently the types indicate resource will never be undefined but that is not the case when nothing is returned from a query in Cosmos. This fix causes API to break in a sense that it may result in compile errors however these compile errors may be masking bugs when the consumer is using strict null checking. Fixes #7819 --- sdk/cosmosdb/cosmos/review/cosmos.api.md | 4 ++-- sdk/cosmosdb/cosmos/src/request/ResourceResponse.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/sdk/cosmosdb/cosmos/review/cosmos.api.md b/sdk/cosmosdb/cosmos/review/cosmos.api.md index 29823d41343c..2091b4a59a38 100644 --- a/sdk/cosmosdb/cosmos/review/cosmos.api.md +++ b/sdk/cosmosdb/cosmos/review/cosmos.api.md @@ -966,7 +966,7 @@ export interface Resource { // @public (undocumented) export class ResourceResponse { - constructor(resource: TResource, headers: CosmosHeaders_2, statusCode: StatusCode, substatus?: SubStatusCode); + constructor(resource: TResource | undefined, headers: CosmosHeaders_2, statusCode: StatusCode, substatus?: SubStatusCode); // (undocumented) get activityId(): string; // (undocumented) @@ -976,7 +976,7 @@ export class ResourceResponse { // (undocumented) get requestCharge(): number; // (undocumented) - readonly resource: TResource; + readonly resource: TResource | undefined; // Warning: (ae-forgotten-export) The symbol "StatusCode" needs to be exported by the entry point index.d.ts // // (undocumented) diff --git a/sdk/cosmosdb/cosmos/src/request/ResourceResponse.ts b/sdk/cosmosdb/cosmos/src/request/ResourceResponse.ts index 7e24bfe0ae2a..11b6b4ca52ee 100644 --- a/sdk/cosmosdb/cosmos/src/request/ResourceResponse.ts +++ b/sdk/cosmosdb/cosmos/src/request/ResourceResponse.ts @@ -6,11 +6,11 @@ import { StatusCode, SubStatusCode } from "./StatusCodes"; export class ResourceResponse { constructor( - public readonly resource: TResource, + public readonly resource: TResource | undefined, public readonly headers: CosmosHeaders, public readonly statusCode: StatusCode, public readonly substatus?: SubStatusCode - ) {} + ) { } public get requestCharge(): number { return Number(this.headers[Constants.HttpHeaders.RequestCharge]) || 0; } From 5bf9eedf0822b2fc3c69c4b768497c6c7ef1c45d Mon Sep 17 00:00:00 2001 From: KarishmaGhiya Date: Tue, 17 Mar 2020 10:37:14 -0700 Subject: [PATCH 14/28] Pinning to Typedoc 0.16.x until further investigation (#7850) --- .../templates/jobs/archetype-sdk-client.yml | 19 +++--------------- .../templates/steps/generate-doc.yml | 20 +++++++++++++++++++ 2 files changed, 23 insertions(+), 16 deletions(-) create mode 100644 eng/pipelines/templates/steps/generate-doc.yml diff --git a/eng/pipelines/templates/jobs/archetype-sdk-client.yml b/eng/pipelines/templates/jobs/archetype-sdk-client.yml index 89378219ec13..548dab4586f0 100644 --- a/eng/pipelines/templates/jobs/archetype-sdk-client.yml +++ b/eng/pipelines/templates/jobs/archetype-sdk-client.yml @@ -108,22 +108,9 @@ jobs: artifactName: packages path: $(Build.ArtifactStagingDirectory) - - script: | - npm i -g typedoc - displayName: "Install typedoc" - - - script: | - npm install - workingDirectory: $(System.DefaultWorkingDirectory)/eng/tools/generate-doc - displayName: "Install tool dependencies" - - - pwsh: | - node $(Build.SourcesDirectory)/eng/tools/generate-doc/index.js --dgOp "dg" -i "inc" --inc "${{parameters.ServiceDirectory}}" - displayName: "Run Typedoc Docs" - - - pwsh: | - $(Build.SourcesDirectory)/eng/tools/compress-subfolders.ps1 "$(Build.SourcesDirectory)/docGen" "$(Build.ArtifactStagingDirectory)/Documentation" - displayName: "Generate Typedoc Docs" + - template: ../steps/generate-doc.yml + parameters: + ServiceDirectory: ${{parameters.ServiceDirectory}} - task: PublishPipelineArtifact@1 condition: succeededOrFailed() diff --git a/eng/pipelines/templates/steps/generate-doc.yml b/eng/pipelines/templates/steps/generate-doc.yml new file mode 100644 index 000000000000..ce402446d67e --- /dev/null +++ b/eng/pipelines/templates/steps/generate-doc.yml @@ -0,0 +1,20 @@ +parameters: + ServiceDirectory: not-specified + +steps: + - script: | + npm i -g typedoc@0.16.x + displayName: "Install typedoc" + + - script: | + npm install + workingDirectory: $(System.DefaultWorkingDirectory)/eng/tools/generate-doc + displayName: "Install tool dependencies" + + - pwsh: | + node $(Build.SourcesDirectory)/eng/tools/generate-doc/index.js --dgOp "dg" -i "inc" --inc "${{parameters.ServiceDirectory}}" + displayName: "Run Typedoc Docs" + + - pwsh: | + $(Build.SourcesDirectory)/eng/tools/compress-subfolders.ps1 "$(Build.SourcesDirectory)/docGen" "$(Build.ArtifactStagingDirectory)/Documentation" + displayName: "Generate Typedoc Docs" From 0f39565a2e1b29e9f5d9974eb470f3530c9c3739 Mon Sep 17 00:00:00 2001 From: Scott Beddall <45376673+scbedd@users.noreply.github.com> Date: Tue, 17 Mar 2020 13:56:43 -0700 Subject: [PATCH 15/28] adjusting for new versioning files (#7696) --- .../theme/default/assets/js/get_options.js | 28 +++++-------------- 1 file changed, 7 insertions(+), 21 deletions(-) diff --git a/eng/tools/generate-doc/theme/default/assets/js/get_options.js b/eng/tools/generate-doc/theme/default/assets/js/get_options.js index 90013faef080..5fab72f7c589 100644 --- a/eng/tools/generate-doc/theme/default/assets/js/get_options.js +++ b/eng/tools/generate-doc/theme/default/assets/js/get_options.js @@ -41,26 +41,16 @@ function hideSelectors(selectors) { function populateOptions(optionSelector, otherSelectors) { if (currentPackage()) { var versionRequestUrl = - "https://azuresdkdocs.blob.core.windows.net/$web?restype=container&comp=list&prefix=" + + "https://azuresdkdocs.blob.core.windows.net/$web/" + SELECTED_LANGUAGE + "/" + currentPackage() + - "/versions/"; + "/versioning/versions"; httpGetAsync(versionRequestUrl, function(responseText) { if (responseText) { - data_stored = responseText; - - parser = new DOMParser(); - xmlDoc = parser.parseFromString(responseText, "text/xml"); - - nameElements = Array.from(xmlDoc.getElementsByTagName("Name")); - options = []; - - for (var i in nameElements) { - options.push(nameElements[i].textContent.split("/")[3]); - } - + options = responseText.match(/[^\r\n]+/g) + populateVersionDropDown(optionSelector, options); showSelectors(otherSelectors); @@ -101,19 +91,15 @@ function getPackageUrl(language, package, version) { function populateIndexList(selector, packageName) { url = - "https://azuresdkdocs.blob.core.windows.net/$web?restype=container&comp=list&prefix=" + + "https://azuresdkdocs.blob.core.windows.net/$web/" + SELECTED_LANGUAGE + "/" + packageName + - "/versions/"; + "/versioning/versions"; httpGetAsync(url, function(responseText) { if (responseText) { - parser = new DOMParser(); - xmlDoc = parser.parseFromString(responseText, "text/xml"); - - nameElements = Array.from(xmlDoc.getElementsByTagName("Name")); - options = []; + options = responseText.match(/[^\r\n]+/g) for (var i in nameElements) { options.push(nameElements[i].textContent.split("/")[3]); From 211fd465ffb3fd33235ed380bb9c19d25e43430b Mon Sep 17 00:00:00 2001 From: Scott Beddall <45376673+scbedd@users.noreply.github.com> Date: Tue, 17 Mar 2020 14:46:24 -0700 Subject: [PATCH 16/28] New Versioning Should Apply to Index as Well (#7860) * new versioning in index as well --- .../generate-static-index/static-files/main.js | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/eng/tools/generate-static-index/static-files/main.js b/eng/tools/generate-static-index/static-files/main.js index ed2b6f228f1d..a8f8b3295409 100644 --- a/eng/tools/generate-static-index/static-files/main.js +++ b/eng/tools/generate-static-index/static-files/main.js @@ -12,23 +12,18 @@ function httpGetAsync(targetUrl, callback) { } function populateIndexList(selector, packageName) { url = - "https://azuresdkdocs.blob.core.windows.net/$web?restype=container&comp=list&prefix=" + + "https://azuresdkdocs.blob.core.windows.net/$web/" + SELECTED_LANGUAGE + "/" + packageName + - "/versions/"; + "/versioning/versions"; console.log(url); console.log(selector); httpGetAsync(url, function(responseText) { var publishedversions = document.createElement("ul"); if (responseText) { - parser = new DOMParser(); - xmlDoc = parser.parseFromString(responseText, "text/xml"); - nameElements = Array.from(xmlDoc.getElementsByTagName("Name")); - options = []; - for (var i in nameElements) { - options.push(nameElements[i].textContent.split("/")[3]); - } + options = responseText.match(/[^\r\n]+/g) + for (var i in options) { $(publishedversions).append( '
  • Date: Tue, 17 Mar 2020 16:35:03 -0700 Subject: [PATCH 18/28] [service-bus] Get message classes ready for preview.1 (#7847) Place settlement and lock renewal methods on the message object itself. - Messages that are to be sent are just called ServiceBusMessage. - Messages that have been received from service bus are called ReceivedMessage. - Messages that have been received on a receiver using "peekLock" are ReceivedMessageWithLock. - ServiceBusMessage (the class) has been renamed to ServiceBusMessageImpl. At this point our client should only serve up interfaces, not named concrete classes. --- .../service-bus/review/service-bus.api.md | 168 +--- .../service-bus/src/core/batchingReceiver.ts | 15 +- .../service-bus/src/core/managementClient.ts | 20 +- .../service-bus/src/core/messageReceiver.ts | 8 +- .../service-bus/src/core/messageSender.ts | 8 +- sdk/servicebus/service-bus/src/index.ts | 22 +- .../service-bus/src/internalReceivers.ts | 901 ------------------ sdk/servicebus/service-bus/src/models.ts | 139 +-- .../service-bus/src/receivers/receiver.ts | 137 +-- .../src/receivers/sessionReceiver.ts | 75 +- .../service-bus/src/receivers/shared.ts | 43 +- sdk/servicebus/service-bus/src/sender.ts | 21 +- .../service-bus/src/serviceBusClient.ts | 43 +- .../service-bus/src/serviceBusMessage.ts | 268 ++++-- .../service-bus/src/session/messageSession.ts | 14 +- sdk/servicebus/service-bus/src/util/errors.ts | 2 +- .../service-bus/test/batchReceiver.spec.ts | 185 ++-- .../service-bus/test/deferredMessage.spec.ts | 25 +- .../test/invalidParameters.spec.ts | 8 +- .../test/receiveAndDeleteMode.spec.ts | 40 +- .../service-bus/test/renewLock.spec.ts | 46 +- .../test/renewLockSessions.spec.ts | 53 +- .../service-bus/test/sendSchedule.spec.ts | 45 +- .../sessionsRequiredCleanEntityTests.spec.ts | 29 +- .../service-bus/test/sessionsTests.spec.ts | 27 +- .../test/streamingReceiver.spec.ts | 89 +- .../test/streamingReceiverSessions.spec.ts | 87 +- .../service-bus/test/topicFilters.spec.ts | 22 +- .../service-bus/test/track2.samples.spec.ts | 60 +- sdk/servicebus/service-bus/test/utils/misc.ts | 21 + .../service-bus/test/utils/testUtils.ts | 8 +- .../service-bus/test/utils/testutils2.ts | 23 +- 32 files changed, 734 insertions(+), 1918 deletions(-) delete mode 100644 sdk/servicebus/service-bus/src/internalReceivers.ts create mode 100644 sdk/servicebus/service-bus/test/utils/misc.ts diff --git a/sdk/servicebus/service-bus/review/service-bus.api.md b/sdk/servicebus/service-bus/review/service-bus.api.md index 97467a71e873..315934d3e8b3 100644 --- a/sdk/servicebus/service-bus/review/service-bus.api.md +++ b/sdk/servicebus/service-bus/review/service-bus.api.md @@ -30,26 +30,6 @@ export type AuthorizationRule = { secondaryKey?: string; }; -// @public -export interface Closeable { - close(): Promise; -} - -// @public -export type ContextType = LockModeT extends "peekLock" ? ContextWithSettlement : LockModeT extends "receiveAndDelete" ? {} : never; - -// @public -export interface ContextWithSettlement { - abandon(message: ReceivedMessage, propertiesToModify?: { - [key: string]: any; - }): Promise; - complete(message: ReceivedMessage): Promise; - deadLetter(message: ReceivedMessage, options?: DeadLetterOptions): Promise; - defer(message: ReceivedMessage, propertiesToModify?: { - [key: string]: any; - }): Promise; -} - // @public export interface CorrelationFilter { contentType?: string; @@ -79,7 +59,7 @@ export { Delivery } export type EntityStatus = "Active" | "Creating" | "Deleting" | "ReceiveDisabled" | "SendDisabled" | "Disabled" | "Renaming" | "Restoring" | "Unknown"; // @public -export interface GetMessageIteratorOptions extends OperationOptions { +export interface GetMessageIteratorOptions extends OperationOptions, WaitTimeOptions { } // @public @@ -90,12 +70,6 @@ export interface GetSessionReceiverOptions extends OperationOptions { export { HttpOperationResponse } -// @public -export interface MessageAndContext { - context: ContextT; - message: ReceivedMessage; -} - // @public export type MessageCountDetails = { activeMessageCount: number; @@ -113,26 +87,13 @@ export interface MessageHandlerOptions { } // @public -export interface MessageHandlers { +export interface MessageHandlers { processError(err: Error): Promise; - processMessage(message: ReceivedMessage, context: ContextT): Promise; + processMessage(message: ReceivedMessageT): Promise; } -// @public -export type MessageIterator = AsyncIterable>; - export { MessagingError } -// @public -export interface OnError { - (error: MessagingError | Error): void; -} - -// @public -export interface OnMessage { - (message: ServiceBusMessage): Promise; -} - // @public export interface QueueDetails { accessedOn?: string; @@ -185,14 +146,11 @@ export interface QueueOptions { } // @public -export interface ReceiveBatchOptions extends OperationOptions { +export interface ReceiveBatchOptions extends OperationOptions, WaitTimeOptions { } // @public -export type ReceivedMessage = Omit; - -// @public -export interface ReceivedMessageInfo extends SendableMessageInfo { +export interface ReceivedMessage extends ServiceBusMessage { readonly _amqpMessage: AmqpMessage; readonly deadLetterSource?: string; readonly deliveryCount?: number; @@ -204,6 +162,19 @@ export interface ReceivedMessageInfo extends SendableMessageInfo { readonly sequenceNumber?: Long; } +// @public +export interface ReceivedMessageWithLock extends ReceivedMessage { + abandon(propertiesToModify?: { + [key: string]: any; + }): Promise; + complete(): Promise; + deadLetter(options?: DeadLetterOptions): Promise; + defer(propertiesToModify?: { + [key: string]: any; + }): Promise; + renewLock(): Promise; +} + // @public export enum ReceiveMode { peekLock = 1, @@ -211,7 +182,7 @@ export enum ReceiveMode { } // @public -export interface Receiver { +export interface Receiver { close(): Promise; diagnostics: { peek(maxMessageCount?: number): Promise; @@ -220,20 +191,13 @@ export interface Receiver { entityPath: string; entityType: "queue" | "subscription"; getDeadLetterPath(): string; - getMessageIterator(options?: GetMessageIteratorOptions): AsyncIterableIterator<{ - message: ReceivedMessage; - context: ContextT; - }>; + getMessageIterator(options?: GetMessageIteratorOptions): AsyncIterableIterator; isReceivingMessages(): boolean; - receiveBatch(maxMessages: number, maxWaitTimeInSeconds?: number, options?: ReceiveBatchOptions): Promise<{ - messages: ReceivedMessage[]; - context: ContextT; - }>; - receiveDeferredMessage(sequenceNumber: Long, options?: OperationOptions): Promise; - receiveDeferredMessages(sequenceNumbers: Long[], options?: OperationOptions): Promise; + receiveBatch(maxMessages: number, options?: ReceiveBatchOptions): Promise; + receiveDeferredMessage(sequenceNumber: Long, options?: OperationOptions): Promise; + receiveDeferredMessages(sequenceNumbers: Long[], options?: OperationOptions): Promise; receiveMode: "peekLock" | "receiveAndDelete"; - renewMessageLock(lockTokenOrMessage: string | ReceivedMessage): Promise; - subscribe(handler: MessageHandlers, options?: SubscribeOptions): void; + subscribe(handlers: MessageHandlers, options?: SubscribeOptions): void; } export { RetryOptions } @@ -261,36 +225,16 @@ export interface RuleOptions { filter?: SqlFilter | CorrelationFilter; } -// @public -export interface SendableMessageInfo { - body: any; - contentType?: string; - correlationId?: string | number | Buffer; - label?: string; - messageId?: string | number | Buffer; - partitionKey?: string; - replyTo?: string; - replyToSessionId?: string; - scheduledEnqueueTimeUtc?: Date; - sessionId?: string; - timeToLive?: number; - to?: string; - userProperties?: { - [key: string]: any; - }; - viaPartitionKey?: string; -} - // @public export interface Sender { cancelScheduledMessage(sequenceNumber: Long): Promise; cancelScheduledMessages(sequenceNumbers: Long[]): Promise; close(): Promise; isClosed: boolean; - scheduleMessage(scheduledEnqueueTimeUtc: Date, message: SendableMessageInfo): Promise; - scheduleMessages(scheduledEnqueueTimeUtc: Date, messages: SendableMessageInfo[]): Promise; - send(message: SendableMessageInfo): Promise; - sendBatch(messages: SendableMessageInfo[]): Promise; + scheduleMessage(scheduledEnqueueTimeUtc: Date, message: ServiceBusMessage): Promise; + scheduleMessages(scheduledEnqueueTimeUtc: Date, messages: ServiceBusMessage[]): Promise; + send(message: ServiceBusMessage): Promise; + sendBatch(messages: ServiceBusMessage[]): Promise; } // @public @@ -298,15 +242,15 @@ export class ServiceBusClient { constructor(connectionString: string, options?: ServiceBusClientOptions); constructor(hostName: string, tokenCredential: TokenCredential, options?: ServiceBusClientOptions); close(): Promise; - getReceiver(queueName: string, receiveMode: "peekLock"): Receiver; - getReceiver(queueName: string, receiveMode: "receiveAndDelete"): Receiver<{}>; - getReceiver(topicName: string, subscriptionName: string, receiveMode: "peekLock"): Receiver & SubscriptionRuleManagement; - getReceiver(topicName: string, subscriptionName: string, receiveMode: "receiveAndDelete"): Receiver<{}> & SubscriptionRuleManagement; + getReceiver(queueName: string, receiveMode: "peekLock"): Receiver; + getReceiver(queueName: string, receiveMode: "receiveAndDelete"): Receiver; + getReceiver(topicName: string, subscriptionName: string, receiveMode: "peekLock"): Receiver & SubscriptionRuleManagement; + getReceiver(topicName: string, subscriptionName: string, receiveMode: "receiveAndDelete"): Receiver & SubscriptionRuleManagement; getSender(queueOrTopicName: string): Sender; - getSessionReceiver(queueName: string, receiveMode: "peekLock", options?: GetSessionReceiverOptions): SessionReceiver; - getSessionReceiver(queueName: string, receiveMode: "receiveAndDelete", options?: GetSessionReceiverOptions): SessionReceiver<{}>; - getSessionReceiver(topicName: string, subscriptionName: string, receiveMode: "peekLock", options?: GetSessionReceiverOptions): SessionReceiver & SubscriptionRuleManagement; - getSessionReceiver(topicName: string, subscriptionName: string, receiveMode: "receiveAndDelete", options?: GetSessionReceiverOptions): SessionReceiver<"receiveAndDelete"> & SubscriptionRuleManagement; + getSessionReceiver(queueName: string, receiveMode: "peekLock", options?: GetSessionReceiverOptions): SessionReceiver; + getSessionReceiver(queueName: string, receiveMode: "receiveAndDelete", options?: GetSessionReceiverOptions): SessionReceiver; + getSessionReceiver(topicName: string, subscriptionName: string, receiveMode: "peekLock", options?: GetSessionReceiverOptions): SessionReceiver & SubscriptionRuleManagement; + getSessionReceiver(topicName: string, subscriptionName: string, receiveMode: "receiveAndDelete", options?: GetSessionReceiverOptions): SessionReceiver & SubscriptionRuleManagement; } // @public @@ -316,36 +260,16 @@ export interface ServiceBusClientOptions { } // @public -export class ServiceBusMessage implements ReceivedMessageInfo { - abandon(propertiesToModify?: { - [key: string]: any; - }): Promise; - readonly _amqpMessage: AmqpMessage; +export interface ServiceBusMessage { body: any; - clone(): SendableMessageInfo; - complete(): Promise; contentType?: string; correlationId?: string | number | Buffer; - deadLetter(options?: DeadLetterOptions): Promise; - readonly deadLetterSource?: string; - defer(propertiesToModify?: { - [key: string]: any; - }): Promise; - readonly delivery: Delivery; - readonly deliveryCount?: number; - readonly enqueuedSequenceNumber?: number; - readonly enqueuedTimeUtc?: Date; - readonly expiresAtUtc?: Date; - get isSettled(): boolean; label?: string; - lockedUntilUtc?: Date; - readonly lockToken?: string; messageId?: string | number | Buffer; partitionKey?: string; replyTo?: string; replyToSessionId?: string; scheduledEnqueueTimeUtc?: Date; - readonly sequenceNumber?: Long; sessionId?: string; timeToLive?: number; to?: string; @@ -355,17 +279,6 @@ export class ServiceBusMessage implements ReceivedMessageInfo { viaPartitionKey?: string; } -// @public -export interface Session { - connections?: SessionConnections; - id?: string; - maxSessionAutoRenewLockDurationInSeconds?: number; -} - -// @public -export class SessionConnections { -} - // @public export interface SessionMessageHandlerOptions { autoComplete?: boolean; @@ -373,7 +286,7 @@ export interface SessionMessageHandlerOptions { } // @public -export interface SessionReceiver extends Receiver { +export interface SessionReceiver extends Receiver { diagnostics: { peek(maxMessageCount?: number): Promise; peekBySequenceNumber(fromSequenceNumber: Long, maxMessageCount?: number): Promise; @@ -514,6 +427,11 @@ export interface TopicOptions { userMetadata?: string; } +// @public +export interface WaitTimeOptions { + maxWaitTimeSeconds: number; +} + export { WebSocketImpl } export { WebSocketOptions } diff --git a/sdk/servicebus/service-bus/src/core/batchingReceiver.ts b/sdk/servicebus/service-bus/src/core/batchingReceiver.ts index de57886b56f0..dc011cf149d6 100644 --- a/sdk/servicebus/service-bus/src/core/batchingReceiver.ts +++ b/sdk/servicebus/service-bus/src/core/batchingReceiver.ts @@ -4,7 +4,7 @@ import * as log from "../log"; import { Constants, translate, MessagingError } from "@azure/core-amqp"; import { ReceiverEvents, EventContext, OnAmqpEvent, SessionEvents, AmqpError } from "rhea-promise"; -import { ServiceBusMessage, ReceiveMode } from "../serviceBusMessage"; +import { ServiceBusMessageImpl, ReceiveMode } from "../serviceBusMessage"; import { MessageReceiver, ReceiveOptions, @@ -71,19 +71,22 @@ export class BatchingReceiver extends MessageReceiver { * @param maxWaitTimeInSeconds The total wait time in seconds until which the receiver will attempt to receive specified number of messages. * If this time elapses before the `maxMessageCount` is reached, then messages collected till then will be returned to the user. * - **Default**: `60` seconds. - * @returns {Promise} A promise that resolves with an array of Message objects. + * @returns {Promise} A promise that resolves with an array of Message objects. */ - receive(maxMessageCount: number, maxWaitTimeInSeconds?: number): Promise { + receive( + maxMessageCount: number, + maxWaitTimeInSeconds?: number + ): Promise { throwErrorIfConnectionClosed(this._context.namespace); if (maxWaitTimeInSeconds == null) { maxWaitTimeInSeconds = Constants.defaultOperationTimeoutInMs / 1000; } - const brokeredMessages: ServiceBusMessage[] = []; + const brokeredMessages: ServiceBusMessageImpl[] = []; this.isReceivingMessages = true; - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { let totalWaitTimer: NodeJS.Timer | undefined; const onSessionError: OnAmqpEvent = (context: EventContext) => { @@ -182,7 +185,7 @@ export class BatchingReceiver extends MessageReceiver { const onReceiveMessage: OnAmqpEventAsPromise = async (context: EventContext) => { this.resetTimerOnNewMessageReceived(); try { - const data: ServiceBusMessage = new ServiceBusMessage( + const data: ServiceBusMessageImpl = new ServiceBusMessageImpl( this._context, context.message!, context.delivery!, diff --git a/sdk/servicebus/service-bus/src/core/managementClient.ts b/sdk/servicebus/service-bus/src/core/managementClient.ts index 1aca1feb7453..450295fca67e 100644 --- a/sdk/servicebus/service-bus/src/core/managementClient.ts +++ b/sdk/servicebus/service-bus/src/core/managementClient.ts @@ -25,9 +25,9 @@ import { } from "@azure/core-amqp"; import { ClientEntityContext } from "../clientEntityContext"; import { - ReceivedMessageInfo, + ReceivedMessage, + ServiceBusMessageImpl, ServiceBusMessage, - SendableMessageInfo, DispositionStatus, toAmqpMessage, getMessagePropertyTypeMismatchError @@ -341,7 +341,7 @@ export class ManagementClient extends LinkEntity { * @param {number} [messageCount] The number of messages to retrieve. Default value `1`. * @returns Promise */ - async peek(messageCount?: number): Promise { + async peek(messageCount?: number): Promise { throwErrorIfConnectionClosed(this._context.namespace); return this.peekBySequenceNumber(this._lastPeekedSequenceNumber.add(1), messageCount); } @@ -361,7 +361,7 @@ export class ManagementClient extends LinkEntity { async peekMessagesBySession( sessionId: string, messageCount?: number - ): Promise { + ): Promise { throwErrorIfConnectionClosed(this._context.namespace); return this.peekBySequenceNumber( this._lastPeekedSequenceNumber.add(1), @@ -381,7 +381,7 @@ export class ManagementClient extends LinkEntity { fromSequenceNumber: Long, maxMessageCount?: number, sessionId?: string - ): Promise { + ): Promise { throwErrorIfConnectionClosed(this._context.namespace); const connId = this._context.namespace.connectionId; @@ -399,7 +399,7 @@ export class ManagementClient extends LinkEntity { maxMessageCount = 1; } - const messageList: ReceivedMessageInfo[] = []; + const messageList: ReceivedMessage[] = []; try { const messageBody: any = {}; messageBody[Constants.fromSequenceNumber] = types.wrap_long( @@ -534,7 +534,7 @@ export class ManagementClient extends LinkEntity { */ async scheduleMessages( scheduledEnqueueTimeUtc: Date, - messages: SendableMessageInfo[] + messages: ServiceBusMessage[] ): Promise { throwErrorIfConnectionClosed(this._context.namespace); const messageBody: any[] = []; @@ -708,10 +708,10 @@ export class ManagementClient extends LinkEntity { sequenceNumbers: Long[], receiveMode: ReceiveMode, sessionId?: string - ): Promise { + ): Promise { throwErrorIfConnectionClosed(this._context.namespace); - const messageList: ServiceBusMessage[] = []; + const messageList: ServiceBusMessageImpl[] = []; const messageBody: any = {}; messageBody[Constants.sequenceNumbers] = []; for (let i = 0; i < sequenceNumbers.length; i++) { @@ -774,7 +774,7 @@ export class ManagementClient extends LinkEntity { }[]; for (const msg of messages) { const decodedMessage = RheaMessageUtil.decode(msg.message); - const message = new ServiceBusMessage( + const message = new ServiceBusMessageImpl( this._context, decodedMessage as any, { tag: msg["lock-token"] } as any, diff --git a/sdk/servicebus/service-bus/src/core/messageReceiver.ts b/sdk/servicebus/service-bus/src/core/messageReceiver.ts index c2d75540b80a..f913907756f9 100644 --- a/sdk/servicebus/service-bus/src/core/messageReceiver.ts +++ b/sdk/servicebus/service-bus/src/core/messageReceiver.ts @@ -22,7 +22,7 @@ import { import * as log from "../log"; import { LinkEntity } from "./linkEntity"; import { ClientEntityContext } from "../clientEntityContext"; -import { ServiceBusMessage, DispositionType, ReceiveMode } from "../serviceBusMessage"; +import { ServiceBusMessageImpl, DispositionType, ReceiveMode } from "../serviceBusMessage"; import { getUniqueName, calculateRenewAfterDuration } from "../util/utils"; import { MessageHandlerOptions } from "../models"; @@ -88,7 +88,7 @@ export interface OnMessage { /** * Handler for processing each incoming message. */ - (message: ServiceBusMessage): Promise; + (message: ServiceBusMessageImpl): Promise; } /** @@ -342,7 +342,7 @@ export class MessageReceiver extends LinkEntity { this.resetTimerOnNewMessageReceived(); const connectionId = this._context.namespace.connectionId; - const bMessage: ServiceBusMessage = new ServiceBusMessage( + const bMessage: ServiceBusMessageImpl = new ServiceBusMessageImpl( this._context, context.message!, context.delivery!, @@ -1014,7 +1014,7 @@ export class MessageReceiver extends LinkEntity { * @param options Optional parameters that can be provided while disposing the message. */ async settleMessage( - message: ServiceBusMessage, + message: ServiceBusMessageImpl, operation: DispositionType, options?: DispositionOptions ): Promise { diff --git a/sdk/servicebus/service-bus/src/core/messageSender.ts b/sdk/servicebus/service-bus/src/core/messageSender.ts index c7718ff3172d..5af236a79031 100644 --- a/sdk/servicebus/service-bus/src/core/messageSender.ts +++ b/sdk/servicebus/service-bus/src/core/messageSender.ts @@ -25,7 +25,7 @@ import { MessagingError } from "@azure/core-amqp"; import { - SendableMessageInfo, + ServiceBusMessage, toAmqpMessage, getMessagePropertyTypeMismatchError } from "../serviceBusMessage"; @@ -530,10 +530,10 @@ export class MessageSender extends LinkEntity { /** * Sends the given message, with the given options on this link * - * @param {SendableMessageInfo} data Message to send. Will be sent as UTF8-encoded JSON string. + * @param {ServiceBusMessage} data Message to send. Will be sent as UTF8-encoded JSON string. * @returns {Promise} */ - async send(data: SendableMessageInfo): Promise { + async send(data: ServiceBusMessage): Promise { throwErrorIfConnectionClosed(this._context.namespace); try { if (!this.isOpen()) { @@ -589,7 +589,7 @@ export class MessageSender extends LinkEntity { * Batch message. * @return {Promise} */ - async sendBatch(inputMessages: SendableMessageInfo[]): Promise { + async sendBatch(inputMessages: ServiceBusMessage[]): Promise { throwErrorIfConnectionClosed(this._context.namespace); try { if (!Array.isArray(inputMessages)) { diff --git a/sdk/servicebus/service-bus/src/index.ts b/sdk/servicebus/service-bus/src/index.ts index 1d77f1cc8f07..575858024c79 100644 --- a/sdk/servicebus/service-bus/src/index.ts +++ b/sdk/servicebus/service-bus/src/index.ts @@ -15,17 +15,16 @@ export { WebSocketOptions } from "@azure/core-amqp"; -export { OnError, OnMessage } from "./core/messageReceiver"; export { SessionReceiverOptions, SessionMessageHandlerOptions } from "./session/messageSession"; export { CorrelationFilter, RuleDescription } from "./core/managementClient"; export { + ReceivedMessage, ServiceBusMessage, - ReceivedMessageInfo, - SendableMessageInfo, DeadLetterOptions, - ReceiveMode + ReceiveMode, + ReceivedMessageWithLock } from "./serviceBusMessage"; export { Delivery, WebSocketImpl } from "rhea-promise"; @@ -48,20 +47,13 @@ export { export { MessageCountDetails, AuthorizationRule, EntityStatus } from "./util/utils"; export { - SessionConnections, - ReceivedMessage, - ContextWithSettlement, - Session, GetMessageIteratorOptions, - ReceiveBatchOptions, - SubscribeOptions, + GetSessionReceiverOptions, MessageHandlerOptions, MessageHandlers, - ContextType, - Closeable, - MessageAndContext, - MessageIterator, - GetSessionReceiverOptions + ReceiveBatchOptions, + SubscribeOptions, + WaitTimeOptions } from "./models"; export { Receiver, SubscriptionRuleManagement } from "./receivers/receiver"; diff --git a/sdk/servicebus/service-bus/src/internalReceivers.ts b/sdk/servicebus/service-bus/src/internalReceivers.ts deleted file mode 100644 index bb82c92d8e09..000000000000 --- a/sdk/servicebus/service-bus/src/internalReceivers.ts +++ /dev/null @@ -1,901 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -import Long from "long"; -import * as log from "./log"; -import { StreamingReceiver } from "./core/streamingReceiver"; -import { BatchingReceiver } from "./core/batchingReceiver"; -import { ReceiveOptions, OnError, OnMessage } from "./core/messageReceiver"; -import { ClientEntityContext } from "./clientEntityContext"; -import { ServiceBusMessage, ReceiveMode, ReceivedMessageInfo } from "./serviceBusMessage"; -import { - MessageSession, - SessionMessageHandlerOptions, - SessionReceiverOptions -} from "./session/messageSession"; -import { - getAlreadyReceivingErrorMsg, - getOpenReceiverErrorMsg, - getReceiverClosedErrorMsg, - throwErrorIfConnectionClosed, - throwTypeErrorIfParameterMissing, - throwTypeErrorIfParameterNotLong, - throwTypeErrorIfParameterNotLongArray, - getErrorMessageNotSupportedInReceiveAndDeleteMode, - throwErrorIfClientOrConnectionClosed -} from "./util/errors"; -import { RuleDescription, CorrelationFilter } from "."; -import { MessageHandlerOptions, ReceivedMessage } from "./models"; -import { - addSubscriptionRule, - removeSubscriptionRule, - getSubscriptionRules -} from "./receivers/shared"; - -/** - * The Receiver class can be used to receive messages in a batch or by registering handlers. - * Use the `createReceiver` function on the QueueClient or SubscriptionClient to instantiate a Receiver. - * The Receiver class is an abstraction over the underlying AMQP receiver link. - * @class Receiver - * @internal - * @ignore - */ -export class InternalReceiver { - /** - * @property Describes the amqp connection context for the QueueClient. - */ - private _context: ClientEntityContext; - private _receiveMode: ReceiveMode; - /** - * @property {boolean} [_isClosed] Denotes if close() was called on this receiver - */ - private _isClosed: boolean = false; - - /** - * @throws Error if the underlying connection is closed. - */ - constructor(context: ClientEntityContext, receiveMode: ReceiveMode) { - throwErrorIfConnectionClosed(context.namespace); - this._context = context; - - this._receiveMode = - receiveMode === ReceiveMode.receiveAndDelete ? receiveMode : ReceiveMode.peekLock; - } - - private _throwIfAlreadyReceiving(): void { - if (this.isReceivingMessages()) { - const errorMessage = getAlreadyReceivingErrorMsg(this._context.entityPath); - const error = new Error(errorMessage); - log.error(`[${this._context.namespace.connectionId}] %O`, error); - throw error; - } - } - - private _throwIfReceiverOrConnectionClosed(): void { - throwErrorIfConnectionClosed(this._context.namespace); - if (this.isClosed) { - const errorMessage = getReceiverClosedErrorMsg( - this._context.entityPath, - this._context.clientType, - this._context.isClosed - ); - const error = new Error(errorMessage); - log.error(`[${this._context.namespace.connectionId}] %O`, error); - throw error; - } - } - - /** - * @property Denotes receiveMode of this receiver. - * @readonly - */ - public get receiveMode(): ReceiveMode { - return this._receiveMode; - } - - /** - * @property Returns `true` if the receiver is closed. This can happen either because the receiver - * itself has been closed or the client that created it has been closed. - * @readonly - */ - public get isClosed(): boolean { - return this._isClosed || this._context.isClosed; - } - - /** - * Registers handlers to deal with the incoming stream of messages over an AMQP receiver link - * from a Queue/Subscription. - * To stop receiving messages, call `close()` on the Receiver. - * - * Throws an error if there is another receive operation in progress on the same receiver. If you - * are not sure whether there is another receive operation running, check the `isReceivingMessages` - * property on the receiver. - * - * @param onMessage - Handler for processing each incoming message. - * @param onError - Handler for any error that occurs while receiving or processing messages. - * @param options - Options to control if messages should be automatically completed, and/or have - * their locks automatically renewed. You can control the maximum number of messages that should - * be concurrently processed. You can also provide a timeout in seconds to denote the - * amount of time to wait for a new message before closing the receiver. - * - * @returns void - * @throws Error if the underlying connection or receiver is closed. - * @throws Error if current receiver is already in state of receiving messages. - * @throws MessagingError if the service returns an error while receiving messages. These are bubbled up to be handled by user provided `onError` handler. - */ - registerMessageHandler( - onMessage: OnMessage, - onError: OnError, - options?: MessageHandlerOptions - ): void { - this._throwIfReceiverOrConnectionClosed(); - this._throwIfAlreadyReceiving(); - const connId = this._context.namespace.connectionId; - throwTypeErrorIfParameterMissing(connId, "onMessage", onMessage); - throwTypeErrorIfParameterMissing(connId, "onError", onError); - if (typeof onMessage !== "function") { - throw new TypeError("The parameter 'onMessage' must be of type 'function'."); - } - if (typeof onError !== "function") { - throw new TypeError("The parameter 'onError' must be of type 'function'."); - } - - StreamingReceiver.create(this._context, { - ...options, - receiveMode: this._receiveMode - }) - .then(async (sReceiver) => { - if (!sReceiver) { - return; - } - if (!this.isClosed) { - sReceiver.receive(onMessage, onError); - } else { - await sReceiver.close(); - } - return; - }) - .catch((err) => { - onError(err); - }); - } - - /** - * Returns a promise that resolves to an array of messages based on given count and timeout over - * an AMQP receiver link from a Queue/Subscription. - * - * Throws an error if there is another receive operation in progress on the same receiver. If you - * are not sure whether there is another receive operation running, check the `isReceivingMessages` - * property on the receiver. - * - * @param maxMessageCount The maximum number of messages to receive from Queue/Subscription. - * @param maxWaitTimeInSeconds The total wait time in seconds until which the receiver will attempt to receive specified number of messages. - * If this time elapses before the `maxMessageCount` is reached, then messages collected till then will be returned to the user. - * - **Default**: `60` seconds. - * @returns Promise A promise that resolves with an array of Message objects. - * @throws Error if the underlying connection, client or receiver is closed. - * @throws Error if current receiver is already in state of receiving messages. - * @throws MessagingError if the service returns an error while receiving messages. - */ - async receiveMessages( - maxMessageCount: number, - maxWaitTimeInSeconds?: number - ): Promise { - this._throwIfReceiverOrConnectionClosed(); - this._throwIfAlreadyReceiving(); - - if (!this._context.batchingReceiver || !this._context.batchingReceiver.isOpen()) { - const options: ReceiveOptions = { - maxConcurrentCalls: 0, - receiveMode: this._receiveMode - }; - this._context.batchingReceiver = BatchingReceiver.create(this._context, options); - } - - return this._context.batchingReceiver.receive(maxMessageCount, maxWaitTimeInSeconds); - } - - /** - * Gets an async iterator over messages from the receiver. - * - * Throws an error if there is another receive operation in progress on the same receiver. If you - * are not sure whether there is another receive operation running, check the `isReceivingMessages` - * property on the receiver. - * - * If the iterator is not able to fetch a new message in over a minute, `undefined` will be returned. - * @throws Error if the underlying connection, client or receiver is closed. - * @throws Error if current receiver is already in state of receiving messages. - * @throws MessagingError if the service returns an error while receiving messages. - */ - async *getMessageIterator(): AsyncIterableIterator { - while (true) { - const currentBatch = await this.receiveMessages(1); - yield currentBatch[0]; - } - } - - /** - * Renews the lock on the message for the duration as specified during the Queue/Subscription - * creation. - * - Check the `lockedUntilUtc` property on the message for the time when the lock expires. - * - If a message is not settled (using either `complete()`, `defer()` or `deadletter()`, - * before its lock expires, then the message lands back in the Queue/Subscription for the next - * receive operation. - * - * @param lockTokenOrMessage - The `lockToken` property of the message or the message itself. - * @returns Promise - New lock token expiry date and time in UTC format. - * @throws Error if the underlying connection, client or receiver is closed. - * @throws MessagingError if the service returns an error while renewing message lock. - */ - async renewMessageLock(lockTokenOrMessage: string | ReceivedMessage): Promise { - this._throwIfReceiverOrConnectionClosed(); - if (this._receiveMode !== ReceiveMode.peekLock) { - throw new Error(getErrorMessageNotSupportedInReceiveAndDeleteMode("renew the message lock")); - } - throwTypeErrorIfParameterMissing( - this._context.namespace.connectionId, - "lockTokenOrMessage", - lockTokenOrMessage - ); - - const lockToken = - lockTokenOrMessage instanceof ServiceBusMessage - ? String(lockTokenOrMessage.lockToken) - : String(lockTokenOrMessage); - - const lockedUntilUtc = await this._context.managementClient!.renewLock(lockToken); - - return lockedUntilUtc; - } - - /** - * Returns a promise that resolves to a deferred message identified by the given `sequenceNumber`. - * @param sequenceNumber The sequence number of the message that needs to be received. - * @returns Promise - * - Returns `Message` identified by sequence number. - * - Returns `undefined` if no such message is found. - * @throws Error if the underlying connection, client or receiver is closed. - * @throws MessagingError if the service returns an error while receiving deferred message. - */ - async receiveDeferredMessage(sequenceNumber: Long): Promise { - this._throwIfReceiverOrConnectionClosed(); - throwTypeErrorIfParameterMissing( - this._context.namespace.connectionId, - "sequenceNumber", - sequenceNumber - ); - throwTypeErrorIfParameterNotLong( - this._context.namespace.connectionId, - "sequenceNumber", - sequenceNumber - ); - - const messages = await this._context.managementClient!.receiveDeferredMessages( - [sequenceNumber], - this._receiveMode - ); - return messages[0]; - } - - /** - * Returns a promise that resolves to an array of deferred messages identified by given `sequenceNumbers`. - * @param sequenceNumbers An array of sequence numbers for the messages that need to be received. - * @returns Promise - * - Returns a list of messages identified by the given sequenceNumbers. - * - Returns an empty list if no messages are found. - * @throws Error if the underlying connection, client or receiver is closed. - * @throws MessagingError if the service returns an error while receiving deferred messages. - */ - async receiveDeferredMessages(sequenceNumbers: Long[]): Promise { - this._throwIfReceiverOrConnectionClosed(); - throwTypeErrorIfParameterMissing( - this._context.namespace.connectionId, - "sequenceNumbers", - sequenceNumbers - ); - if (!Array.isArray(sequenceNumbers)) { - sequenceNumbers = [sequenceNumbers]; - } - throwTypeErrorIfParameterNotLongArray( - this._context.namespace.connectionId, - "sequenceNumbers", - sequenceNumbers - ); - - return this._context.managementClient!.receiveDeferredMessages( - sequenceNumbers, - this._receiveMode - ); - } - - // ManagementClient methods # Begin - - peek(entityPath: string, maxMessageCount?: number): Promise { - throwErrorIfClientOrConnectionClosed( - this._context.namespace, - entityPath, - this._context.isClosed - ); - - return this._context.managementClient!.peek(maxMessageCount); - } - - peekBySequenceNumber( - entityPath: string, - fromSequenceNumber: Long, - maxMessageCount?: number - ): Promise { - throwErrorIfClientOrConnectionClosed( - this._context.namespace, - entityPath, - this._context.isClosed - ); - - return this._context.managementClient!.peekBySequenceNumber( - fromSequenceNumber, - maxMessageCount - ); - } - - // /** - // * Lists the ids of the sessions on the ServiceBus Queue. - // * @param maxNumberOfSessions Maximum number of sessions. - // * @param lastUpdateTime Filter to include only sessions updated after a given time. Default - // * value is 3 days before the current time. - // */ - // async listMessageSessions( - // maxNumberOfSessions: number, - // lastUpdatedTime?: Date - // ): Promise { - // TODO: Parameter validation if required - // this.throwErrorIfClientOrConnectionClosed(); - // return this._context.managementClient!.listMessageSessions( - // 0, - // maxNumberOfSessions, - // lastUpdatedTime - // ); - // } - - // ManagementClient methods # End - - // #region topic-filters - - getRules(): Promise { - return getSubscriptionRules(this._context); - } - - removeRule(ruleName: string): Promise { - return removeSubscriptionRule(this._context, ruleName); - } - - addRule( - ruleName: string, - filter: boolean | string | CorrelationFilter, - sqlRuleActionExpression?: string - ): Promise { - return addSubscriptionRule(this._context, ruleName, filter, sqlRuleActionExpression); - } - - // #endregion - - /** - * Closes the underlying AMQP receiver link. - * Once closed, the receiver cannot be used for any further operations. - * Use the `createReceiver` function on the QueueClient or SubscriptionClient to instantiate - * a new Receiver - * - * @returns {Promise} - */ - async close(): Promise { - try { - this._isClosed = true; - if (this._context.namespace.connection && this._context.namespace.connection.isOpen()) { - // Close the streaming receiver. - if (this._context.streamingReceiver) { - await this._context.streamingReceiver.close(); - } - - // Close the batching receiver. - if (this._context.batchingReceiver) { - await this._context.batchingReceiver.close(); - } - - // Make sure that we clear the map of deferred messages - this._context.requestResponseLockedMessages.clear(); - } - } catch (err) { - log.error( - "[%s] An error occurred while closing the Receiver for %s: %O", - this._context.namespace.connectionId, - this._context.entityPath, - err - ); - throw err; - } - } - - /** - * Indicates whether the receiver is currently receiving messages or not. - * When this returns true, new `registerMessageHandler()` or `receiveMessages()` calls cannot be made. - */ - isReceivingMessages(): boolean { - if (this._context.streamingReceiver && this._context.streamingReceiver.isOpen()) { - return true; - } - if ( - this._context.batchingReceiver && - this._context.batchingReceiver.isOpen() && - this._context.batchingReceiver.isReceivingMessages - ) { - return true; - } - return false; - } -} - -/** - * The SessionReceiver class can be used to receive messages from a session enabled Queue or - * Subscription in a batch or by registering handlers. - * Use the `createReceiver` function on the QueueClient or SubscriptionClient to instantiate a - * SessionReceiver. - * The SessionReceiver class is an abstraction over the underlying AMQP receiver link. - * @class SessionReceiver - * @internal - * @ignore - */ -export class InternalSessionReceiver { - /** - * @property {ClientEntityContext} _context Describes the amqp connection context for the QueueClient. - */ - - private _context: ClientEntityContext; - private _receiveMode: ReceiveMode; - private _messageSession: MessageSession | undefined; - private _sessionOptions: SessionReceiverOptions; - /** - * @property {boolean} [_isClosed] Denotes if close() was called on this receiver - */ - private _isClosed: boolean = false; - private _sessionId: string | undefined; - - /** - * @internal - * @throws Error if the underlying connection is closed. - * @throws Error if an open receiver is already existing for given sessionId. - */ - constructor( - context: ClientEntityContext, - receiveMode: ReceiveMode, - sessionOptions: SessionReceiverOptions - ) { - throwErrorIfConnectionClosed(context.namespace); - this._context = context; - this._receiveMode = - receiveMode === ReceiveMode.receiveAndDelete ? receiveMode : ReceiveMode.peekLock; - this._sessionOptions = sessionOptions; - - if (sessionOptions.sessionId) { - sessionOptions.sessionId = String(sessionOptions.sessionId); - - // Check if receiver for given session already exists - if ( - this._context.messageSessions[sessionOptions.sessionId] && - this._context.messageSessions[sessionOptions.sessionId].isOpen() - ) { - const errorMessage = getOpenReceiverErrorMsg( - this._context.clientType, - this._context.entityPath, - sessionOptions.sessionId - ); - const error = new Error(errorMessage); - log.error(`[${this._context.namespace.connectionId}] %O`, error); - throw error; - } - } - } - - private _throwIfReceiverOrConnectionClosed(): void { - throwErrorIfConnectionClosed(this._context.namespace); - if (this.isClosed) { - const errorMessage = getReceiverClosedErrorMsg( - this._context.entityPath, - this._context.clientType, - this._context.isClosed, - this.sessionId! - ); - const error = new Error(errorMessage); - log.error(`[${this._context.namespace.connectionId}] %O`, error); - throw error; - } - } - - private async _createMessageSessionIfDoesntExist(): Promise { - if (this._messageSession) { - return; - } - this._context.isSessionEnabled = true; - this._messageSession = await MessageSession.create(this._context, { - sessionId: this._sessionOptions.sessionId, - maxSessionAutoRenewLockDurationInSeconds: this._sessionOptions - .maxSessionAutoRenewLockDurationInSeconds, - receiveMode: this._receiveMode - }); - // By this point, we should have a valid sessionId on the messageSession - // If not, the receiver cannot be used, so throw error. - if (this._messageSession.sessionId == null) { - const error = new Error("Something went wrong. Cannot lock a session."); - log.error(`[${this._context.namespace.connectionId}] %O`, error); - throw error; - } - this._sessionId = this._messageSession.sessionId; - delete this._context.expiredMessageSessions[this._messageSession.sessionId]; - } - - private _throwIfAlreadyReceiving(): void { - if (this.isReceivingMessages()) { - const errorMessage = getAlreadyReceivingErrorMsg(this._context.entityPath, this.sessionId); - const error = new Error(errorMessage); - log.error(`[${this._context.namespace.connectionId}] %O`, error); - throw error; - } - } - - /** - * @property Denotes receiveMode of this receiver. - * @readonly - */ - public get receiveMode(): ReceiveMode { - return this._receiveMode; - } - - /** - * @property Returns `true` if the receiver is closed. This can happen either because the receiver - * itself has been closed or the client that created it has been closed. - * @readonly - */ - public get isClosed(): boolean { - return ( - this._isClosed || (this.sessionId ? !this._context.messageSessions[this.sessionId] : false) - ); - } - - /** - * @property The id of the session from which this receiver will receive messages. - * Will return undefined until a AMQP receiver link has been successfully set up for the session. - * @readonly - */ - public get sessionId(): string | undefined { - return this._sessionId; - } - - /** - * @property The time in UTC until which the session is locked. - * Everytime `renewSessionLock()` is called, this time gets updated to current time plus the lock - * duration as specified during the Queue/Subscription creation. - * - * Will return undefined until a AMQP receiver link has been successfully set up for the session. - * - * @readonly - */ - public get sessionLockedUntilUtc(): Date | undefined { - return this._messageSession ? this._messageSession.sessionLockedUntilUtc : undefined; - } - - /** - * Renews the lock on the session for the duration as specified during the Queue/Subscription - * creation. - * - Check the `sessionLockedUntilUtc` property on the SessionReceiver for the time when the lock expires. - * - When the lock on the session expires - * - No more messages can be received using this receiver - * - If a message is not settled (using either `complete()`, `defer()` or `deadletter()`, - * before the session lock expires, then the message lands back in the Queue/Subscription for the next - * receive operation. - * - * @returns Promise - New lock token expiry date and time in UTC format. - * @throws Error if the underlying connection or receiver is closed. - * @throws MessagingError if the service returns an error while renewing session lock. - */ - async renewSessionLock(): Promise { - this._throwIfReceiverOrConnectionClosed(); - await this._createMessageSessionIfDoesntExist(); - - this._messageSession!.sessionLockedUntilUtc = await this._context.managementClient!.renewSessionLock( - this.sessionId! - ); - return this._messageSession!.sessionLockedUntilUtc!; - } - - /** - * Sets the state on the Session. For more on session states, see - * {@link https://docs.microsoft.com/en-us/azure/service-bus-messaging/message-sessions#message-session-state Session State} - * @param state The state that needs to be set. - * @throws Error if the underlying connection or receiver is closed. - * @throws MessagingError if the service returns an error while setting the session state. - */ - async setState(state: any): Promise { - this._throwIfReceiverOrConnectionClosed(); - await this._createMessageSessionIfDoesntExist(); - return this._context.managementClient!.setSessionState(this.sessionId!, state); - } - - /** - * Gets the state of the Session. For more on session states, see - * {@link https://docs.microsoft.com/en-us/azure/service-bus-messaging/message-sessions#message-session-state Session State} - * @returns Promise The state of that session - * @throws Error if the underlying connection or receiver is closed. - * @throws MessagingError if the service returns an error while retrieving session state. - */ - async getState(): Promise { - this._throwIfReceiverOrConnectionClosed(); - await this._createMessageSessionIfDoesntExist(); - return this._context.managementClient!.getSessionState(this.sessionId!); - } - - /** - * Fetches the next batch of active messages (including deferred but not deadlettered messages) in - * the current session. - * - The first call to `peek()` fetches the first active message. Each subsequent call fetches the - * subsequent message. - * - Unlike a `received` message, `peeked` message is a read-only version of the message. - * It cannot be `Completed/Abandoned/Deferred/Deadlettered`. The lock on it cannot be renewed. - * - * @param maxMessageCount The maximum number of messages to peek. Default value `1`. - * @returns Promise - * @throws Error if the underlying connection or receiver is closed. - * @throws MessagingError if the service returns an error while peeking for messages. - */ - async peek(maxMessageCount?: number): Promise { - this._throwIfReceiverOrConnectionClosed(); - await this._createMessageSessionIfDoesntExist(); - return this._context.managementClient!.peekMessagesBySession(this.sessionId!, maxMessageCount); - } - - /** - * Peeks the desired number of active messages (including deferred but not deadlettered messages) - * from the specified sequence number in the current session. - * - Unlike a `received` message, `peeked` message is a read-only version of the message. - * It cannot be `Completed/Abandoned/Deferred/Deadlettered`. The lock on it cannot be renewed. - * - * @param fromSequenceNumber The sequence number from where to read the message. - * @param [maxMessageCount] The maximum number of messages to peek. Default value `1`. - * @returns Promise - * @throws Error if the underlying connection or receiver is closed. - * @throws MessagingError if the service returns an error while peeking for messages. - */ - async peekBySequenceNumber( - fromSequenceNumber: Long, - maxMessageCount?: number - ): Promise { - this._throwIfReceiverOrConnectionClosed(); - await this._createMessageSessionIfDoesntExist(); - return this._context.managementClient!.peekBySequenceNumber( - fromSequenceNumber, - maxMessageCount, - this.sessionId - ); - } - - /** - * Returns a promise that resolves to a deferred message identified by the given `sequenceNumber`. - * @param sequenceNumber The sequence number of the message that needs to be received. - * @returns Promise - * - Returns `Message` identified by sequence number. - * - Returns `undefined` if no such message is found. - * @throws Error if the underlying connection or receiver is closed. - * @throws MessagingError if the service returns an error while receiving deferred message. - */ - async receiveDeferredMessage(sequenceNumber: Long): Promise { - this._throwIfReceiverOrConnectionClosed(); - throwTypeErrorIfParameterMissing( - this._context.namespace.connectionId, - "sequenceNumber", - sequenceNumber - ); - throwTypeErrorIfParameterNotLong( - this._context.namespace.connectionId, - "sequenceNumber", - sequenceNumber - ); - - await this._createMessageSessionIfDoesntExist(); - const messages = await this._context.managementClient!.receiveDeferredMessages( - [sequenceNumber], - this._receiveMode, - this.sessionId - ); - return messages[0]; - } - - /** - * Returns a promise that resolves to an array of deferred messages identified by given `sequenceNumbers`. - * @param sequenceNumbers An array of sequence numbers for the messages that need to be received. - * @returns Promise - * - Returns a list of messages identified by the given sequenceNumbers. - * - Returns an empty list if no messages are found. - * @throws Error if the underlying connection or receiver is closed. - * @throws MessagingError if the service returns an error while receiving deferred messages. - */ - async receiveDeferredMessages(sequenceNumbers: Long[]): Promise { - this._throwIfReceiverOrConnectionClosed(); - throwTypeErrorIfParameterMissing( - this._context.namespace.connectionId, - "sequenceNumbers", - sequenceNumbers - ); - if (!Array.isArray(sequenceNumbers)) { - sequenceNumbers = [sequenceNumbers]; - } - throwTypeErrorIfParameterNotLongArray( - this._context.namespace.connectionId, - "sequenceNumbers", - sequenceNumbers - ); - - await this._createMessageSessionIfDoesntExist(); - return this._context.managementClient!.receiveDeferredMessages( - sequenceNumbers, - this._receiveMode, - this.sessionId - ); - } - - /** - * Returns a promise that resolves to an array of messages based on given count and timeout over - * an AMQP receiver link from a Queue/Subscription. - * - * Throws an error if there is another receive operation in progress on the same receiver. If you - * are not sure whether there is another receive operation running, check the `isReceivingMessages` - * property on the receiver. - * - * @param maxMessageCount The maximum number of messages to receive from Queue/Subscription. - * @param maxWaitTimeInSeconds The total wait time in seconds until which the receiver will attempt to receive specified number of messages. - * If this time elapses before the `maxMessageCount` is reached, then messages collected till then will be returned to the user. - * - **Default**: `60` seconds. - * @returns Promise A promise that resolves with an array of Message objects. - * @throws Error if the underlying connection or receiver is closed. - * @throws Error if the receiver is already in state of receiving messages. - * @throws MessagingError if the service returns an error while receiving messages. - */ - async receiveMessages( - maxMessageCount: number, - maxWaitTimeInSeconds?: number - ): Promise { - this._throwIfReceiverOrConnectionClosed(); - this._throwIfAlreadyReceiving(); - await this._createMessageSessionIfDoesntExist(); - return this._messageSession!.receiveMessages(maxMessageCount, maxWaitTimeInSeconds); - } - - /** - * Registers handlers to deal with the incoming stream of messages over an AMQP receiver link - * from a Queue/Subscription. - * To stop receiving messages, call `close()` on the SessionReceiver. - * - * Throws an error if there is another receive operation in progress on the same receiver. If you - * are not sure whether there is another receive operation running, check the `isReceivingMessages` - * property on the receiver. - * - * @param onMessage - Handler for processing each incoming message. - * @param onError - Handler for any error that occurs while receiving or processing messages. - * @param options - Options to control whether messages should be automatically completed - * or if the lock on the session should be automatically renewed. You can control the - * maximum number of messages that should be concurrently processed. You can - * also provide a timeout in seconds to denote the amount of time to wait for a new message - * before closing the receiver. - * - * @returns void - * @throws Error if the underlying connection or receiver is closed. - * @throws Error if the receiver is already in state of receiving messages. - * @throws MessagingErrormif the service returns an error while receiving messages. These are bubbled up to be handled by user provided `onError` handler. - */ - registerMessageHandler( - onMessage: OnMessage, - onError: OnError, - options?: SessionMessageHandlerOptions - ): void { - this._throwIfReceiverOrConnectionClosed(); - this._throwIfAlreadyReceiving(); - const connId = this._context.namespace.connectionId; - throwTypeErrorIfParameterMissing(connId, "onMessage", onMessage); - throwTypeErrorIfParameterMissing(connId, "onError", onError); - if (typeof onMessage !== "function") { - throw new TypeError("The parameter 'onMessage' must be of type 'function'."); - } - if (typeof onError !== "function") { - throw new TypeError("The parameter 'onError' must be of type 'function'."); - } - - this._createMessageSessionIfDoesntExist() - .then(async () => { - if (!this._messageSession) { - return; - } - if (!this._isClosed) { - this._messageSession.receive(onMessage, onError, options); - } else { - await this._messageSession.close(); - } - return; - }) - .catch((err) => { - onError(err); - }); - } - - /** - * Gets an async iterator over messages from the receiver. - * - * Throws an error if there is another receive operation in progress on the same receiver. If you - * are not sure whether there is another receive operation running, check the `isReceivingMessages` - * property on the receiver. - * - * If the iterator is not able to fetch a new message in over a minute, `undefined` will be returned - * @throws Error if the underlying connection or receiver is closed. - * @throws Error if the receiver is already in state of receiving messages. - * @throws MessagingError if the service returns an error while receiving messages. - */ - async *getMessageIterator(): AsyncIterableIterator { - while (true) { - const currentBatch = await this.receiveMessages(1); - yield currentBatch[0]; - } - } - - // #region topic-filters - - getRules(): Promise { - return getSubscriptionRules(this._context); - } - - removeRule(ruleName: string): Promise { - return removeSubscriptionRule(this._context, ruleName); - } - - addRule( - ruleName: string, - filter: boolean | string | CorrelationFilter, - sqlRuleActionExpression?: string - ): Promise { - return addSubscriptionRule(this._context, ruleName, filter, sqlRuleActionExpression); - } - - // #endregion - - /** - * Closes the underlying AMQP receiver link. - * Once closed, the receiver cannot be used for any further operations. - * Use the `createReceiver` function on the QueueClient or SubscriptionClient to instantiate - * a new Receiver - * - * @returns {Promise} - */ - async close(): Promise { - try { - if (this._messageSession) { - await this._messageSession.close(); - this._messageSession = undefined; - } - } catch (err) { - log.error( - "[%s] An error occurred while closing the SessionReceiver for session %s in %s: %O", - this._context.namespace.connectionId, - this.sessionId, - this._context.entityPath, - err - ); - throw err; - } finally { - this._isClosed = true; - } - } - - /** - * Indicates whether the receiver is currently receiving messages or not. - * When this returns true, new `registerMessageHandler()` or `receiveMessages()` calls cannot be made. - */ - isReceivingMessages(): boolean { - return this._messageSession ? this._messageSession.isReceivingMessages : false; - } -} - -// #region topic-filters diff --git a/sdk/servicebus/service-bus/src/models.ts b/sdk/servicebus/service-bus/src/models.ts index b3e15cc60b71..8daf644f5593 100644 --- a/sdk/servicebus/service-bus/src/models.ts +++ b/sdk/servicebus/service-bus/src/models.ts @@ -1,113 +1,19 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -import { ServiceBusMessage, DeadLetterOptions } from "./serviceBusMessage"; import { OperationOptions } from "@azure/core-auth"; -/** - * An opaque class, used internally to manage AMQP connections for sessions. - */ -// TODO: should probably implement Closeable so the user can just shut down all -// connections when their app is done processing. -export class SessionConnections {} - -/** - * Information needed to target a session. - */ -export interface Session { - /** - * The ID of a session to target or `undefined` to pick an session that is not currently - * owned. - */ - id?: string; - /** - * A shared instance that allows multiple sessions to share the same underlying AMQP - * connection. - * - * A single instance should be created for your application and passed to each - * ReceiverClient you create that processes a session. - */ - connections?: SessionConnections; - /** - * @property The maximum duration in seconds - * until which, the lock on the session will be renewed automatically by the sdk. - * - **Default**: `300` seconds (5 minutes). - * - **To disable autolock renewal**, set this to `0`. - */ - maxSessionAutoRenewLockDurationInSeconds?: number; -} - -export function isSession(possibleSession: Session | any): possibleSession is Session { - return possibleSession != null; -} - -/** - * A message that has been received via ServiceBusReceiver. - */ -export type ReceivedMessage = Omit< - ServiceBusMessage, - | "complete" - | "abandon" - | "defer" - | "deadletter" - // Um..am I doing something odd here or is this a normal thing to exclude? - | "prototype" ->; - -/** - * A context with methods to settle (ie, complete, abandon, etc..) - * messages. This context is only available when you open a Receiver in "PeekLock" - * mode. - */ -export interface ContextWithSettlement { - /** - * Removes the message from Service Bus. - * @returns Promise. - */ - complete(message: ReceivedMessage): Promise; - - /** - * The lock held on the message by the receiver is let go, making the message available again in - * Service Bus for another receive operation. - * @param propertiesToModify The properties of the message to modify while abandoning the message. - * - * @return Promise. - */ - abandon(message: ReceivedMessage, propertiesToModify?: { [key: string]: any }): Promise; - - /** - * Defers the processing of the message. Save the `sequenceNumber` of the message, in order to - * receive it message again in the future using the `receiveDeferredMessage` method. - * @param propertiesToModify The properties of the message to modify while deferring the message - * - * @returns Promise - */ - defer(message: ReceivedMessage, propertiesToModify?: { [key: string]: any }): Promise; - - /** - * Moves the message to the deadletter sub-queue. To receive a - * deadlettered message, create a new ServiceBusReceiver client - * using the path for the deadletter sub-queue. - * - * @param options The DeadLetter options that can be provided while - * rejecting the message. - * - * @returns Promise - */ - deadLetter(message: ReceivedMessage, options?: DeadLetterOptions): Promise; -} - /** * The general message handler interface (used for streamMessages). */ -export interface MessageHandlers { +export interface MessageHandlers { /** * Handler that processes messages from service bus. * * @param message A message received from Service Bus. * @param context A context that can be used to settle messages when in peekLock mode. */ - processMessage(message: ReceivedMessage, context: ContextT): Promise; + processMessage(message: ReceivedMessageT): Promise; /** * Handler that processes errors that occur during receiving. * @param err An error from Service Bus. @@ -116,52 +22,25 @@ export interface MessageHandlers { } /** - * Used when an object must be explicitly closed to release resources. + * Options related to wait times. */ -export interface Closeable { +export interface WaitTimeOptions { /** - * Close the object, releasing any resources. + * The maximum amount of time to wait for messages to arrive. + * **Default**: `60` seconds. */ - close(): Promise; + maxWaitTimeSeconds: number; } -/** - * A message along with an associated context. - */ -export interface MessageAndContext { - /** - * A message received from service bus. - */ - message: ReceivedMessage; - /** - * A context used to settle messages, when applicable. - */ - context: ContextT; -} - -/** - * An iterator that can also contain a context for settling messages. - */ -export type MessageIterator = AsyncIterable>; - -/** - * Type that converts PeekLock/ReceiveAndDelete into the proper Context type - */ -export type ContextType = LockModeT extends "peekLock" - ? ContextWithSettlement - : LockModeT extends "receiveAndDelete" - ? {} - : never; - /** * Options when receiving a batch of messages from Service Bus. */ -export interface ReceiveBatchOptions extends OperationOptions {} +export interface ReceiveBatchOptions extends OperationOptions, WaitTimeOptions {} /** * Options when getting an iterable iterator from Service Bus. */ -export interface GetMessageIteratorOptions extends OperationOptions {} +export interface GetMessageIteratorOptions extends OperationOptions, WaitTimeOptions {} /** * Options used when subscribing to a Service Bus queue or subscription. diff --git a/sdk/servicebus/service-bus/src/receivers/receiver.ts b/sdk/servicebus/service-bus/src/receivers/receiver.ts index ff74faecc22a..8f0a0d900805 100644 --- a/sdk/servicebus/service-bus/src/receivers/receiver.ts +++ b/sdk/servicebus/service-bus/src/receivers/receiver.ts @@ -6,18 +6,16 @@ import { SubscribeOptions, GetMessageIteratorOptions, ReceiveBatchOptions, - ReceivedMessage, MessageHandlerOptions } from "../models"; import { OperationOptions } from "@azure/core-auth"; -import { ServiceBusMessage, RuleDescription, CorrelationFilter } from ".."; +import { RuleDescription, CorrelationFilter, ReceivedMessage } from ".."; import { ClientEntityContext } from "../clientEntityContext"; import { throwErrorIfConnectionClosed, getAlreadyReceivingErrorMsg, getReceiverClosedErrorMsg, throwTypeErrorIfParameterMissing, - getErrorMessageNotSupportedInReceiveAndDeleteMode, throwTypeErrorIfParameterNotLong, throwTypeErrorIfParameterNotLongArray, throwErrorIfClientOrConnectionClosed @@ -30,59 +28,36 @@ import { getSubscriptionRules, removeSubscriptionRule, addSubscriptionRule, - settlementContext, - assertValidMessageHandlers + assertValidMessageHandlers, + getMessageIterator } from "./shared"; import { convertToInternalReceiveMode } from "../constructorHelpers"; import Long from "long"; +import { ServiceBusMessageImpl, ReceivedMessageWithLock } from "../serviceBusMessage"; /** * A receiver that does not handle sessions. */ -export interface Receiver { +export interface Receiver { /** * Streams messages to message handlers. - * @param handler A handler that gets called for messages and errors. + * @param handlers A handler that gets called for messages and errors. * @param options Options for subscribe. */ - subscribe(handler: MessageHandlers, options?: SubscribeOptions): void; + subscribe(handlers: MessageHandlers, options?: SubscribeOptions): void; /** * Returns an iterator that can be used to receive messages from Service Bus. * @param options Options for getMessageIterator. */ - getMessageIterator( - options?: GetMessageIteratorOptions - ): AsyncIterableIterator<{ message: ReceivedMessage; context: ContextT }>; + getMessageIterator(options?: GetMessageIteratorOptions): AsyncIterableIterator; /** * Receives, at most, `maxMessages` worth of messages. * @param maxMessages The maximum number of messages to accept. - * @param maxWaitTimeInSeconds The maximum time to wait, in seconds, for messages to arrive. * @param options Options for receiveBatch. */ - receiveBatch( - maxMessages: number, - maxWaitTimeInSeconds?: number, - options?: ReceiveBatchOptions - ): Promise<{ messages: ReceivedMessage[]; context: ContextT }>; - - // TODO: move to message object itself. - - /** - * Renews the lock on the message for the duration as specified during the Queue/Subscription - * creation. - * - Check the `lockedUntilUtc` property on the message for the time when the lock expires. - * - If a message is not settled (using either `complete()`, `defer()` or `deadletter()`, - * before its lock expires, then the message lands back in the Queue/Subscription for the next - * receive operation. - * - * @param lockTokenOrMessage - The `lockToken` property of the message or the message itself. - * @returns Promise - New lock token expiry date and time in UTC format. - * @throws Error if the underlying connection, client or receiver is closed. - * @throws MessagingError if the service returns an error while renewing message lock. - */ - renewMessageLock(lockTokenOrMessage: string | ReceivedMessage): Promise; + receiveBatch(maxMessages: number, options?: ReceiveBatchOptions): Promise; /** * Returns a promise that resolves to a deferred message identified by the given `sequenceNumber`. @@ -96,7 +71,7 @@ export interface Receiver { receiveDeferredMessage( sequenceNumber: Long, options?: OperationOptions - ): Promise; + ): Promise; /** * Returns a promise that resolves to an array of deferred messages identified by given `sequenceNumbers`. @@ -111,7 +86,7 @@ export interface Receiver { receiveDeferredMessages( sequenceNumbers: Long[], options?: OperationOptions - ): Promise; + ): Promise; /** * Indicates whether the receiver is currently receiving messages or not. * When this returns true, new `registerMessageHandler()` or `receiveMessages()` calls cannot be made. @@ -235,7 +210,8 @@ export interface SubscriptionRuleManagement { * @internal * @ignore */ -export class ReceiverImpl implements Receiver, SubscriptionRuleManagement { +export class ReceiverImpl + implements Receiver, SubscriptionRuleManagement { /** * @property Describes the amqp connection context for the QueueClient. */ @@ -376,9 +352,6 @@ export class ReceiverImpl implements Receiver, SubscriptionR * property on the receiver. * * @param maxMessageCount The maximum number of messages to receive from Queue/Subscription. - * @param maxWaitTimeInSeconds The total wait time in seconds until which the receiver will attempt to receive specified number of messages. - * If this time elapses before the `maxMessageCount` is reached, then messages collected till then will be returned to the user. - * - **Default**: `60` seconds. * @returns Promise A promise that resolves with an array of Message objects. * @throws Error if the underlying connection, client or receiver is closed. * @throws Error if current receiver is already in state of receiving messages. @@ -386,9 +359,8 @@ export class ReceiverImpl implements Receiver, SubscriptionR */ async receiveBatch( maxMessageCount: number, - maxWaitTimeInSeconds?: number, options?: ReceiveBatchOptions - ): Promise<{ messages: ReceivedMessage[]; context: ContextT }> { + ): Promise { this._throwIfReceiverOrConnectionClosed(); this._throwIfAlreadyReceiving(); @@ -400,15 +372,12 @@ export class ReceiverImpl implements Receiver, SubscriptionR this._context.batchingReceiver = BatchingReceiver.create(this._context, options); } - const messages = await this._context.batchingReceiver.receive( + const receivedMessages = await this._context.batchingReceiver.receive( maxMessageCount, - maxWaitTimeInSeconds + options?.maxWaitTimeSeconds ); - return { - messages, - context: this.getContext() - }; + return (receivedMessages as unknown) as ReceivedMessageT[]; } /** @@ -423,54 +392,8 @@ export class ReceiverImpl implements Receiver, SubscriptionR * @throws Error if current receiver is already in state of receiving messages. * @throws MessagingError if the service returns an error while receiving messages. */ - async *getMessageIterator( - options?: GetMessageIteratorOptions - ): AsyncIterableIterator<{ - message: ReceivedMessage; - context: ContextT; - }> { - while (true) { - const { messages, context } = await this.receiveBatch(1); - - yield { - message: messages[0], - context - }; - } - } - - /** - * Renews the lock on the message for the duration as specified during the Queue/Subscription - * creation. - * - Check the `lockedUntilUtc` property on the message for the time when the lock expires. - * - If a message is not settled (using either `complete()`, `defer()` or `deadletter()`, - * before its lock expires, then the message lands back in the Queue/Subscription for the next - * receive operation. - * - * @param lockTokenOrMessage - The `lockToken` property of the message or the message itself. - * @returns Promise - New lock token expiry date and time in UTC format. - * @throws Error if the underlying connection, client or receiver is closed. - * @throws MessagingError if the service returns an error while renewing message lock. - */ - async renewMessageLock(lockTokenOrMessage: string | ReceivedMessage): Promise { - this._throwIfReceiverOrConnectionClosed(); - if (this.receiveMode !== "peekLock") { - throw new Error(getErrorMessageNotSupportedInReceiveAndDeleteMode("renew the message lock")); - } - throwTypeErrorIfParameterMissing( - this._context.namespace.connectionId, - "lockTokenOrMessage", - lockTokenOrMessage - ); - - const lockToken = - lockTokenOrMessage instanceof ServiceBusMessage - ? String(lockTokenOrMessage.lockToken) - : String(lockTokenOrMessage); - - const lockedUntilUtc = await this._context.managementClient!.renewLock(lockToken); - - return lockedUntilUtc; + getMessageIterator(options?: GetMessageIteratorOptions): AsyncIterableIterator { + return getMessageIterator(this, options); } /** @@ -482,7 +405,7 @@ export class ReceiverImpl implements Receiver, SubscriptionR * @throws Error if the underlying connection, client or receiver is closed. * @throws MessagingError if the service returns an error while receiving deferred message. */ - async receiveDeferredMessage(sequenceNumber: Long): Promise { + async receiveDeferredMessage(sequenceNumber: Long): Promise { this._throwIfReceiverOrConnectionClosed(); throwTypeErrorIfParameterMissing( this._context.namespace.connectionId, @@ -499,7 +422,7 @@ export class ReceiverImpl implements Receiver, SubscriptionR [sequenceNumber], convertToInternalReceiveMode(this.receiveMode) ); - return messages[0]; + return (messages[0] as unknown) as ReceivedMessageT; } /** @@ -511,7 +434,7 @@ export class ReceiverImpl implements Receiver, SubscriptionR * @throws Error if the underlying connection, client or receiver is closed. * @throws MessagingError if the service returns an error while receiving deferred messages. */ - async receiveDeferredMessages(sequenceNumbers: Long[]): Promise { + async receiveDeferredMessages(sequenceNumbers: Long[]): Promise { this._throwIfReceiverOrConnectionClosed(); throwTypeErrorIfParameterMissing( this._context.namespace.connectionId, @@ -527,10 +450,12 @@ export class ReceiverImpl implements Receiver, SubscriptionR sequenceNumbers ); - return this._context.managementClient!.receiveDeferredMessages( + const deferredMessages = await this._context.managementClient!.receiveDeferredMessages( sequenceNumbers, convertToInternalReceiveMode(this.receiveMode) ); + + return (deferredMessages as any) as ReceivedMessageT[]; } // ManagementClient methods # Begin @@ -563,12 +488,12 @@ export class ReceiverImpl implements Receiver, SubscriptionR return internalMessages.map((m) => m as ReceivedMessage); } - subscribe(handlers: MessageHandlers, options?: SubscribeOptions): void { + subscribe(handlers: MessageHandlers, options?: SubscribeOptions): void { assertValidMessageHandlers(handlers); this._registerMessageHandler( - async (message: ServiceBusMessage) => { - return handlers.processMessage(message, this.getContext()); + async (message: ServiceBusMessageImpl) => { + return handlers.processMessage((message as any) as ReceivedMessageT); }, (err: Error) => { // TODO: not async internally yet. @@ -657,10 +582,4 @@ export class ReceiverImpl implements Receiver, SubscriptionR } return false; } - - private getContext(): ContextT { - return this.receiveMode === "peekLock" - ? ((settlementContext as any) as ContextT) - : ({} as ContextT); - } } diff --git a/sdk/servicebus/service-bus/src/receivers/sessionReceiver.ts b/sdk/servicebus/service-bus/src/receivers/sessionReceiver.ts index cd6a3077d051..41023d1ac06d 100644 --- a/sdk/servicebus/service-bus/src/receivers/sessionReceiver.ts +++ b/sdk/servicebus/service-bus/src/receivers/sessionReceiver.ts @@ -4,11 +4,9 @@ import { ClientEntityContext } from "../clientEntityContext"; import { SessionReceiverOptions, - ServiceBusMessage, SessionMessageHandlerOptions, RuleDescription, CorrelationFilter, - ContextWithSettlement, MessageHandlers, SubscribeOptions, ReceiveBatchOptions, @@ -32,18 +30,20 @@ import { getSubscriptionRules, removeSubscriptionRule, addSubscriptionRule, - settlementContext, - assertValidMessageHandlers + assertValidMessageHandlers, + getMessageIterator } from "./shared"; import { convertToInternalReceiveMode } from "../constructorHelpers"; import { Receiver } from "./receiver"; import Long from "long"; +import { ServiceBusMessageImpl, ReceivedMessageWithLock } from "../serviceBusMessage"; /** *A receiver that handles sessions, including renewing the session lock. */ -export interface SessionReceiver - extends Receiver { +export interface SessionReceiver< + ReceivedMessageT extends ReceivedMessage | ReceivedMessageWithLock +> extends Receiver { /** * The session ID. * Can be undefined until a AMQP receiver link has been successfully set up for the session @@ -115,8 +115,8 @@ export interface SessionReceiver * @internal * @ignore */ -export class SessionReceiverImpl - implements SessionReceiver { +export class SessionReceiverImpl + implements SessionReceiver { public entityPath: string; public sessionId: string | undefined; @@ -363,7 +363,7 @@ export class SessionReceiverImpl * @throws Error if the underlying connection or receiver is closed. * @throws MessagingError if the service returns an error while receiving deferred message. */ - async receiveDeferredMessage(sequenceNumber: Long): Promise { + async receiveDeferredMessage(sequenceNumber: Long): Promise { this._throwIfReceiverOrConnectionClosed(); throwTypeErrorIfParameterMissing( this._context.namespace.connectionId, @@ -382,7 +382,7 @@ export class SessionReceiverImpl convertToInternalReceiveMode(this.receiveMode), this.sessionId ); - return messages[0]; + return (messages[0] as any) as ReceivedMessageT; } /** @@ -394,7 +394,7 @@ export class SessionReceiverImpl * @throws Error if the underlying connection or receiver is closed. * @throws MessagingError if the service returns an error while receiving deferred messages. */ - async receiveDeferredMessages(sequenceNumbers: Long[]): Promise { + async receiveDeferredMessages(sequenceNumbers: Long[]): Promise { this._throwIfReceiverOrConnectionClosed(); throwTypeErrorIfParameterMissing( this._context.namespace.connectionId, @@ -411,11 +411,13 @@ export class SessionReceiverImpl ); await this._createMessageSessionIfDoesntExist(); - return this._context.managementClient!.receiveDeferredMessages( + const deferredMessages = await this._context.managementClient!.receiveDeferredMessages( sequenceNumbers, convertToInternalReceiveMode(this.receiveMode), this.sessionId ); + + return (deferredMessages as any) as ReceivedMessageT[]; } /** @@ -427,9 +429,6 @@ export class SessionReceiverImpl * property on the receiver. * * @param maxMessageCount The maximum number of messages to receive from Queue/Subscription. - * @param maxWaitTimeInSeconds The total wait time in seconds until which the receiver will attempt to receive specified number of messages. - * If this time elapses before the `maxMessageCount` is reached, then messages collected till then will be returned to the user. - * - **Default**: `60` seconds. * @returns Promise A promise that resolves with an array of Message objects. * @throws Error if the underlying connection or receiver is closed. * @throws Error if the receiver is already in state of receiving messages. @@ -437,30 +436,26 @@ export class SessionReceiverImpl */ async receiveBatch( maxMessageCount: number, - maxWaitTimeInSeconds?: number, - // TODO: use the options options?: ReceiveBatchOptions - ): Promise<{ messages: ReceivedMessage[]; context: ContextT }> { + ): Promise { this._throwIfReceiverOrConnectionClosed(); this._throwIfAlreadyReceiving(); await this._createMessageSessionIfDoesntExist(); - const messages = await this._messageSession!.receiveMessages( + + const receivedMessages = await this._messageSession!.receiveMessages( maxMessageCount, - maxWaitTimeInSeconds + options?.maxWaitTimeSeconds ); - return { - messages, - context: this.getContext() - }; + return (receivedMessages as any) as ReceivedMessageT[]; } - subscribe(handlers: MessageHandlers, options?: SubscribeOptions): void { + subscribe(handlers: MessageHandlers, options?: SubscribeOptions): void { assertValidMessageHandlers(handlers); this._registerMessageHandler( - async (message: ServiceBusMessage) => { - return handlers.processMessage(message, this.getContext()); + async (message: ServiceBusMessageImpl) => { + return handlers.processMessage((message as any) as ReceivedMessageT); }, (err: Error) => { // TODO: not async internally yet. @@ -538,20 +533,8 @@ export class SessionReceiverImpl * @throws Error if current receiver is already in state of receiving messages. * @throws MessagingError if the service returns an error while receiving messages. */ - async *getMessageIterator( - options?: GetMessageIteratorOptions - ): AsyncIterableIterator<{ - message: ReceivedMessage; - context: ContextT; - }> { - while (true) { - const { messages, context } = await this.receiveBatch(1); - - yield { - message: messages[0], - context - }; - } + getMessageIterator(options?: GetMessageIteratorOptions): AsyncIterableIterator { + return getMessageIterator(this, options); } // #region topic-filters @@ -615,14 +598,4 @@ export class SessionReceiverImpl isReceivingMessages(): boolean { return this._messageSession ? this._messageSession.isReceivingMessages : false; } - - private getContext(): ContextT { - return this.receiveMode === "peekLock" - ? ((settlementContext as any) as ContextT) - : ({} as ContextT); - } - - async renewMessageLock(lockTokenOrMessage: string | ReceivedMessage): Promise { - throw new Error("Move to the context"); - } } diff --git a/sdk/servicebus/service-bus/src/receivers/shared.ts b/sdk/servicebus/service-bus/src/receivers/shared.ts index e389f0885f11..bd48a3d19d62 100644 --- a/sdk/servicebus/service-bus/src/receivers/shared.ts +++ b/sdk/servicebus/service-bus/src/receivers/shared.ts @@ -1,11 +1,11 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -import { ContextWithSettlement } from "../models"; -import { ServiceBusMessage } from "../serviceBusMessage"; import { throwErrorIfClientOrConnectionClosed } from "../util/errors"; import { ClientEntityContext } from "../clientEntityContext"; import { RuleDescription, CorrelationFilter } from "../core/managementClient"; +import { GetMessageIteratorOptions } from "../models"; +import { Receiver } from "./receiver"; /** * @internal @@ -56,21 +56,6 @@ export function addSubscriptionRule( // #endregion -/** - * @internal - * @ignore - */ -export const settlementContext: ContextWithSettlement = { - // TODO: need to move the settlement methods out of sb message - - // we don't need to have this runtime dependency. - abandon: (message, propertiesToModify) => - ((message as unknown) as ServiceBusMessage).abandon(propertiesToModify), - complete: (message) => ((message as unknown) as ServiceBusMessage).complete(), - defer: (message, propertiesToModify) => - ((message as unknown) as ServiceBusMessage).defer(propertiesToModify), - deadLetter: (message, options) => ((message as unknown) as ServiceBusMessage).deadLetter(options) -}; - /** * @internal * @ignore @@ -86,3 +71,27 @@ export function assertValidMessageHandlers(handlers: any) { throw new TypeError('Invalid "MessageHandlers" provided.'); } + +/** + * @internal + * @ignore + */ +export async function* getMessageIterator( + receiver: Receiver, + options?: GetMessageIteratorOptions +): AsyncIterableIterator { + while (true) { + const messages = await receiver.receiveBatch(1, options); + + // In EventHubs we've had a concept of "punctuation" (thanks @jsquire) that + // allows the user, when working in a model like this, to get a periodic "no message + // arrived in this window of time" notification. + // + // TODO: do we want this same behavior for ServiceBus? + if (messages.length === 0) { + continue; + } + + yield messages[0]; + } +} diff --git a/sdk/servicebus/service-bus/src/sender.ts b/sdk/servicebus/service-bus/src/sender.ts index abcb6e17851f..808287e4f674 100644 --- a/sdk/servicebus/service-bus/src/sender.ts +++ b/sdk/servicebus/service-bus/src/sender.ts @@ -4,7 +4,7 @@ import Long from "long"; import * as log from "./log"; import { MessageSender } from "./core/messageSender"; -import { SendableMessageInfo } from "./serviceBusMessage"; +import { ServiceBusMessage } from "./serviceBusMessage"; import { ClientEntityContext } from "./clientEntityContext"; import { getSenderClosedErrorMsg, @@ -32,7 +32,7 @@ export interface Sender { * @throws Error if the underlying connection, client or sender is closed. * @throws MessagingError if the service returns an error while sending messages to the service. */ - send(message: SendableMessageInfo): Promise; + send(message: ServiceBusMessage): Promise; /** * Sends the given messages in a single batch i.e. in a single AMQP message after creating an AMQP @@ -49,7 +49,7 @@ export interface Sender { * @throws Error if the underlying connection, client or sender is closed. * @throws MessagingError if the service returns an error while sending messages to the service. */ - sendBatch(messages: SendableMessageInfo[]): Promise; + sendBatch(messages: ServiceBusMessage[]): Promise; /** * @property Returns `true` if either the sender or the client that created it has been closed * @readonly @@ -67,7 +67,7 @@ export interface Sender { * @throws Error if the underlying connection, client or sender is closed. * @throws MessagingError if the service returns an error while scheduling a message. */ - scheduleMessage(scheduledEnqueueTimeUtc: Date, message: SendableMessageInfo): Promise; + scheduleMessage(scheduledEnqueueTimeUtc: Date, message: ServiceBusMessage): Promise; /** * Schedules given messages to appear on Service Bus Queue/Subscription at a later time. @@ -81,7 +81,7 @@ export interface Sender { * @throws Error if the underlying connection, client or sender is closed. * @throws MessagingError if the service returns an error while scheduling messages. */ - scheduleMessages(scheduledEnqueueTimeUtc: Date, messages: SendableMessageInfo[]): Promise; + scheduleMessages(scheduledEnqueueTimeUtc: Date, messages: ServiceBusMessage[]): Promise; /** * Cancels a message that was scheduled to appear on a ServiceBus Queue/Subscription. @@ -147,14 +147,14 @@ export class SenderImpl implements Sender { return this._isClosed || this._context.isClosed; } - async send(message: SendableMessageInfo): Promise { + async send(message: ServiceBusMessage): Promise { this._throwIfSenderOrConnectionClosed(); throwTypeErrorIfParameterMissing(this._context.namespace.connectionId, "message", message); const sender = MessageSender.create(this._context); return sender.send(message); } - async sendBatch(messages: SendableMessageInfo[]): Promise { + async sendBatch(messages: ServiceBusMessage[]): Promise { this._throwIfSenderOrConnectionClosed(); throwTypeErrorIfParameterMissing(this._context.namespace.connectionId, "messages", messages); if (!Array.isArray(messages)) { @@ -176,10 +176,7 @@ export class SenderImpl implements Sender { * @throws Error if the underlying connection, client or sender is closed. * @throws MessagingError if the service returns an error while scheduling a message. */ - async scheduleMessage( - scheduledEnqueueTimeUtc: Date, - message: SendableMessageInfo - ): Promise { + async scheduleMessage(scheduledEnqueueTimeUtc: Date, message: ServiceBusMessage): Promise { this._throwIfSenderOrConnectionClosed(); throwTypeErrorIfParameterMissing( this._context.namespace.connectionId, @@ -198,7 +195,7 @@ export class SenderImpl implements Sender { async scheduleMessages( scheduledEnqueueTimeUtc: Date, - messages: SendableMessageInfo[] + messages: ServiceBusMessage[] ): Promise { this._throwIfSenderOrConnectionClosed(); throwTypeErrorIfParameterMissing( diff --git a/sdk/servicebus/service-bus/src/serviceBusClient.ts b/sdk/servicebus/service-bus/src/serviceBusClient.ts index bf273f924417..f04cd6d1b641 100644 --- a/sdk/servicebus/service-bus/src/serviceBusClient.ts +++ b/sdk/servicebus/service-bus/src/serviceBusClient.ts @@ -12,9 +12,10 @@ import { ConnectionContext } from "./connectionContext"; import { ClientEntityContext } from "./clientEntityContext"; import { ClientType } from "./client"; import { SenderImpl, Sender } from "./sender"; -import { GetSessionReceiverOptions, ContextWithSettlement } from "./models"; +import { GetSessionReceiverOptions } from "./models"; import { Receiver, ReceiverImpl, SubscriptionRuleManagement } from "./receivers/receiver"; import { SessionReceiver, SessionReceiverImpl } from "./receivers/sessionReceiver"; +import { ReceivedMessageWithLock, ReceivedMessage } from "./serviceBusMessage"; /** * A client that can create Sender instances for sending messages to queues and @@ -75,7 +76,7 @@ export class ServiceBusClient { * @param receiveMode The receive mode to use (defaults to PeekLock) * @param options Options for the receiver itself. */ - getReceiver(queueName: string, receiveMode: "peekLock"): Receiver; + getReceiver(queueName: string, receiveMode: "peekLock"): Receiver; /** * Creates a receiver for an Azure Service Bus queue. * @@ -83,7 +84,7 @@ export class ServiceBusClient { * @param receiveMode The receive mode to use (defaults to PeekLock) * @param options Options for the receiver itself. */ - getReceiver(queueName: string, receiveMode: "receiveAndDelete"): Receiver<{}>; + getReceiver(queueName: string, receiveMode: "receiveAndDelete"): Receiver; /** * Creates a receiver for an Azure Service Bus subscription. * @@ -96,7 +97,7 @@ export class ServiceBusClient { topicName: string, subscriptionName: string, receiveMode: "peekLock" - ): Receiver & SubscriptionRuleManagement; + ): Receiver & SubscriptionRuleManagement; /** * Creates a receiver for an Azure Service Bus subscription. * @@ -109,16 +110,16 @@ export class ServiceBusClient { topicName: string, subscriptionName: string, receiveMode: "receiveAndDelete" - ): Receiver<{}> & SubscriptionRuleManagement; + ): Receiver & SubscriptionRuleManagement; getReceiver( queueOrTopicName1: string, receiveModeOrSubscriptionName2: "peekLock" | "receiveAndDelete" | string, receiveMode3?: "peekLock" | "receiveAndDelete" ): - | Receiver - | Receiver<{}> - | (Receiver & SubscriptionRuleManagement) - | (Receiver<{}> & SubscriptionRuleManagement) { + | Receiver + | Receiver + | (Receiver & SubscriptionRuleManagement) + | (Receiver & SubscriptionRuleManagement) { let entityPath: string; let receiveMode: "peekLock" | "receiveAndDelete"; let entityType: "queue" | "subscription"; @@ -145,9 +146,13 @@ export class ServiceBusClient { ); if (receiveMode === "peekLock") { - return new ReceiverImpl(clientEntityContext, receiveMode, entityType); + return new ReceiverImpl( + clientEntityContext, + receiveMode, + entityType + ); } else { - return new ReceiverImpl<{}>(clientEntityContext, receiveMode, entityType); + return new ReceiverImpl(clientEntityContext, receiveMode, entityType); } } @@ -162,7 +167,7 @@ export class ServiceBusClient { queueName: string, receiveMode: "peekLock", options?: GetSessionReceiverOptions - ): SessionReceiver; + ): SessionReceiver; /** * Creates a receiver for an Azure Service Bus queue. * @@ -174,7 +179,7 @@ export class ServiceBusClient { queueName: string, receiveMode: "receiveAndDelete", options?: GetSessionReceiverOptions - ): SessionReceiver<{}>; + ): SessionReceiver; /** * Creates a receiver for an Azure Service Bus subscription. * @@ -188,7 +193,7 @@ export class ServiceBusClient { subscriptionName: string, receiveMode: "peekLock", options?: GetSessionReceiverOptions - ): SessionReceiver & SubscriptionRuleManagement; + ): SessionReceiver & SubscriptionRuleManagement; /** * Creates a receiver for an Azure Service Bus subscription. * @@ -202,17 +207,17 @@ export class ServiceBusClient { subscriptionName: string, receiveMode: "receiveAndDelete", options?: GetSessionReceiverOptions - ): SessionReceiver<"receiveAndDelete"> & SubscriptionRuleManagement; + ): SessionReceiver & SubscriptionRuleManagement; getSessionReceiver( queueOrTopicName1: string, receiveModeOrSubscriptionName2: "peekLock" | "receiveAndDelete" | string, receiveModeOrOptions3?: "peekLock" | "receiveAndDelete" | GetSessionReceiverOptions, options4?: GetSessionReceiverOptions ): - | SessionReceiver - | SessionReceiver<{}> - | (SessionReceiver<{}> & SubscriptionRuleManagement) - | (SessionReceiver & SubscriptionRuleManagement) { + | SessionReceiver + | SessionReceiver + | (SessionReceiver & SubscriptionRuleManagement) + | (SessionReceiver & SubscriptionRuleManagement) { let entityPath: string; let receiveMode: "peekLock" | "receiveAndDelete"; let entityType: "queue" | "subscription"; diff --git a/sdk/servicebus/service-bus/src/serviceBusMessage.ts b/sdk/servicebus/service-bus/src/serviceBusMessage.ts index cdeb5542a1a4..27b190051363 100644 --- a/sdk/servicebus/service-bus/src/serviceBusMessage.ts +++ b/sdk/servicebus/service-bus/src/serviceBusMessage.ts @@ -133,9 +133,8 @@ export interface DeadLetterOptions { /** * Describes the message to be sent to Service Bus. - * @interface SendableMessageInfo. */ -export interface SendableMessageInfo { +export interface ServiceBusMessage { /** * @property The message body that needs to be sent or is received. */ @@ -247,7 +246,7 @@ export interface SendableMessageInfo { * @internal * Gets the error message for when a property on given message is not of expected type */ -export function getMessagePropertyTypeMismatchError(msg: SendableMessageInfo): Error | undefined { +export function getMessagePropertyTypeMismatchError(msg: ServiceBusMessage): Error | undefined { if (msg.contentType != null && typeof msg.contentType !== "string") { return new TypeError("The property 'contentType' on the message must be of type 'string'"); } @@ -304,7 +303,7 @@ export function getMessagePropertyTypeMismatchError(msg: SendableMessageInfo): E * @internal * Converts given SendableMessageInfo to AmqpMessage */ -export function toAmqpMessage(msg: SendableMessageInfo): AmqpMessage { +export function toAmqpMessage(msg: ServiceBusMessage): AmqpMessage { const amqpMsg: AmqpMessage = { body: msg.body, message_annotations: {} @@ -380,9 +379,9 @@ export function toAmqpMessage(msg: SendableMessageInfo): AmqpMessage { /** * Describes the message received from Service Bus during peek operations and so cannot be settled. - * @class ReceivedSBMessage + * @class ReceivedMessage */ -export interface ReceivedMessageInfo extends SendableMessageInfo { +export interface ReceivedMessage extends ServiceBusMessage { /** * @property The lock token is a reference to the lock that is being held by the broker in * `ReceiveMode.PeekLock` mode. Locks are used internally settle messages as explained in the @@ -454,6 +453,128 @@ export interface ReceivedMessageInfo extends SendableMessageInfo { readonly _amqpMessage: AmqpMessage; } +/** + * A message that can be settled by completing it, abandoning it, deferring it, or sending + * it to the dead letter queue. + */ +export interface ReceivedMessageWithLock extends ReceivedMessage { + /** + * Removes the message from Service Bus. + * + * @throws Error with name `SessionLockLostError` (for messages from a Queue/Subscription with sessions enabled) + * if the AMQP link with which the message was received is no longer alive. This can + * happen either because the lock on the session expired or the receiver was explicitly closed by + * the user or the AMQP link got closed by the library due to network loss or service error. + * @throws Error with name `MessageLockLostError` (for messages from a Queue/Subscription with sessions not enabled) + * if the lock on the message has expired or the AMQP link with which the message was received is + * no longer alive. The latter can happen if the receiver was explicitly closed by the user or the + * AMQP link got closed by the library due to network loss or service error. + * @throws Error if the message is already settled. To avoid this error check the `isSettled` + * property on the message if you are not sure whether the message is settled. + * @throws Error if used in `ReceiveAndDelete` mode because all messages received in this mode + * are pre-settled. To avoid this error, update your code to not settle a message which is received + * in this mode. + * @throws Error with name `ServiceUnavailableError` if Service Bus does not acknowledge the request to settle + * the message in time. The message may or may not have been settled successfully. + * + * @returns Promise. + */ + complete(): Promise; + + /** + * The lock held on the message by the receiver is let go, making the message available again in + * Service Bus for another receive operation. + * + * @throws Error with name `SessionLockLostError` (for messages from a Queue/Subscription with sessions enabled) + * if the AMQP link with which the message was received is no longer alive. This can + * happen either because the lock on the session expired or the receiver was explicitly closed by + * the user or the AMQP link got closed by the library due to network loss or service error. + * @throws Error with name `MessageLockLostError` (for messages from a Queue/Subscription with sessions not enabled) + * if the lock on the message has expired or the AMQP link with which the message was received is + * no longer alive. The latter can happen if the receiver was explicitly closed by the user or the + * AMQP link got closed by the library due to network loss or service error. + * @throws Error if the message is already settled. To avoid this error check the `isSettled` + * property on the message if you are not sure whether the message is settled. + * @throws Error if used in `ReceiveAndDelete` mode because all messages received in this mode + * are pre-settled. To avoid this error, update your code to not settle a message which is received + * in this mode. + * @throws Error with name `ServiceUnavailableError` if Service Bus does not acknowledge the request to settle + * the message in time. The message may or may not have been settled successfully. + * + * @param propertiesToModify The properties of the message to modify while abandoning the message. + * + * @return Promise. + */ + abandon(propertiesToModify?: { [key: string]: any }): Promise; + + /** + * Defers the processing of the message. Save the `sequenceNumber` of the message, in order to + * receive it message again in the future using the `receiveDeferredMessage` method. + * + * @throws Error with name `SessionLockLostError` (for messages from a Queue/Subscription with sessions enabled) + * if the AMQP link with which the message was received is no longer alive. This can + * happen either because the lock on the session expired or the receiver was explicitly closed by + * the user or the AMQP link got closed by the library due to network loss or service error. + * @throws Error with name `MessageLockLostError` (for messages from a Queue/Subscription with sessions not enabled) + * if the lock on the message has expired or the AMQP link with which the message was received is + * no longer alive. The latter can happen if the receiver was explicitly closed by the user or the + * AMQP link got closed by the library due to network loss or service error. + * @throws Error if the message is already settled. To avoid this error check the `isSettled` + * property on the message if you are not sure whether the message is settled. + * @throws Error if used in `ReceiveAndDelete` mode because all messages received in this mode + * are pre-settled. To avoid this error, update your code to not settle a message which is received + * in this mode. + * @throws Error with name `ServiceUnavailableError` if Service Bus does not acknowledge the request to settle + * the message in time. The message may or may not have been settled successfully. + * + * @param propertiesToModify The properties of the message to modify while deferring the message + * + * @returns Promise + */ + defer(propertiesToModify?: { [key: string]: any }): Promise; + + /** + * Moves the message to the deadletter sub-queue. To receive a deadletted message, create a new + * QueueClient/SubscriptionClient using the path for the deadletter sub-queue. + * + * @throws Error with name `SessionLockLostError` (for messages from a Queue/Subscription with sessions enabled) + * if the AMQP link with which the message was received is no longer alive. This can + * happen either because the lock on the session expired or the receiver was explicitly closed by + * the user or the AMQP link got closed by the library due to network loss or service error. + * @throws Error with name `MessageLockLostError` (for messages from a Queue/Subscription with sessions not enabled) + * if the lock on the message has expired or the AMQP link with which the message was received is + * no longer alive. The latter can happen if the receiver was explicitly closed by the user or the + * AMQP link got closed by the library due to network loss or service error. + * @throws Error if the message is already settled. To avoid this error check the `isSettled` + * property on the message if you are not sure whether the message is settled. + * @throws Error if used in `ReceiveAndDelete` mode because all messages received in this mode + * are pre-settled. To avoid this error, update your code to not settle a message which is received + * in this mode. + * @throws Error with name `ServiceUnavailableError` if Service Bus does not acknowledge the request to settle + * the message in time. The message may or may not have been settled successfully. + * + * @param options The DeadLetter options that can be provided while + * rejecting the message. + * + * @returns Promise + */ + deadLetter(options?: DeadLetterOptions): Promise; + + /** + * Renews the lock on the message for the duration as specified during the Queue/Subscription + * creation. + * - Check the `lockedUntilUtc` property on the message for the time when the lock expires. + * - If a message is not settled (using either `complete()`, `defer()` or `deadletter()`, + * before its lock expires, then the message lands back in the Queue/Subscription for the next + * receive operation. + * + * @returns Promise - New lock token expiry date and time in UTC format. + * @throws Error if the underlying connection, client or receiver is closed. + * @throws MessagingError if the service returns an error while renewing message lock. + */ + renewLock(): Promise; +} + /** * @ignore * Converts given AmqpMessage to ReceivedMessageInfo @@ -462,13 +583,13 @@ export function fromAmqpMessage( msg: AmqpMessage, delivery?: Delivery, shouldReorderLockToken?: boolean -): ReceivedMessageInfo { +): ReceivedMessage { if (!msg) { msg = { body: undefined }; } - const sbmsg: SendableMessageInfo = { + const sbmsg: ServiceBusMessage = { body: msg.body }; @@ -543,7 +664,7 @@ export function fromAmqpMessage( props.expiresAtUtc = new Date(props.enqueuedTimeUtc.getTime() + msg.ttl!); } - const rcvdsbmsg: ReceivedMessageInfo = { + const rcvdsbmsg: ReceivedMessage = { _amqpMessage: msg, _delivery: delivery, deliveryCount: msg.delivery_count, @@ -569,9 +690,8 @@ export function fromAmqpMessage( /** * Describes the message received from Service Bus. - * @class ServiceBusMessage */ -export class ServiceBusMessage implements ReceivedMessageInfo { +export class ServiceBusMessageImpl implements ReceivedMessageWithLock { /** * @property The message body that needs to be sent or is received. */ @@ -776,25 +896,7 @@ export class ServiceBusMessage implements ReceivedMessageInfo { } /** - * Removes the message from Service Bus. - * - * @throws Error with name `SessionLockLostError` (for messages from a Queue/Subscription with sessions enabled) - * if the AMQP link with which the message was received is no longer alive. This can - * happen either because the lock on the session expired or the receiver was explicitly closed by - * the user or the AMQP link got closed by the library due to network loss or service error. - * @throws Error with name `MessageLockLostError` (for messages from a Queue/Subscription with sessions not enabled) - * if the lock on the message has expired or the AMQP link with which the message was received is - * no longer alive. The latter can happen if the receiver was explicitly closed by the user or the - * AMQP link got closed by the library due to network loss or service error. - * @throws Error if the message is already settled. To avoid this error check the `isSettled` - * property on the message if you are not sure whether the message is settled. - * @throws Error if used in `ReceiveAndDelete` mode because all messages received in this mode - * are pre-settled. To avoid this error, update your code to not settle a message which is received - * in this mode. - * @throws Error with name `ServiceUnavailableError` if Service Bus does not acknowledge the request to settle - * the message in time. The message may or may not have been settled successfully. - * - * @returns Promise. + * See ReceivedMessageWithLock.complete(). */ async complete(): Promise { log.message( @@ -815,34 +917,14 @@ export class ServiceBusMessage implements ReceivedMessageInfo { this._context.requestResponseLockedMessages.delete(this.lockToken!); return; } - const receiver = this._context.getReceiver(this.delivery.link.name, this.sessionId); + const receiver = this.getReceiverFromContext(); this.throwIfMessageCannotBeSettled(receiver, DispositionType.complete); return receiver!.settleMessage(this, DispositionType.complete); } + /** - * The lock held on the message by the receiver is let go, making the message available again in - * Service Bus for another receive operation. - * - * @throws Error with name `SessionLockLostError` (for messages from a Queue/Subscription with sessions enabled) - * if the AMQP link with which the message was received is no longer alive. This can - * happen either because the lock on the session expired or the receiver was explicitly closed by - * the user or the AMQP link got closed by the library due to network loss or service error. - * @throws Error with name `MessageLockLostError` (for messages from a Queue/Subscription with sessions not enabled) - * if the lock on the message has expired or the AMQP link with which the message was received is - * no longer alive. The latter can happen if the receiver was explicitly closed by the user or the - * AMQP link got closed by the library due to network loss or service error. - * @throws Error if the message is already settled. To avoid this error check the `isSettled` - * property on the message if you are not sure whether the message is settled. - * @throws Error if used in `ReceiveAndDelete` mode because all messages received in this mode - * are pre-settled. To avoid this error, update your code to not settle a message which is received - * in this mode. - * @throws Error with name `ServiceUnavailableError` if Service Bus does not acknowledge the request to settle - * the message in time. The message may or may not have been settled successfully. - * - * @param propertiesToModify The properties of the message to modify while abandoning the message. - * - * @return Promise. + * See ReceivedMessageWithLock.abandon(). */ async abandon(propertiesToModify?: { [key: string]: any }): Promise { // TODO: Figure out a mechanism to convert specified properties to message_annotations. @@ -862,7 +944,7 @@ export class ServiceBusMessage implements ReceivedMessageInfo { this._context.requestResponseLockedMessages.delete(this.lockToken!); return; } - const receiver = this._context.getReceiver(this.delivery.link.name, this.sessionId); + const receiver = this.getReceiverFromContext(); this.throwIfMessageCannotBeSettled(receiver, DispositionType.abandon); return receiver!.settleMessage(this, DispositionType.abandon, { @@ -871,28 +953,7 @@ export class ServiceBusMessage implements ReceivedMessageInfo { } /** - * Defers the processing of the message. Save the `sequenceNumber` of the message, in order to - * receive it message again in the future using the `receiveDeferredMessage` method. - * - * @throws Error with name `SessionLockLostError` (for messages from a Queue/Subscription with sessions enabled) - * if the AMQP link with which the message was received is no longer alive. This can - * happen either because the lock on the session expired or the receiver was explicitly closed by - * the user or the AMQP link got closed by the library due to network loss or service error. - * @throws Error with name `MessageLockLostError` (for messages from a Queue/Subscription with sessions not enabled) - * if the lock on the message has expired or the AMQP link with which the message was received is - * no longer alive. The latter can happen if the receiver was explicitly closed by the user or the - * AMQP link got closed by the library due to network loss or service error. - * @throws Error if the message is already settled. To avoid this error check the `isSettled` - * property on the message if you are not sure whether the message is settled. - * @throws Error if used in `ReceiveAndDelete` mode because all messages received in this mode - * are pre-settled. To avoid this error, update your code to not settle a message which is received - * in this mode. - * @throws Error with name `ServiceUnavailableError` if Service Bus does not acknowledge the request to settle - * the message in time. The message may or may not have been settled successfully. - * - * @param propertiesToModify The properties of the message to modify while deferring the message - * - * @returns Promise + * See ReceivedMessageWithLock.defer(). */ async defer(propertiesToModify?: { [key: string]: any }): Promise { log.message( @@ -911,7 +972,7 @@ export class ServiceBusMessage implements ReceivedMessageInfo { this._context.requestResponseLockedMessages.delete(this.lockToken!); return; } - const receiver = this._context.getReceiver(this.delivery.link.name, this.sessionId); + const receiver = this.getReceiverFromContext(); this.throwIfMessageCannotBeSettled(receiver, DispositionType.defer); return receiver!.settleMessage(this, DispositionType.defer, { @@ -920,29 +981,7 @@ export class ServiceBusMessage implements ReceivedMessageInfo { } /** - * Moves the message to the deadletter sub-queue. To receive a deadletted message, create a new - * QueueClient/SubscriptionClient using the path for the deadletter sub-queue. - * - * @throws Error with name `SessionLockLostError` (for messages from a Queue/Subscription with sessions enabled) - * if the AMQP link with which the message was received is no longer alive. This can - * happen either because the lock on the session expired or the receiver was explicitly closed by - * the user or the AMQP link got closed by the library due to network loss or service error. - * @throws Error with name `MessageLockLostError` (for messages from a Queue/Subscription with sessions not enabled) - * if the lock on the message has expired or the AMQP link with which the message was received is - * no longer alive. The latter can happen if the receiver was explicitly closed by the user or the - * AMQP link got closed by the library due to network loss or service error. - * @throws Error if the message is already settled. To avoid this error check the `isSettled` - * property on the message if you are not sure whether the message is settled. - * @throws Error if used in `ReceiveAndDelete` mode because all messages received in this mode - * are pre-settled. To avoid this error, update your code to not settle a message which is received - * in this mode. - * @throws Error with name `ServiceUnavailableError` if Service Bus does not acknowledge the request to settle - * the message in time. The message may or may not have been settled successfully. - * - * @param options The DeadLetter options that can be provided while - * rejecting the message. - * - * @returns Promise + * See ReceivedMessageWithLock.deadLetter(). */ async deadLetter(options?: DeadLetterOptions): Promise { const error: AmqpError = { @@ -974,7 +1013,7 @@ export class ServiceBusMessage implements ReceivedMessageInfo { this._context.requestResponseLockedMessages.delete(this.lockToken!); return; } - const receiver = this._context.getReceiver(this.delivery.link.name, this.sessionId); + const receiver = this.getReceiverFromContext(); this.throwIfMessageCannotBeSettled(receiver, DispositionType.deadletter); return receiver!.settleMessage(this, DispositionType.deadletter, { @@ -982,13 +1021,30 @@ export class ServiceBusMessage implements ReceivedMessageInfo { }); } + /** + * Renews the lock on the message for the duration as specified during the Queue/Subscription + * creation. + * - Check the `lockedUntilUtc` property on the message for the time when the lock expires. + * - If a message is not settled (using either `complete()`, `defer()` or `deadletter()`, + * before its lock expires, then the message lands back in the Queue/Subscription for the next + * receive operation. + * + * @returns Promise - New lock token expiry date and time in UTC format. + * @throws Error if the underlying connection, client or receiver is closed. + * @throws MessagingError if the service returns an error while renewing message lock. + */ + async renewLock(): Promise { + this.throwIfMessageCannotBeSettled(this.getReceiverFromContext(), "renew the lock on"); + return await this._context.managementClient!.renewLock(this.lockToken!); + } + /** * Creates a clone of the current message to allow it to be re-sent to the queue * @returns ServiceBusMessage */ - clone(): SendableMessageInfo { + clone(): ServiceBusMessage { // We are returning a SendableMessageInfo object because that object can then be sent to Service Bus - const clone: SendableMessageInfo = { + const clone: ServiceBusMessage = { body: this.body, contentType: this.contentType, correlationId: this.correlationId, @@ -1016,7 +1072,7 @@ export class ServiceBusMessage implements ReceivedMessageInfo { */ private throwIfMessageCannotBeSettled( receiver: MessageReceiver | MessageSession | undefined, - operation: DispositionType + operation: DispositionType | string ): void { let error: Error | undefined; @@ -1057,4 +1113,8 @@ export class ServiceBusMessage implements ReceivedMessageInfo { throw error; } + + private getReceiverFromContext() { + return this._context.getReceiver(this.delivery.link.name, this.sessionId); + } } diff --git a/sdk/servicebus/service-bus/src/session/messageSession.ts b/sdk/servicebus/service-bus/src/session/messageSession.ts index 3a6b637eb50d..96e16de54de6 100644 --- a/sdk/servicebus/service-bus/src/session/messageSession.ts +++ b/sdk/servicebus/service-bus/src/session/messageSession.ts @@ -29,7 +29,7 @@ import { LinkEntity } from "../core/linkEntity"; import { ClientEntityContext } from "../clientEntityContext"; import { convertTicksToDate, calculateRenewAfterDuration } from "../util/utils"; import { throwErrorIfConnectionClosed } from "../util/errors"; -import { ServiceBusMessage, DispositionType, ReceiveMode } from "../serviceBusMessage"; +import { ServiceBusMessageImpl, DispositionType, ReceiveMode } from "../serviceBusMessage"; /** * Enum to denote who is calling the session receiver @@ -810,7 +810,7 @@ export class MessageSession extends LinkEntity { } resetTimerOnNewMessageReceived(); - const bMessage: ServiceBusMessage = new ServiceBusMessage( + const bMessage: ServiceBusMessageImpl = new ServiceBusMessageImpl( this._context, context.message!, context.delivery!, @@ -925,15 +925,15 @@ export class MessageSession extends LinkEntity { async receiveMessages( maxMessageCount: number, maxWaitTimeInSeconds?: number - ): Promise { + ): Promise { if (maxWaitTimeInSeconds == null) { maxWaitTimeInSeconds = Constants.defaultOperationTimeoutInMs / 1000; } - const brokeredMessages: ServiceBusMessage[] = []; + const brokeredMessages: ServiceBusMessageImpl[] = []; this.isReceivingMessages = true; - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { let totalWaitTimer: any; const setnewMessageWaitTimeoutInSeconds = (value?: number): void => { @@ -974,7 +974,7 @@ export class MessageSession extends LinkEntity { const onReceiveMessage: OnAmqpEventAsPromise = async (context: EventContext) => { resetTimerOnNewMessageReceived(); try { - const data: ServiceBusMessage = new ServiceBusMessage( + const data: ServiceBusMessageImpl = new ServiceBusMessageImpl( this._context, context.message!, context.delivery!, @@ -1131,7 +1131,7 @@ export class MessageSession extends LinkEntity { * @param options Optional parameters that can be provided while disposing the message. */ async settleMessage( - message: ServiceBusMessage, + message: ServiceBusMessageImpl, operation: DispositionType, options?: DispositionOptions ): Promise { diff --git a/sdk/servicebus/service-bus/src/util/errors.ts b/sdk/servicebus/service-bus/src/util/errors.ts index 04f136fabad2..fb40c80485ea 100644 --- a/sdk/servicebus/service-bus/src/util/errors.ts +++ b/sdk/servicebus/service-bus/src/util/errors.ts @@ -267,5 +267,5 @@ export function throwTypeErrorIfParameterIsEmptyString( * that is not supported in ReceiveAndDelete mode */ export function getErrorMessageNotSupportedInReceiveAndDeleteMode(failedToDo: string): string { - return `Failed to ${failedToDo} as the operation is only supported in 'PeekLock' recieve mode.`; + return `Failed to ${failedToDo} as the operation is only supported in 'PeekLock' receive mode.`; } diff --git a/sdk/servicebus/service-bus/test/batchReceiver.spec.ts b/sdk/servicebus/service-bus/test/batchReceiver.spec.ts index bc49b55205c1..6de1d82661c6 100644 --- a/sdk/servicebus/service-bus/test/batchReceiver.spec.ts +++ b/sdk/servicebus/service-bus/test/batchReceiver.spec.ts @@ -3,7 +3,7 @@ import chai from "chai"; import chaiAsPromised from "chai-as-promised"; -import { delay, SendableMessageInfo, ContextWithSettlement, ReceivedMessage } from "../src"; +import { delay, ServiceBusMessage } from "../src"; import { getAlreadyReceivingErrorMsg } from "../src/util/errors"; import { TestClientType, TestMessage } from "./utils/testUtils"; import { Receiver } from "../src/receivers/receiver"; @@ -13,6 +13,7 @@ import { ServiceBusClientForTests, testPeekMsgsLength } from "./utils/testutils2"; +import { ReceivedMessageWithLock } from "../src/serviceBusMessage"; const should = chai.should(); chai.use(chaiAsPromised); @@ -22,8 +23,8 @@ describe("batchReceiver", () => { let errorWasThrown: boolean; let senderClient: Sender; - let receiverClient: Receiver; - let deadLetterClient: Receiver; + let receiverClient: Receiver; + let deadLetterClient: Receiver; const maxDeliveryCount = 10; before(() => { @@ -57,33 +58,29 @@ describe("batchReceiver", () => { }); async function sendReceiveMsg( - testMessages: SendableMessageInfo - ): Promise<{ message: ReceivedMessage; context: ContextWithSettlement }> { + testMessages: ServiceBusMessage + ): Promise { await senderClient.send(testMessages); const msgs = await receiverClient.receiveBatch(1); - should.equal(Array.isArray(msgs.messages), true, "`ReceivedMessages` is not an array"); - should.equal(msgs.messages.length, 1, "Unexpected number of messages"); + should.equal(Array.isArray(msgs), true, "`ReceivedMessages` is not an array"); + should.equal(msgs.length, 1, "Unexpected number of messages"); + should.equal(msgs[0].body, testMessages.body, "MessageBody is different than expected"); should.equal( - msgs.messages[0].body, - testMessages.body, - "MessageBody is different than expected" - ); - should.equal( - msgs.messages[0].messageId, + msgs[0].messageId, testMessages.messageId, "MessageId is different than expected" ); - should.equal(msgs.messages[0].deliveryCount, 0, "DeliveryCount is different than expected"); + should.equal(msgs[0].deliveryCount, 0, "DeliveryCount is different than expected"); - return { message: msgs.messages[0], context: msgs.context as ContextWithSettlement }; + return msgs[0]; } async function testComplete(useSessions?: boolean): Promise { const testMessages = useSessions ? TestMessage.getSessionSample() : TestMessage.getSample(); const msg = await sendReceiveMsg(testMessages); - await msg.context.complete(msg.message); + await msg.complete(); await testPeekMsgsLength(receiverClient, 0); } @@ -141,25 +138,21 @@ describe("batchReceiver", () => { async function testAbandon(useSessions?: boolean): Promise { const testMessages = useSessions ? TestMessage.getSessionSample() : TestMessage.getSample(); const msg = await sendReceiveMsg(testMessages); - await msg.context.abandon(msg.message); + await msg.abandon(); await testPeekMsgsLength(receiverClient, 1); const messageBatch = await receiverClient.receiveBatch(1); - should.equal(messageBatch.messages.length, 1, "Unexpected number of messages"); + should.equal(messageBatch.length, 1, "Unexpected number of messages"); + should.equal(messageBatch[0].deliveryCount, 1, "DeliveryCount is different than expected"); should.equal( - messageBatch.messages[0].deliveryCount, - 1, - "DeliveryCount is different than expected" - ); - should.equal( - messageBatch.messages[0].messageId, + messageBatch[0].messageId, testMessages.messageId, "MessageId is different than expected" ); - await (messageBatch.context as ContextWithSettlement).complete(messageBatch.messages[0]); + await messageBatch[0].complete(); await testPeekMsgsLength(receiverClient, 0); } @@ -228,20 +221,20 @@ describe("batchReceiver", () => { while (abandonMsgCount < maxDeliveryCount) { const batch = await receiverClient.receiveBatch(1); - should.equal(batch.messages.length, 1, "Unexpected number of messages"); + should.equal(batch.length, 1, "Unexpected number of messages"); should.equal( - batch.messages[0].messageId, + batch[0].messageId, testMessages.messageId, "MessageId is different than expected" ); should.equal( - batch.messages[0].deliveryCount, + batch[0].deliveryCount, abandonMsgCount, "DeliveryCount is different than expected" ); abandonMsgCount++; - await batch.context.abandon(batch.messages[0]); + await batch[0].abandon(); } await testPeekMsgsLength(receiverClient, 0); @@ -249,23 +242,23 @@ describe("batchReceiver", () => { const deadLetterMsgsBatch = await deadLetterClient.receiveBatch(1); should.equal( - Array.isArray(deadLetterMsgsBatch.messages), + Array.isArray(deadLetterMsgsBatch), true, "`ReceivedMessages` from Deadletter is not an array" ); - should.equal(deadLetterMsgsBatch.messages.length, 1, "Unexpected number of messages"); + should.equal(deadLetterMsgsBatch.length, 1, "Unexpected number of messages"); should.equal( - deadLetterMsgsBatch.messages[0].body, + deadLetterMsgsBatch[0].body, testMessages.body, "MessageBody is different than expected" ); should.equal( - deadLetterMsgsBatch.messages[0].messageId, + deadLetterMsgsBatch[0].messageId, testMessages.messageId, "MessageId is different than expected" ); - await deadLetterMsgsBatch.context.complete(deadLetterMsgsBatch.messages[0]); + await deadLetterMsgsBatch[0].complete(); await testPeekMsgsLength(deadLetterClient, 0); } @@ -330,11 +323,11 @@ describe("batchReceiver", () => { const testMessages = useSessions ? TestMessage.getSessionSample() : TestMessage.getSample(); const msg = await sendReceiveMsg(testMessages); - if (!msg.message.sequenceNumber) { + if (!msg.sequenceNumber) { throw "Sequence Number can not be null"; } - const sequenceNumber = msg.message.sequenceNumber; - await msg.context.defer(msg.message); + const sequenceNumber = msg.sequenceNumber; + await msg.defer(); const deferredMsgs = await receiverClient.receiveDeferredMessage(sequenceNumber); if (!deferredMsgs) { @@ -412,30 +405,30 @@ describe("batchReceiver", () => { async function testDeadletter(useSessions?: boolean): Promise { const testMessages = useSessions ? TestMessage.getSessionSample() : TestMessage.getSample(); const msg = await sendReceiveMsg(testMessages); - await msg.context.deadLetter(msg.message); + await msg.deadLetter(); await testPeekMsgsLength(receiverClient, 0); const deadLetterMsgsBatch = await deadLetterClient.receiveBatch(1); should.equal( - Array.isArray(deadLetterMsgsBatch.messages), + Array.isArray(deadLetterMsgsBatch), true, "`ReceivedMessages` from Deadletter is not an array" ); - should.equal(deadLetterMsgsBatch.messages.length, 1, "Unexpected number of messages"); + should.equal(deadLetterMsgsBatch.length, 1, "Unexpected number of messages"); should.equal( - deadLetterMsgsBatch.messages[0].body, + deadLetterMsgsBatch[0].body, testMessages.body, "MessageBody is different than expected" ); should.equal( - deadLetterMsgsBatch.messages[0].messageId, + deadLetterMsgsBatch[0].messageId, testMessages.messageId, "MessageId is different than expected" ); - await deadLetterMsgsBatch.context.complete(deadLetterMsgsBatch.messages[0]); + await deadLetterMsgsBatch[0].complete(); await testPeekMsgsLength(deadLetterClient, 0); } @@ -503,82 +496,78 @@ describe("batchReceiver", () => { }); async function deadLetterMessage( - testMessage: SendableMessageInfo - ): Promise<{ message: ReceivedMessage; context: ContextWithSettlement }> { + testMessage: ServiceBusMessage + ): Promise { await senderClient.send(testMessage); const batch = await receiverClient.receiveBatch(1); - should.equal(batch.messages.length, 1, "Unexpected number of messages"); + should.equal(batch.length, 1, "Unexpected number of messages"); + should.equal(batch[0].body, testMessage.body, "MessageBody is different than expected"); should.equal( - batch.messages[0].body, - testMessage.body, - "MessageBody is different than expected" - ); - should.equal( - batch.messages[0].messageId, + batch[0].messageId, testMessage.messageId, "MessageId is different than expected" ); - should.equal(batch.messages[0].deliveryCount, 0, "DeliveryCount is different than expected"); + should.equal(batch[0].deliveryCount, 0, "DeliveryCount is different than expected"); - await batch.context.deadLetter(batch.messages[0]); + await batch[0].deadLetter(); await testPeekMsgsLength(receiverClient, 0); const deadLetterMsgsBatch = await deadLetterClient.receiveBatch(1); - should.equal(deadLetterMsgsBatch.messages.length, 1, "Unexpected number of messages"); + should.equal(deadLetterMsgsBatch.length, 1, "Unexpected number of messages"); should.equal( - deadLetterMsgsBatch.messages[0].body, + deadLetterMsgsBatch[0].body, testMessage.body, "MessageBody is different than expected" ); should.equal( - deadLetterMsgsBatch.messages[0].messageId, + deadLetterMsgsBatch[0].messageId, testMessage.messageId, "MessageId is different than expected" ); should.equal( - deadLetterMsgsBatch.messages[0].deliveryCount, + deadLetterMsgsBatch[0].deliveryCount, 0, "DeliveryCount is different than expected" ); - return { message: deadLetterMsgsBatch.messages[0], context: deadLetterMsgsBatch.context }; + return deadLetterMsgsBatch[0]; } async function completeDeadLetteredMessage( - testMessage: SendableMessageInfo, - deadletterClient: Receiver, + testMessage: ServiceBusMessage, + deadletterClient: Receiver, expectedDeliverCount: number ): Promise { const deadLetterMsgsBatch = await deadLetterClient.receiveBatch(1); - should.equal(deadLetterMsgsBatch.messages.length, 1, "Unexpected number of messages"); + should.equal(deadLetterMsgsBatch.length, 1, "Unexpected number of messages"); should.equal( - deadLetterMsgsBatch.messages[0].body, + deadLetterMsgsBatch[0].body, testMessage.body, "MessageBody is different than expected" ); should.equal( - deadLetterMsgsBatch.messages[0].messageId, + deadLetterMsgsBatch[0].messageId, testMessage.messageId, "MessageId is different than expected" ); should.equal( - deadLetterMsgsBatch.messages[0].deliveryCount, + deadLetterMsgsBatch[0].deliveryCount, expectedDeliverCount, "DeliveryCount is different than expected" ); - await deadLetterMsgsBatch.context.complete(deadLetterMsgsBatch.messages[0]); + await deadLetterMsgsBatch[0].complete(); await testPeekMsgsLength(deadletterClient, 0); } - async function testDeadletter(testMessage: SendableMessageInfo): Promise { + async function testDeadletter(testMessage: ServiceBusMessage): Promise { const deadLetterMsg = await deadLetterMessage(testMessage); - await deadLetterMsg.context.deadLetter(deadLetterMsg.message).catch((err) => { + await deadLetterMsg.deadLetter().catch((err) => { should.equal(err.code, "InvalidOperationError", "Error code is different than expected"); errorWasThrown = true; }); @@ -616,10 +605,10 @@ describe("batchReceiver", () => { await testDeadletter(TestMessage.getSample()); }); - async function testAbandon(testMessage: SendableMessageInfo): Promise { + async function testAbandon(testMessage: ServiceBusMessage): Promise { const deadLetterMsg = await deadLetterMessage(testMessage); - await deadLetterMsg.context.abandon(deadLetterMsg.message); + await deadLetterMsg.abandon(); await completeDeadLetteredMessage(testMessage, deadLetterClient, 0); } @@ -652,15 +641,15 @@ describe("batchReceiver", () => { await testAbandon(TestMessage.getSample()); }); - async function testDefer(testMessage: SendableMessageInfo): Promise { + async function testDefer(testMessage: ServiceBusMessage): Promise { const deadLetterMsg = await deadLetterMessage(testMessage); - if (!deadLetterMsg.message.sequenceNumber) { + if (!deadLetterMsg.sequenceNumber) { throw "Sequence Number can not be null"; } - const sequenceNumber = deadLetterMsg.message.sequenceNumber; - await deadLetterMsg.context.defer(deadLetterMsg.message); + const sequenceNumber = deadLetterMsg.sequenceNumber; + await deadLetterMsg.defer(); const deferredMsgs = await deadLetterClient.receiveDeferredMessage(sequenceNumber); if (!deferredMsgs) { @@ -716,7 +705,7 @@ describe("batchReceiver", () => { // We use an empty queue/topic here so that the first receiveMessages call takes time to return async function testParallelReceiveCalls(useSessions?: boolean): Promise { - const firstBatchPromise = receiverClient.receiveBatch(1, 10); + const firstBatchPromise = receiverClient.receiveBatch(1, { maxWaitTimeSeconds: 10 }); await delay(5000); let errorMessage; @@ -777,7 +766,7 @@ describe("batchReceiver", () => { await testParallelReceiveCalls(true); }); - const messages: SendableMessageInfo[] = [ + const messages: ServiceBusMessage[] = [ { body: "hello1", messageId: `test message ${Math.random()}`, @@ -789,7 +778,7 @@ describe("batchReceiver", () => { partitionKey: "dummy" // partitionKey is only for partitioned queue/subscrption, Unpartitioned queue/subscrption do not care about partitionKey. } ]; - const messageWithSessions: SendableMessageInfo[] = [ + const messageWithSessions: ServiceBusMessage[] = [ { body: "hello1", messageId: `test message ${Math.random()}`, @@ -812,25 +801,25 @@ describe("batchReceiver", () => { // Results are checked after both receiveMessages are done to ensure that the second call doesnt // affect the result from the first one. - should.equal(Array.isArray(msgs1.messages), true, "`ReceivedMessages` is not an array"); - should.equal(msgs1.messages.length, 1, "Unexpected number of messages"); + should.equal(Array.isArray(msgs1), true, "`ReceivedMessages` is not an array"); + should.equal(msgs1.length, 1, "Unexpected number of messages"); - should.equal(Array.isArray(msgs2.messages), true, "`ReceivedMessages` is not an array"); - should.equal(msgs2.messages.length, 1, "Unexpected number of messages"); + should.equal(Array.isArray(msgs2), true, "`ReceivedMessages` is not an array"); + should.equal(msgs2.length, 1, "Unexpected number of messages"); should.equal( - testMessages.some((x) => x.messageId === msgs1.messages[0].messageId), + testMessages.some((x) => x.messageId === msgs1[0].messageId), true, "MessageId is different than expected" ); should.equal( - testMessages.some((x) => x.messageId === msgs2.messages[0].messageId), + testMessages.some((x) => x.messageId === msgs2[0].messageId), true, "MessageId is different than expected" ); - await msgs1.context.complete(msgs1.messages[0]); - await msgs2.context.complete(msgs2.messages[0]); + await msgs1[0].complete(); + await msgs2[0].complete(); } it("Unpartitioned Queue: Multiple sequential receiveMessages calls #RunInBrowser", async function(): Promise< @@ -859,10 +848,10 @@ describe("batchReceiver", () => { let batch = await receiverClient.receiveBatch(1); - should.equal(batch.messages.length, 1, "Unexpected number of messages"); - should.equal(batch.messages[0].deliveryCount, 0, "DeliveryCount is different than expected"); + should.equal(batch.length, 1, "Unexpected number of messages"); + should.equal(batch[0].deliveryCount, 0, "DeliveryCount is different than expected"); should.equal( - batch.messages[0].messageId, + batch[0].messageId, testMessages.messageId, "MessageId is different than expected" ); @@ -871,15 +860,15 @@ describe("batchReceiver", () => { batch = await receiverClient.receiveBatch(1); - should.equal(batch.messages.length, 1, "Unexpected number of messages"); - should.equal(batch.messages[0].deliveryCount, 1, "DeliveryCount is different than expected"); + should.equal(batch.length, 1, "Unexpected number of messages"); + should.equal(batch[0].deliveryCount, 1, "DeliveryCount is different than expected"); should.equal( - batch.messages[0].messageId, + batch[0].messageId, testMessages.messageId, "MessageId is different than expected" ); - await batch.context.complete(batch.messages[0]); + await batch[0].complete(); } it("Partitioned Queue: No settlement of the message is retained with incremented deliveryCount", async function(): Promise< @@ -915,19 +904,15 @@ describe("batchReceiver", () => { await senderClient.send(testMessages); const batch = await receiverClient.receiveBatch(2); - should.equal(batch.messages.length, 1, "Unexpected number of messages"); - should.equal( - batch.messages[0].body, - testMessages.body, - "MessageBody is different than expected" - ); + should.equal(batch.length, 1, "Unexpected number of messages"); + should.equal(batch[0].body, testMessages.body, "MessageBody is different than expected"); should.equal( - batch.messages[0].messageId, + batch[0].messageId, testMessages.messageId, "MessageId is different than expected" ); - await batch.context.complete(batch.messages[0]); + await batch[0].complete(); await testPeekMsgsLength(receiverClient, 0); } diff --git a/sdk/servicebus/service-bus/test/deferredMessage.spec.ts b/sdk/servicebus/service-bus/test/deferredMessage.spec.ts index 95d7d2b26db8..b398f2798800 100644 --- a/sdk/servicebus/service-bus/test/deferredMessage.spec.ts +++ b/sdk/servicebus/service-bus/test/deferredMessage.spec.ts @@ -5,17 +5,18 @@ import chai from "chai"; const should = chai.should(); import chaiAsPromised from "chai-as-promised"; chai.use(chaiAsPromised); -import { ServiceBusMessage, SendableMessageInfo, ContextWithSettlement } from "../src"; +import { ServiceBusMessage } from "../src"; import { TestMessage, TestClientType } from "./utils/testUtils"; import { testPeekMsgsLength, createServiceBusClientForTests } from "./utils/testutils2"; import { Receiver } from "../src/receivers/receiver"; import { Sender } from "../src/sender"; +import { ReceivedMessageWithLock } from "../src/serviceBusMessage"; describe("deferred messages", () => { let serviceBusClient: ReturnType; let senderClient: Sender; - let receiverClient: Receiver; - let deadLetterClient: Receiver; + let receiverClient: Receiver; + let deadLetterClient: Receiver; before(() => { serviceBusClient = createServiceBusClientForTests(); @@ -50,12 +51,11 @@ describe("deferred messages", () => { * `receiveDeferredMessages` to ensure both get code coverage */ async function deferMessage( - testMessage: SendableMessageInfo, + testMessage: ServiceBusMessage, useReceiveDeferredMessages: boolean - ): Promise { + ): Promise { await senderClient.send(testMessage); - const batch = await receiverClient.receiveBatch(1); - const receivedMsgs = batch.messages; + const receivedMsgs = await receiverClient.receiveBatch(1); should.equal(receivedMsgs.length, 1, "Unexpected number of messages"); should.equal(receivedMsgs[0].body, testMessage.body, "MessageBody is different than expected"); @@ -70,9 +70,9 @@ describe("deferred messages", () => { throw "Sequence Number can not be null"; } const sequenceNumber = receivedMsgs[0].sequenceNumber; - await batch.context.defer(receivedMsgs[0]); + await receivedMsgs[0].defer(); - let deferredMsg: ServiceBusMessage | undefined; + let deferredMsg: ReceivedMessageWithLock | undefined; // Randomly choose receiveDeferredMessage/receiveDeferredMessages as the latter is expected to // convert single input to array and then use it @@ -99,7 +99,7 @@ describe("deferred messages", () => { async function completeDeferredMessage( sequenceNumber: Long, expectedDeliverCount: number, - testMessages: SendableMessageInfo + testMessages: ServiceBusMessage ): Promise { await testPeekMsgsLength(receiverClient, 1); @@ -272,8 +272,7 @@ describe("deferred messages", () => { await testPeekMsgsLength(receiverClient, 0); - const deadLetterMsgsBatch = await deadLetterClient.receiveBatch(1); - const deadLetterMsgs = deadLetterMsgsBatch.messages; + const deadLetterMsgs = await deadLetterClient.receiveBatch(1); should.equal(deadLetterMsgs.length, 1, "Unexpected number of messages"); should.equal( @@ -288,7 +287,7 @@ describe("deferred messages", () => { "MessageId is different than expected" ); - await deadLetterMsgsBatch.context.complete(deadLetterMsgs[0]); + await deadLetterMsgs[0].complete(); await testPeekMsgsLength(deadLetterClient, 0); } diff --git a/sdk/servicebus/service-bus/test/invalidParameters.spec.ts b/sdk/servicebus/service-bus/test/invalidParameters.spec.ts index cb76c390dd20..4b6e4d178e77 100644 --- a/sdk/servicebus/service-bus/test/invalidParameters.spec.ts +++ b/sdk/servicebus/service-bus/test/invalidParameters.spec.ts @@ -6,12 +6,12 @@ import Long from "long"; const should = chai.should(); import chaiAsPromised from "chai-as-promised"; chai.use(chaiAsPromised); -import { ContextWithSettlement } from "../src"; import { TestMessage, TestClientType } from "./utils/testUtils"; import { ServiceBusClientForTests, createServiceBusClientForTests } from "./utils/testutils2"; import { SubscriptionRuleManagement, Receiver } from "../src/receivers/receiver"; import { Sender } from "../src/sender"; import { SessionReceiver } from "../src/receivers/sessionReceiver"; +import { ReceivedMessageWithLock } from "../src/serviceBusMessage"; describe("invalid parameters", () => { let serviceBusClient: ServiceBusClientForTests; @@ -25,7 +25,7 @@ describe("invalid parameters", () => { }); describe("Invalid parameters in Sender/ReceiverClients for PartitionedQueue #RunInBrowser", function(): void { - let receiver: Receiver; + let receiver: Receiver; // Since, the below tests never actually make use of any AMQP links, there is no need to create // new sender/receiver clients before each test. Doing it once for each describe block. @@ -112,7 +112,7 @@ describe("invalid parameters", () => { }); describe("Invalid parameters in Sender/ReceiverClients for PartitionedSubscription #RunInBrowser", function(): void { - let subscriptionReceiverClient: Receiver & SubscriptionRuleManagement; + let subscriptionReceiverClient: Receiver & SubscriptionRuleManagement; // Since, the below tests never actually make use of any AMQP links, there is no need to create // new sender/receiver clients before each test. Doing it once for each describe block. @@ -312,7 +312,7 @@ describe("invalid parameters", () => { describe("Invalid parameters in SessionReceiver #RunInBrowser", function(): void { let sender: Sender; - let receiver: SessionReceiver; + let receiver: SessionReceiver; // Since, the below tests never actually make use of any AMQP links, there is no need to create // new sender/receiver clients before each test. Doing it once for each describe block. diff --git a/sdk/servicebus/service-bus/test/receiveAndDeleteMode.spec.ts b/sdk/servicebus/service-bus/test/receiveAndDeleteMode.spec.ts index bab8b16ea96a..8a43891f136c 100644 --- a/sdk/servicebus/service-bus/test/receiveAndDeleteMode.spec.ts +++ b/sdk/servicebus/service-bus/test/receiveAndDeleteMode.spec.ts @@ -6,7 +6,7 @@ const should = chai.should(); const expect = chai.expect; import chaiAsPromised from "chai-as-promised"; chai.use(chaiAsPromised); -import { SendableMessageInfo, ContextWithSettlement, ReceivedMessage, Receiver } from "../src"; +import { ServiceBusMessage, ReceivedMessage, Receiver } from "../src"; import { TestMessage, TestClientType, checkWithTimeout } from "./utils/testUtils"; @@ -17,13 +17,13 @@ import { createServiceBusClientForTests, testPeekMsgsLength } from "./utils/testutils2"; -import { DispositionType } from "../src/serviceBusMessage"; +import { DispositionType, ReceivedMessageWithLock } from "../src/serviceBusMessage"; let errorWasThrown: boolean; describe("receive and delete", () => { let senderClient: Sender; - let receiverClient: Receiver<{}>; + let receiverClient: Receiver; let serviceBusClient: ServiceBusClientForTests; before(() => { @@ -54,9 +54,9 @@ describe("receive and delete", () => { await afterEachTest(); }); - async function sendReceiveMsg(testMessages: SendableMessageInfo): Promise { + async function sendReceiveMsg(testMessages: ServiceBusMessage): Promise { await senderClient.send(testMessages); - const msgs = (await receiverClient.receiveBatch(1)).messages; + const msgs = await receiverClient.receiveBatch(1); should.equal(Array.isArray(msgs), true, "`ReceivedMessages` is not an array"); should.equal(msgs.length, 1, "Unexpected number of messages"); @@ -141,7 +141,7 @@ describe("receive and delete", () => { }); async function sendReceiveMsg( - testMessages: SendableMessageInfo, + testMessages: ServiceBusMessage, autoCompleteFlag: boolean ): Promise { await senderClient.send(testMessages); @@ -313,11 +313,9 @@ describe("receive and delete", () => { await afterEachTest(); }); - async function sendReceiveMsg( - testMessages: SendableMessageInfo - ): Promise<{ message: ReceivedMessage; context: ContextWithSettlement }> { + async function sendReceiveMsg(testMessages: ServiceBusMessage): Promise { await senderClient.send(testMessages); - const msgs = (await receiverClient.receiveBatch(1)).messages; + const msgs = await receiverClient.receiveBatch(1); should.equal(Array.isArray(msgs), true, "`ReceivedMessages` is not an array"); should.equal(msgs.length, 1, "Unexpected number of messages"); @@ -329,12 +327,12 @@ describe("receive and delete", () => { ); should.equal(msgs[0].deliveryCount, 0, "DeliveryCount is different than expected"); - return { message: msgs[0], context: {} as ContextWithSettlement }; + return msgs[0]; } const testError = (err: Error, operation: DispositionType): void => { expect(err.message.toLowerCase(), "ErrorMessage is different than expected").includes( - `.context.${operation} is not a function` + `failed to ${operation} the message as the operation is only supported in \'peeklock\' receive mode.` ); }; @@ -343,16 +341,19 @@ describe("receive and delete", () => { useSessions?: boolean ): Promise { const testMessages = useSessions ? TestMessage.getSessionSample() : TestMessage.getSample(); - const msg = await sendReceiveMsg(testMessages); + // we have to force this cast - the type system doesn't allow this if you've chosen receiveAndDelete + // as your lock mode. + const msg = (await sendReceiveMsg(testMessages)) as ReceivedMessageWithLock ; + try { if (operation === DispositionType.complete) { - await msg.context.complete(msg.message); + await msg.complete(); } else if (operation === DispositionType.abandon) { - await msg.context.abandon(msg.message); + await msg.abandon(); } else if (operation === DispositionType.deadletter) { - await msg.context.deadLetter(msg.message); + await msg.deadLetter(); } else if (operation === DispositionType.defer) { - await msg.context.defer(msg.message); + await msg.defer(); } } catch (err) { errorWasThrown = true; @@ -680,10 +681,11 @@ describe("receive and delete", () => { async function testRenewLock(): Promise { const msg = await sendReceiveMsg(TestMessage.getSample()); - await receiverClient.renewMessageLock(msg.message).catch((err) => { + // have to cast it - the type system doesn't allow us to call into this method otherwise. + await (msg as ReceivedMessageWithLock ).renewLock().catch((err) => { should.equal( err.message, - getErrorMessageNotSupportedInReceiveAndDeleteMode("renew the message lock"), + getErrorMessageNotSupportedInReceiveAndDeleteMode("renew the lock on the message"), "ErrorMessage is different than expected" ); errorWasThrown = true; diff --git a/sdk/servicebus/service-bus/test/renewLock.spec.ts b/sdk/servicebus/service-bus/test/renewLock.spec.ts index 540f55d05c0f..ac37658bff0d 100644 --- a/sdk/servicebus/service-bus/test/renewLock.spec.ts +++ b/sdk/servicebus/service-bus/test/renewLock.spec.ts @@ -5,17 +5,17 @@ import chai from "chai"; const should = chai.should(); import chaiAsPromised from "chai-as-promised"; chai.use(chaiAsPromised); -import { ReceivedMessage, ContextWithSettlement } from "../src"; import { delay } from "rhea-promise"; import { TestClientType, TestMessage } from "./utils/testUtils"; import { ServiceBusClientForTests, createServiceBusClientForTests } from "./utils/testutils2"; import { Receiver } from "../src/receivers/receiver"; import { Sender } from "../src/sender"; +import { ReceivedMessageWithLock } from "../src/serviceBusMessage"; describe("renew lock", () => { let serviceBusClient: ServiceBusClientForTests; let senderClient: Sender; - let receiverClient: Receiver; + let receiverClient: Receiver; before(() => { serviceBusClient = createServiceBusClientForTests(); @@ -268,13 +268,12 @@ describe("renew lock", () => { */ async function testBatchReceiverManualLockRenewalHappyCase( senderClient: Sender, - receiverClient: Receiver + receiverClient: Receiver ): Promise { const testMessage = TestMessage.getSample(); await senderClient.send(testMessage); - const batch = await receiverClient.receiveBatch(1); - const msgs = batch.messages; + const msgs = await receiverClient.receiveBatch(1); // Compute expected initial lock expiry time const expectedLockExpiryTimeUtc = new Date(); @@ -296,7 +295,7 @@ describe("renew lock", () => { await delay(5000); if (msgs[0].lockToken) { - await receiverClient.renewMessageLock(msgs[0].lockToken); + await msgs[0].renewLock(); } // Compute expected lock expiry time after renewing lock after 5 seconds @@ -309,7 +308,7 @@ describe("renew lock", () => { "After renewlock()" ); - await batch.context.complete(msgs[0]); + await msgs[0].complete(); } /** @@ -317,13 +316,12 @@ describe("renew lock", () => { */ async function testBatchReceiverManualLockRenewalErrorOnLockExpiry( senderClient: Sender, - receiverClient: Receiver + receiverClient: Receiver ): Promise { const testMessage = TestMessage.getSample(); await senderClient.send(testMessage); - const batch = await receiverClient.receiveBatch(1); - const msgs = batch.messages; + const msgs = await receiverClient.receiveBatch(1); should.equal(Array.isArray(msgs), true, "`ReceivedMessages` is not an array"); should.equal(msgs.length, 1, "Expected message length does not match"); @@ -334,7 +332,7 @@ describe("renew lock", () => { await delay(lockDurationInMilliseconds + 1000); let errorWasThrown: boolean = false; - await batch.context.complete(msgs[0]).catch((err) => { + await msgs[0].complete().catch((err) => { should.equal(err.code, "MessageLockLostError", "Error code is different than expected"); errorWasThrown = true; }); @@ -343,7 +341,7 @@ describe("renew lock", () => { // Clean up any left over messages const unprocessedMsgsBatch = await receiverClient.receiveBatch(1); - await unprocessedMsgsBatch.context.complete(unprocessedMsgsBatch.messages[0]); + await unprocessedMsgsBatch[0].complete(); } /** @@ -351,16 +349,13 @@ describe("renew lock", () => { */ async function testStreamingReceiverManualLockRenewalHappyCase( senderClient: Sender, - receiverClient: Receiver + receiverClient: Receiver ): Promise { let numOfMessagesReceived = 0; const testMessage = TestMessage.getSample(); await senderClient.send(testMessage); - async function processMessage( - brokeredMessage: ReceivedMessage, - context: ContextWithSettlement - ): Promise { + async function processMessage(brokeredMessage: ReceivedMessageWithLock): Promise { if (numOfMessagesReceived < 1) { numOfMessagesReceived++; @@ -389,7 +384,7 @@ describe("renew lock", () => { ); await delay(5000); - await receiverClient.renewMessageLock(brokeredMessage); + await brokeredMessage.renewLock(); // Compute expected lock expiry time after renewing lock after 5 seconds expectedLockExpiryTimeUtc.setSeconds(expectedLockExpiryTimeUtc.getSeconds() + 5); @@ -401,7 +396,7 @@ describe("renew lock", () => { "After renewlock" ); - await context.complete(brokeredMessage); + await brokeredMessage.complete(); } } @@ -430,7 +425,7 @@ describe("renew lock", () => { async function testAutoLockRenewalConfigBehavior( senderClient: Sender, - receiverClient: Receiver, + receiverClient: Receiver, options: AutoLockRenewalTestOptions, entityType: TestClientType ): Promise { @@ -438,10 +433,7 @@ describe("renew lock", () => { const testMessage = TestMessage.getSample(); await senderClient.send(testMessage); - async function processMessage( - brokeredMessage: ReceivedMessage, - context: ContextWithSettlement - ): Promise { + async function processMessage(brokeredMessage: ReceivedMessageWithLock): Promise { if (numOfMessagesReceived < 1) { numOfMessagesReceived++; @@ -460,7 +452,7 @@ describe("renew lock", () => { await delay(options.delayBeforeAttemptingToCompleteMessageInSeconds * 1000); let errorWasThrown: boolean = false; - await context.complete(brokeredMessage).catch((err) => { + await brokeredMessage.complete().catch((err) => { should.equal(err.code, "MessageLockLostError", "Error code is different than expected"); errorWasThrown = true; }); @@ -493,8 +485,8 @@ describe("renew lock", () => { ); const unprocessedMsgsBatch = await receiverClient.receiveBatch(1); - if (unprocessedMsgsBatch.messages.length) { - await unprocessedMsgsBatch.context.complete(unprocessedMsgsBatch.messages[0]); + if (unprocessedMsgsBatch.length) { + await unprocessedMsgsBatch[0].complete(); } } } diff --git a/sdk/servicebus/service-bus/test/renewLockSessions.spec.ts b/sdk/servicebus/service-bus/test/renewLockSessions.spec.ts index 178210c865e8..5389499699e8 100644 --- a/sdk/servicebus/service-bus/test/renewLockSessions.spec.ts +++ b/sdk/servicebus/service-bus/test/renewLockSessions.spec.ts @@ -5,21 +5,16 @@ import chai from "chai"; const should = chai.should(); import chaiAsPromised from "chai-as-promised"; chai.use(chaiAsPromised); -import { - MessagingError, - delay, - ReceivedMessage, - ContextWithSettlement, - SendableMessageInfo -} from "../src"; +import { MessagingError, delay, ServiceBusMessage } from "../src"; import { TestClientType, TestMessage, isMessagingError } from "./utils/testUtils"; import { ServiceBusClientForTests, createServiceBusClientForTests } from "./utils/testutils2"; import { Sender } from "../src/sender"; import { SessionReceiver } from "../src/receivers/sessionReceiver"; +import { ReceivedMessageWithLock } from "../src/serviceBusMessage"; describe("renew lock sessions", () => { let sender: Sender; - let receiver: SessionReceiver; + let receiver: SessionReceiver; let maxSessionAutoRenewLockDurationInSeconds: number; let sessionId: string; @@ -349,14 +344,13 @@ describe("renew lock sessions", () => { */ async function testBatchReceiverManualLockRenewalHappyCase( senderClient: Sender, - receiverClient: SessionReceiver + receiverClient: SessionReceiver ): Promise { const testMessage = getTestMessage(); testMessage.body = `testBatchReceiverManualLockRenewalHappyCase-${Date.now().toString()}`; await senderClient.send(testMessage); - const batch = await receiverClient.receiveBatch(1); - const msgs = batch.messages; + const msgs = await receiverClient.receiveBatch(1); // Compute expected initial lock expiry time const expectedLockExpiryTimeUtc = new Date(); @@ -389,7 +383,7 @@ describe("renew lock sessions", () => { "After renewlock()" ); - await batch.context.complete(msgs[0]); + await msgs[0].complete(); } /** @@ -398,14 +392,13 @@ describe("renew lock sessions", () => { async function testBatchReceiverManualLockRenewalErrorOnLockExpiry( entityType: TestClientType, senderClient: Sender, - receiver: SessionReceiver + receiver: SessionReceiver ): Promise { const testMessage = getTestMessage(); testMessage.body = `testBatchReceiverManualLockRenewalErrorOnLockExpiry-${Date.now().toString()}`; await senderClient.send(testMessage); - const batch = await receiver.receiveBatch(1); - const msgs = batch.messages; + const msgs = await receiver.receiveBatch(1); should.equal(Array.isArray(msgs), true, "`ReceivedMessages` is not an array"); should.equal(msgs.length, 1, "Expected message length does not match"); @@ -415,7 +408,7 @@ describe("renew lock sessions", () => { await delay(lockDurationInMilliseconds + 1000); let errorWasThrown: boolean = false; - await batch.context.complete(msgs[0]).catch((err) => { + await msgs[0].complete().catch((err) => { should.equal(err.code, "SessionLockLostError", "Error code is different than expected"); errorWasThrown = true; }); @@ -429,8 +422,8 @@ describe("renew lock sessions", () => { receiver = serviceBusClient.test.getSessionPeekLockReceiver(entityNames); const unprocessedMsgsBatch = await receiver.receiveBatch(1); - should.equal(unprocessedMsgsBatch.messages[0].deliveryCount, 1, "Unexpected deliveryCount"); - await unprocessedMsgsBatch.context.complete(unprocessedMsgsBatch.messages[0]); + should.equal(unprocessedMsgsBatch[0].deliveryCount, 1, "Unexpected deliveryCount"); + await unprocessedMsgsBatch[0].complete(); } /** @@ -438,17 +431,14 @@ describe("renew lock sessions", () => { */ async function testStreamingReceiverManualLockRenewalHappyCase( senderClient: Sender, - receiverClient: SessionReceiver + receiverClient: SessionReceiver ): Promise { let numOfMessagesReceived = 0; const testMessage = getTestMessage(); testMessage.body = `testStreamingReceiverManualLockRenewalHappyCase-${Date.now().toString()}`; await senderClient.send(testMessage); - async function processMessage( - brokeredMessage: ReceivedMessage, - context: ContextWithSettlement - ) { + async function processMessage(brokeredMessage: ReceivedMessageWithLock) { if (numOfMessagesReceived < 1) { numOfMessagesReceived++; @@ -489,7 +479,7 @@ describe("renew lock sessions", () => { "After renewlock()" ); - await context.complete(brokeredMessage); + await brokeredMessage.complete(); } } @@ -517,7 +507,7 @@ describe("renew lock sessions", () => { async function testAutoLockRenewalConfigBehavior( senderClient: Sender, - receiverClient: SessionReceiver, + receiverClient: SessionReceiver, options: AutoLockRenewalTestOptions ): Promise { let numOfMessagesReceived = 0; @@ -526,13 +516,9 @@ describe("renew lock sessions", () => { await senderClient.send(testMessage); let sessionLockLostErrorThrown = false; - const messagesReceived: ReceivedMessage[] = []; - let contextToSettle: ContextWithSettlement; + const messagesReceived: ReceivedMessageWithLock[] = []; - async function processMessage( - brokeredMessage: ReceivedMessage, - context: ContextWithSettlement - ): Promise { + async function processMessage(brokeredMessage: ReceivedMessageWithLock): Promise { if (numOfMessagesReceived < 1) { numOfMessagesReceived++; @@ -548,7 +534,6 @@ describe("renew lock sessions", () => { ); messagesReceived.push(brokeredMessage); - contextToSettle = context; // Sleeping... await delay(options.delayBeforeAttemptingToCompleteMessageInSeconds * 1000); @@ -583,7 +568,7 @@ describe("renew lock sessions", () => { should.equal(messagesReceived.length, 1, "Mismatch in number of messages received"); let errorWasThrown: boolean = false; - await contextToSettle!.complete(messagesReceived[0]).catch((err) => { + await messagesReceived[0].complete().catch((err) => { should.equal(err.code, "SessionLockLostError", "Error code is different than expected"); errorWasThrown = true; }); @@ -615,7 +600,7 @@ describe("renew lock sessions", () => { } } - function getTestMessage(): SendableMessageInfo { + function getTestMessage(): ServiceBusMessage { const baseMessage = TestMessage.getSessionSample(); baseMessage.sessionId = sessionId; return baseMessage; diff --git a/sdk/servicebus/service-bus/test/sendSchedule.spec.ts b/sdk/servicebus/service-bus/test/sendSchedule.spec.ts index 8f949a4313f3..b1583da2490d 100644 --- a/sdk/servicebus/service-bus/test/sendSchedule.spec.ts +++ b/sdk/servicebus/service-bus/test/sendSchedule.spec.ts @@ -5,7 +5,7 @@ import chai from "chai"; const should = chai.should(); import chaiAsPromised from "chai-as-promised"; chai.use(chaiAsPromised); -import { delay, SendableMessageInfo, ContextWithSettlement } from "../src"; +import { delay, ServiceBusMessage } from "../src"; import { TestMessage, TestClientType } from "./utils/testUtils"; import { Receiver } from "../src/receivers/receiver"; import { @@ -14,10 +14,11 @@ import { testPeekMsgsLength } from "./utils/testutils2"; import { Sender } from "../src/sender"; +import { ReceivedMessageWithLock } from "../src/serviceBusMessage"; describe("send scheduled messages", () => { let senderClient: Sender; - let receiverClient: Receiver; + let receiverClient: Receiver; let serviceBusClient: ServiceBusClientForTests; before(() => { @@ -50,8 +51,7 @@ describe("send scheduled messages", () => { async function testSimpleSend(useSessions: boolean, usePartitions: boolean): Promise { const testMessage = useSessions ? TestMessage.getSessionSample() : TestMessage.getSample(); await senderClient.send(testMessage); - const batch = await receiverClient.receiveBatch(1); - const msgs = batch.messages; + const msgs = await receiverClient.receiveBatch(1); should.equal(Array.isArray(msgs), true, "`ReceivedMessages` is not an array"); should.equal(msgs.length, 1, "Unexpected number of messages"); @@ -59,7 +59,7 @@ describe("send scheduled messages", () => { TestMessage.checkMessageContents(testMessage, msgs[0], useSessions, usePartitions); - await batch.context.complete(msgs[0]); + await msgs[0].complete(); await testPeekMsgsLength(receiverClient, 0); } @@ -119,8 +119,7 @@ describe("send scheduled messages", () => { testMessages.push(useSessions ? TestMessage.getSessionSample() : TestMessage.getSample()); await senderClient.sendBatch(testMessages); - const batch = await receiverClient.receiveBatch(2); - const msgs = batch.messages; + const msgs = await receiverClient.receiveBatch(2); should.equal(Array.isArray(msgs), true, "`ReceivedMessages` is not an array"); should.equal(msgs.length, 2, "Unexpected number of messages"); @@ -133,8 +132,8 @@ describe("send scheduled messages", () => { TestMessage.checkMessageContents(testMessages[0], msgs[1], useSessions, usePartitions); } - await batch.context.complete(msgs[0]); - await batch.context.complete(msgs[1]); + await msgs[0].complete(); + await msgs[1].complete(); await testPeekMsgsLength(receiverClient, 0); } @@ -206,8 +205,7 @@ describe("send scheduled messages", () => { await senderClient.scheduleMessage(scheduleTime, testMessage); } - const batch = await receiverClient.receiveBatch(1); - const msgs = batch.messages; + const msgs = await receiverClient.receiveBatch(1); const msgEnqueueTime = msgs[0].enqueuedTimeUtc ? msgs[0].enqueuedTimeUtc.valueOf() : 0; should.equal(Array.isArray(msgs), true, "`ReceivedMessages` is not an array"); @@ -224,7 +222,7 @@ describe("send scheduled messages", () => { "MessageId is different than expected" ); - await batch.context.complete(msgs[0]); + await msgs[0].complete(); await testPeekMsgsLength(receiverClient, 0); } @@ -279,7 +277,7 @@ describe("send scheduled messages", () => { await afterEachTest(); }); - const messages: SendableMessageInfo[] = [ + const messages: ServiceBusMessage[] = [ { body: "hello1", messageId: `test message ${Math.random()}`, @@ -291,7 +289,7 @@ describe("send scheduled messages", () => { partitionKey: "dummy" // partitionKey is only for partitioned queue/subscrption, Unpartitioned queue/subscrption do not care about partitionKey. } ]; - const messageWithSessions: SendableMessageInfo[] = [ + const messageWithSessions: ServiceBusMessage[] = [ { body: "hello1", messageId: `test message ${Math.random()}`, @@ -309,8 +307,7 @@ describe("send scheduled messages", () => { const scheduleTime = new Date(Date.now() + 10000); // 10 seconds from now await senderClient.scheduleMessages(scheduleTime, testMessages); - const batch = await receiverClient.receiveBatch(2); - const msgs = batch.messages; + const msgs = await receiverClient.receiveBatch(2); should.equal(Array.isArray(msgs), true, "`ReceivedMessages` is not an array"); should.equal(msgs.length, 2, "Unexpected number of messages"); @@ -339,8 +336,8 @@ describe("send scheduled messages", () => { "MessageId of second message is different than expected" ); - await batch.context.complete(msgs[0]); - await batch.context.complete(msgs[1]); + await msgs[0].complete(); + await msgs[1].complete(); await testPeekMsgsLength(receiverClient, 0); } @@ -536,7 +533,7 @@ describe("send scheduled messages", () => { }); }); - describe("SendableMessageInfo validations #RunInBrowser", function(): void { + describe("ServiceBusMessage validations #RunInBrowser", function(): void { const longString = "A very very very very very very very very very very very very very very very very very very very very very very very very very long string."; after(async () => { @@ -548,7 +545,7 @@ describe("send scheduled messages", () => { }); const testInputs: { - message: SendableMessageInfo; + message: ServiceBusMessage; expectedErrorMessage: string; title?: string; }[] = [ @@ -682,13 +679,15 @@ describe("send scheduled messages", () => { }); async function testReceivedMsgsLength( - receiverClient: Receiver, + receiverClient: Receiver, expectedReceivedMsgsLength: number ): Promise { - const receivedMsgs = await receiverClient.receiveBatch(expectedReceivedMsgsLength + 1, 5); + const receivedMsgs = await receiverClient.receiveBatch(expectedReceivedMsgsLength + 1, { + maxWaitTimeSeconds: 5 + }); should.equal( - receivedMsgs.messages.length, + receivedMsgs.length, expectedReceivedMsgsLength, "Unexpected number of msgs found when receiving" ); diff --git a/sdk/servicebus/service-bus/test/sessionsRequiredCleanEntityTests.spec.ts b/sdk/servicebus/service-bus/test/sessionsRequiredCleanEntityTests.spec.ts index bf40207d9ea0..97aafbb0658b 100644 --- a/sdk/servicebus/service-bus/test/sessionsRequiredCleanEntityTests.spec.ts +++ b/sdk/servicebus/service-bus/test/sessionsRequiredCleanEntityTests.spec.ts @@ -8,8 +8,9 @@ import { testPeekMsgsLength } from "./utils/testutils2"; import { Sender } from "../src/sender"; -import { SessionReceiver, ContextWithSettlement, SendableMessageInfo } from "../src"; +import { SessionReceiver, ServiceBusMessage } from "../src"; import { TestClientType, TestMessage } from "./utils/testUtils"; +import { ReceivedMessageWithLock } from "../src/serviceBusMessage"; const should = chai.should(); // NOTE: these tests should be reworked, if possible. Since they need to be deterministic @@ -21,7 +22,7 @@ const should = chai.should(); describe("sessions tests - requires completely clean entity for each test", () => { let serviceBusClient: ServiceBusClientForTests; let sender: Sender; - let receiver: SessionReceiver; + let receiver: SessionReceiver; async function beforeEachNoSessionTest( testClientType: TestClientType, @@ -101,8 +102,7 @@ describe("sessions tests - requires completely clean entity for each test", () "SessionId is different than expected" ); - const batch = await receiver.receiveBatch(1); - const msgs = batch.messages; + const msgs = await receiver.receiveBatch(1); should.equal(msgs.length, 1, "Unexpected number of messages received"); should.equal(msgs[0].body, testMessage.body, "MessageBody is different than expected"); should.equal( @@ -116,7 +116,7 @@ describe("sessions tests - requires completely clean entity for each test", () "SessionId is different than expected" ); - await batch.context.complete(msgs[0]); + await msgs[0].complete(); } it("Partitioned Queue - Peek Session with sessionId", async function(): Promise { @@ -160,7 +160,7 @@ describe("sessions tests - requires completely clean entity for each test", () await afterEachTest(); }); - const testMessagesWithDifferentSessionIds: SendableMessageInfo[] = [ + const testMessagesWithDifferentSessionIds: ServiceBusMessage[] = [ { body: "hello1", messageId: `test message ${Math.random()}`, @@ -177,8 +177,7 @@ describe("sessions tests - requires completely clean entity for each test", () await sender.send(testMessagesWithDifferentSessionIds[0]); await sender.send(testMessagesWithDifferentSessionIds[1]); - let batch = await receiver.receiveBatch(2); - let msgs = batch.messages; + let msgs = await receiver.receiveBatch(2); should.equal(msgs.length, 1, "Unexpected number of messages received"); should.equal(receiver.sessionId, msgs[0].sessionId, "Unexpected sessionId in receiver"); @@ -192,7 +191,7 @@ describe("sessions tests - requires completely clean entity for each test", () true, "Received Message doesnt match any of the test messages" ); - await batch.context.complete(msgs[0]); + await msgs[0].complete(); await receiver.close(); const entityNames = serviceBusClient.test.getTestEntities(testClientType); @@ -200,8 +199,7 @@ describe("sessions tests - requires completely clean entity for each test", () // get the next available session ID rather than specifying one receiver = serviceBusClient.test.getSessionPeekLockReceiver(entityNames); - batch = await receiver.receiveBatch(2); - msgs = batch.messages; + msgs = await receiver.receiveBatch(2); should.equal(msgs.length, 1, "Unexpected number of messages received"); should.equal(receiver.sessionId, msgs[0].sessionId, "Unexpected sessionId in receiver"); @@ -215,7 +213,7 @@ describe("sessions tests - requires completely clean entity for each test", () true, "Received Message doesnt match any of the test messages" ); - await batch.context.complete(msgs[0]); + await msgs[0].complete(); await testPeekMsgsLength(receiver, 0); } @@ -255,7 +253,7 @@ describe("sessions tests - requires completely clean entity for each test", () // Sending messages with different session id, so that we know for sure we pick the right one // and that Service Bus is not choosing a random one for us - const testMessagesWithDifferentSessionIds: SendableMessageInfo[] = [ + const testMessagesWithDifferentSessionIds: ServiceBusMessage[] = [ { body: "hello1", messageId: `test message ${Math.random()}`, @@ -277,8 +275,7 @@ describe("sessions tests - requires completely clean entity for each test", () // get the next available session ID rather than specifying one receiver = serviceBusClient.test.getSessionPeekLockReceiver(entityNames, { sessionId: "" }); - const batch = await receiver.receiveBatch(2); - const msgs = batch.messages; + const msgs = await receiver.receiveBatch(2); should.equal(msgs.length, 1, "Unexpected number of messages received"); @@ -290,7 +287,7 @@ describe("sessions tests - requires completely clean entity for each test", () true, "Received Message doesnt match expected test message" ); - await batch.context.complete(msgs[0]); + await msgs[0].complete(); const peekedMsgsInSession = await receiver.diagnostics.peek(); should.equal(peekedMsgsInSession.length, 0, "Unexpected number of messages peeked"); diff --git a/sdk/servicebus/service-bus/test/sessionsTests.spec.ts b/sdk/servicebus/service-bus/test/sessionsTests.spec.ts index 700548f3cdce..7c46e900c46a 100644 --- a/sdk/servicebus/service-bus/test/sessionsTests.spec.ts +++ b/sdk/servicebus/service-bus/test/sessionsTests.spec.ts @@ -5,7 +5,7 @@ import chai from "chai"; const should = chai.should(); import chaiAsPromised from "chai-as-promised"; chai.use(chaiAsPromised); -import { delay, ReceivedMessage, ContextWithSettlement } from "../src"; +import { delay, ReceivedMessage } from "../src"; import { TestMessage, TestClientType, checkWithTimeout } from "./utils/testUtils"; import { Sender } from "../src/sender"; @@ -15,6 +15,7 @@ import { ServiceBusClientForTests, createServiceBusClientForTests } from "./utils/testutils2"; +import { ReceivedMessageWithLock } from "../src/serviceBusMessage"; let unexpectedError: Error | undefined; @@ -27,7 +28,7 @@ async function processError(err: Error): Promise { describe("session tests", () => { let serviceBusClient: ServiceBusClientForTests; let sender: Sender; - let receiver: SessionReceiver; + let receiver: SessionReceiver; before(async () => { serviceBusClient = createServiceBusClientForTests(); @@ -84,8 +85,7 @@ describe("session tests", () => { const testMessage = TestMessage.getSessionSample(); await sender.send(testMessage); - let batch = await receiver.receiveBatch(1, 10); - let msgs = batch.messages; + let msgs = await receiver.receiveBatch(1, { maxWaitTimeSeconds: 10 }); should.equal(msgs.length, 0, "Unexpected number of messages received"); await receiver.close(); @@ -95,8 +95,7 @@ describe("session tests", () => { // get the next available session ID rather than specifying one receiver = serviceBusClient.test.getSessionPeekLockReceiver(entityNames); - batch = await receiver.receiveBatch(1); - msgs = batch.messages; + msgs = await receiver.receiveBatch(1); should.equal(msgs.length, 1, "Unexpected number of messages received"); should.equal(Array.isArray(msgs), true, "`ReceivedMessages` is not an array"); should.equal(msgs[0].body, testMessage.body, "MessageBody is different than expected"); @@ -105,7 +104,7 @@ describe("session tests", () => { testMessage.messageId, "MessageId is different than expected" ); - await batch.context.complete(msgs[0]); + await msgs[0].complete(); await testPeekMsgsLength(receiver, 0); } @@ -149,7 +148,7 @@ describe("session tests", () => { let receivedMsgs: ReceivedMessage[] = []; receiver.subscribe({ - async processMessage(msg: ReceivedMessage, context: ContextWithSettlement) { + async processMessage(msg: ReceivedMessage) { receivedMsgs.push(msg); return Promise.resolve(); }, @@ -167,14 +166,14 @@ describe("session tests", () => { receivedMsgs = []; receiver.subscribe( { - async processMessage(msg: ReceivedMessage, context: ContextWithSettlement) { + async processMessage(msg: ReceivedMessageWithLock) { should.equal(msg.body, testMessage.body, "MessageBody is different than expected"); should.equal( msg.messageId, testMessage.messageId, "MessageId is different than expected" ); - await context.complete(msg); + await msg.complete(); receivedMsgs.push(msg); }, processError @@ -233,8 +232,7 @@ describe("session tests", () => { const testMessage = TestMessage.getSessionSample(); await sender.send(testMessage); - let batch = await receiver.receiveBatch(2); - let msgs = batch.messages; + let msgs = await receiver.receiveBatch(2); should.equal(Array.isArray(msgs), true, "`ReceivedMessages` is not an array"); should.equal(msgs.length, 1, "Unexpected number of messages received"); @@ -263,8 +261,7 @@ describe("session tests", () => { // get the next available session ID rather than specifying one receiver = serviceBusClient.test.getSessionPeekLockReceiver(entityNames); - batch = await receiver.receiveBatch(2); - msgs = batch.messages; + msgs = await receiver.receiveBatch(2); should.equal(Array.isArray(msgs), true, "`ReceivedMessages` is not an array"); should.equal(msgs.length, 1, "Unexpected number of messages received"); @@ -284,7 +281,7 @@ describe("session tests", () => { should.equal(testState, "new_state", "SessionState is different than expected"); await receiver.setState(""); // clearing the session-state - await batch.context.complete(msgs[0]); + await msgs[0].complete(); await testPeekMsgsLength(receiver, 0); } it("Partitioned Queue - Testing getState and setState", async function(): Promise { diff --git a/sdk/servicebus/service-bus/test/streamingReceiver.spec.ts b/sdk/servicebus/service-bus/test/streamingReceiver.spec.ts index df947d7593f3..ce3752afdd1a 100644 --- a/sdk/servicebus/service-bus/test/streamingReceiver.spec.ts +++ b/sdk/servicebus/service-bus/test/streamingReceiver.spec.ts @@ -3,12 +3,16 @@ import chai from "chai"; import chaiAsPromised from "chai-as-promised"; -import { delay, ReceiveMode, ReceivedMessage, ContextWithSettlement } from "../src"; +import { delay, ReceiveMode, ReceivedMessage } from "../src"; import { getAlreadyReceivingErrorMsg } from "../src/util/errors"; import { checkWithTimeout, TestClientType, TestMessage } from "./utils/testUtils"; import { StreamingReceiver } from "../src/core/streamingReceiver"; -import { DispositionType } from "../src/serviceBusMessage"; +import { + DispositionType, + ServiceBusMessageImpl, + ReceivedMessageWithLock +} from "../src/serviceBusMessage"; import { Receiver } from "../src/receivers/receiver"; import { Sender } from "../src/sender"; import { @@ -16,6 +20,7 @@ import { createServiceBusClientForTests, testPeekMsgsLength } from "./utils/testutils2"; +import { getDeliveryProperty } from "./utils/misc"; const should = chai.should(); chai.use(chaiAsPromised); @@ -33,8 +38,8 @@ async function processError(err: Error): Promise { describe("Streaming", () => { let serviceBusClient: ServiceBusClientForTests; let senderClient: Sender; - let receiverClient: Receiver; - let deadLetterClient: Receiver; + let receiverClient: Receiver; + let deadLetterClient: Receiver; before(() => { serviceBusClient = createServiceBusClientForTests(); @@ -87,7 +92,9 @@ describe("Streaming", () => { }); const msgsCheck = await checkWithTimeout( - () => receivedMsgs.length === 1 && receivedMsgs[0].delivery.remote_settled === true + () => + receivedMsgs.length === 1 && + (receivedMsgs[0] as ServiceBusMessageImpl).delivery.remote_settled === true ); should.equal( @@ -123,7 +130,8 @@ describe("Streaming", () => { }); const msgsCheck = await checkWithTimeout( - () => receivedMsgs.length === 1 && receivedMsgs[0].delivery.remote_settled === true + () => + receivedMsgs.length === 1 && getDeliveryProperty(receivedMsgs[0]).remote_settled === true ); should.equal( @@ -171,13 +179,11 @@ describe("Streaming", () => { const testMessage = TestMessage.getSample(); await senderClient.send(testMessage); - const receivedMsgs: ReceivedMessage[] = []; - let contextToSettle: ContextWithSettlement; + const receivedMsgs: ReceivedMessageWithLock[] = []; receiverClient.subscribe( { - async processMessage(msg: ReceivedMessage, context: ContextWithSettlement) { + async processMessage(msg: ReceivedMessageWithLock) { receivedMsgs.push(msg); - contextToSettle = context; should.equal(msg.body, testMessage.body, "MessageBody is different than expected"); should.equal( msg.messageId, @@ -197,7 +203,7 @@ describe("Streaming", () => { await testPeekMsgsLength(receiverClient, 1); should.equal(receivedMsgs.length, 1, "Unexpected number of messages"); - await contextToSettle!.complete(receivedMsgs[0]); + await receivedMsgs[0].complete(); should.equal(unexpectedError, undefined, unexpectedError && unexpectedError.message); await testPeekMsgsLength(receiverClient, 0); @@ -283,17 +289,17 @@ describe("Streaming", () => { const testMessage = TestMessage.getSample(); await senderClient.send(testMessage); - const receivedMsgs: ReceivedMessage[] = []; + const receivedMsgs: ReceivedMessageWithLock[] = []; receiverClient.subscribe( { - async processMessage(msg: ReceivedMessage, context: ContextWithSettlement) { + async processMessage(msg: ReceivedMessageWithLock) { should.equal(msg.body, testMessage.body, "MessageBody is different than expected"); should.equal( msg.messageId, testMessage.messageId, "MessageId is different than expected" ); - await context.complete(msg); + await msg.complete(); receivedMsgs.push(msg); }, processError @@ -370,13 +376,13 @@ describe("Streaming", () => { receiverClient.subscribe( { - async processMessage(msg: ReceivedMessage, context: ContextWithSettlement) { + async processMessage(msg: ReceivedMessageWithLock) { should.equal( msg.deliveryCount, checkDeliveryCount, "DeliveryCount is different than expected" ); - await context.abandon(msg); + await msg.abandon(); checkDeliveryCount++; }, processError @@ -393,8 +399,7 @@ describe("Streaming", () => { await testPeekMsgsLength(receiverClient, 0); // No messages in the queue - const deadLetterMsgsBatch = await deadLetterClient.receiveBatch(1); - const deadLetterMsgs = deadLetterMsgsBatch.messages; + const deadLetterMsgs = await deadLetterClient.receiveBatch(1); should.equal(Array.isArray(deadLetterMsgs), true, "`ReceivedMessages` is not an array"); should.equal(deadLetterMsgs.length, 1, "Unexpected number of messages"); should.equal( @@ -408,7 +413,7 @@ describe("Streaming", () => { "MessageId is different than expected" ); - await deadLetterMsgsBatch.context.complete(deadLetterMsgs[0]); + await deadLetterMsgs[0].complete(); await testPeekMsgsLength(deadLetterClient, 0); } @@ -454,8 +459,8 @@ describe("Streaming", () => { receiverClient.subscribe( { - async processMessage(msg: ReceivedMessage, context: ContextWithSettlement) { - await context.defer(msg); + async processMessage(msg: ReceivedMessageWithLock) { + await msg.defer(); sequenceNum = msg.sequenceNumber; }, processError @@ -561,8 +566,8 @@ describe("Streaming", () => { receiverClient.subscribe( { - async processMessage(msg: ReceivedMessage, context: ContextWithSettlement) { - await context.deadLetter(msg); + async processMessage(msg: ReceivedMessageWithLock) { + await msg.deadLetter(); receivedMsgs.push(msg); }, processError @@ -577,8 +582,7 @@ describe("Streaming", () => { await testPeekMsgsLength(receiverClient, 0); - const deadLetterMsgsBatch = await deadLetterClient.receiveBatch(1); - const deadLetterMsgs = deadLetterMsgsBatch.messages; + const deadLetterMsgs = await deadLetterClient.receiveBatch(1); should.equal(Array.isArray(deadLetterMsgs), true, "`ReceivedMessages` is not an array"); should.equal(deadLetterMsgs.length, 1, "Unexpected number of messages"); should.equal( @@ -587,7 +591,7 @@ describe("Streaming", () => { "MessageId is different than expected" ); - await deadLetterMsgsBatch.context.complete(deadLetterMsgs[0]); + await deadLetterMsgs[0].complete(); await testPeekMsgsLength(deadLetterClient, 0); } @@ -658,8 +662,8 @@ describe("Streaming", () => { const expectedErrorMessage = getAlreadyReceivingErrorMsg(receiverClient.entityPath); receiverClient.subscribe({ - async processMessage(msg: ReceivedMessage, context: ContextWithSettlement) { - await context.complete(msg); + async processMessage(msg: ReceivedMessageWithLock) { + await msg.complete(); }, processError }); @@ -719,19 +723,18 @@ describe("Streaming", () => { async function testSettlement(operation: DispositionType): Promise { const testMessage = TestMessage.getSample(); await senderClient.send(testMessage); - const receivedMsgs: ReceivedMessage[] = []; - let contextToSettle: ContextWithSettlement; + const receivedMsgs: ReceivedMessageWithLock[] = []; receiverClient.subscribe({ - async processMessage(msg: ReceivedMessage, context: ContextWithSettlement) { + async processMessage(msg: ReceivedMessageWithLock) { receivedMsgs.push(msg); - contextToSettle = context; return Promise.resolve(); }, processError }); const msgsCheck = await checkWithTimeout( - () => receivedMsgs.length === 1 && receivedMsgs[0].delivery.remote_settled === true + () => + receivedMsgs.length === 1 && getDeliveryProperty(receivedMsgs[0]).remote_settled === true ); should.equal( msgsCheck, @@ -758,15 +761,13 @@ describe("Streaming", () => { await testPeekMsgsLength(receiverClient, 0); if (operation === DispositionType.complete) { - await contextToSettle!.complete(receivedMsgs[0]).catch((err) => testError(err, operation)); + await receivedMsgs[0].complete().catch((err) => testError(err, operation)); } else if (operation === DispositionType.abandon) { - await contextToSettle!.abandon(receivedMsgs[0]).catch((err) => testError(err, operation)); + await receivedMsgs[0].abandon().catch((err) => testError(err, operation)); } else if (operation === DispositionType.deadletter) { - await contextToSettle! - .deadLetter(receivedMsgs[0]) - .catch((err) => testError(err, operation)); + await receivedMsgs[0].deadLetter().catch((err) => testError(err, operation)); } else if (operation === DispositionType.defer) { - await contextToSettle!.defer(receivedMsgs[0]).catch((err) => testError(err, operation)); + await receivedMsgs[0].defer().catch((err) => testError(err, operation)); } should.equal(errorWasThrown, true, "Error thrown flag must be true"); @@ -828,10 +829,10 @@ describe("Streaming", () => { await senderClient.send(TestMessage.getSample()); const errorMessage = "Will we see this error message?"; - const receivedMsgs: ReceivedMessage[] = []; + const receivedMsgs: ReceivedMessageWithLock[] = []; receiverClient.subscribe({ - async processMessage(msg: ReceivedMessage, context: ContextWithSettlement) { - await context.complete(msg); + async processMessage(msg: ReceivedMessageWithLock) { + await msg.complete(); receivedMsgs.push(msg); throw new Error(errorMessage); }, @@ -954,7 +955,7 @@ describe("Streaming", () => { receiverClient.subscribe( { - async processMessage(msg: ReceivedMessage, context: ContextWithSettlement) { + async processMessage(msg: ReceivedMessageWithLock) { if (receivedMsgs.length === 1) { if ((!maxConcurrentCalls || maxConcurrentCalls === 1) && settledMsgs.length === 0) { throw new Error( @@ -971,7 +972,7 @@ describe("Streaming", () => { receivedMsgs.push(msg); await delay(2000); - await context.complete(msg); + await msg.complete(); settledMsgs.push(msg); }, processError diff --git a/sdk/servicebus/service-bus/test/streamingReceiverSessions.spec.ts b/sdk/servicebus/service-bus/test/streamingReceiverSessions.spec.ts index 36a814d097b3..b3268938a0a5 100644 --- a/sdk/servicebus/service-bus/test/streamingReceiverSessions.spec.ts +++ b/sdk/servicebus/service-bus/test/streamingReceiverSessions.spec.ts @@ -3,10 +3,10 @@ import chai from "chai"; import chaiAsPromised from "chai-as-promised"; -import { delay, ReceivedMessage, ContextWithSettlement } from "../src"; +import { delay, ReceivedMessage } from "../src"; import { getAlreadyReceivingErrorMsg } from "../src/util/errors"; import { checkWithTimeout, TestClientType, TestMessage } from "./utils/testUtils"; -import { DispositionType } from "../src/serviceBusMessage"; +import { DispositionType, ReceivedMessageWithLock } from "../src/serviceBusMessage"; import { SessionReceiver } from "../src/receivers/sessionReceiver"; import { Receiver } from "../src/receivers/receiver"; import { Sender } from "../src/sender"; @@ -15,13 +15,14 @@ import { ServiceBusClientForTests, testPeekMsgsLength } from "./utils/testutils2"; +import { getDeliveryProperty } from "./utils/misc"; const should = chai.should(); chai.use(chaiAsPromised); describe("Streaming with sessions", () => { let senderClient: Sender; - let receiverClient: SessionReceiver; - let deadLetterClient: Receiver; + let receiverClient: SessionReceiver; + let deadLetterClient: Receiver; let errorWasThrown: boolean; let unexpectedError: Error | undefined; let serviceBusClient: ServiceBusClientForTests; @@ -89,7 +90,7 @@ describe("Streaming with sessions", () => { const receivedMsgs: ReceivedMessage[] = []; receiverClient.subscribe({ - async processMessage(msg: ReceivedMessage, context: ContextWithSettlement) { + async processMessage(msg: ReceivedMessage) { receivedMsgs.push(msg); should.equal(msg.body, testMessage.body, "MessageBody is different than expected"); should.equal( @@ -104,7 +105,8 @@ describe("Streaming with sessions", () => { }); const msgsCheck = await checkWithTimeout( - () => receivedMsgs.length === 1 && receivedMsgs[0].delivery.remote_settled === true + () => + receivedMsgs.length === 1 && getDeliveryProperty(receivedMsgs[0]).remote_settled === true ); should.equal( msgsCheck, @@ -149,12 +151,10 @@ describe("Streaming with sessions", () => { async function testManualComplete(): Promise { const testMessage = TestMessage.getSessionSample(); await senderClient.send(testMessage); - let contextToSettle: ContextWithSettlement; - const receivedMsgs: ReceivedMessage[] = []; + const receivedMsgs: ReceivedMessageWithLock[] = []; receiverClient.subscribe( { - async processMessage(msg: ReceivedMessage, context: ContextWithSettlement) { - contextToSettle = context; + async processMessage(msg: ReceivedMessageWithLock) { receivedMsgs.push(msg); should.equal(msg.body, testMessage.body, "MessageBody is different than expected"); should.equal( @@ -174,7 +174,7 @@ describe("Streaming with sessions", () => { await testPeekMsgsLength(receiverClient, 1); - await contextToSettle!.complete(receivedMsgs[0]); + await receivedMsgs[0].complete(); should.equal(unexpectedError, undefined, unexpectedError && unexpectedError.message); should.equal(receivedMsgs.length, 1, "Unexpected number of messages"); @@ -219,17 +219,17 @@ describe("Streaming with sessions", () => { const testMessage = TestMessage.getSessionSample(); await senderClient.send(testMessage); - const receivedMsgs: ReceivedMessage[] = []; + const receivedMsgs: ReceivedMessageWithLock[] = []; receiverClient.subscribe( { - async processMessage(msg: ReceivedMessage, context: ContextWithSettlement) { + async processMessage(msg: ReceivedMessageWithLock) { should.equal(msg.body, testMessage.body, "MessageBody is different than expected"); should.equal( msg.messageId, testMessage.messageId, "MessageId is different than expected" ); - await context.complete(msg); + await msg.complete(); receivedMsgs.push(msg); }, processError @@ -321,8 +321,8 @@ describe("Streaming with sessions", () => { receiverClient.subscribe( { - async processMessage(msg: ReceivedMessage, context: ContextWithSettlement) { - return context.abandon(msg).then(() => { + async processMessage(msg: ReceivedMessageWithLock) { + return msg.abandon().then(() => { abandonFlag = 1; if (receiverClient.isReceivingMessages()) { return receiverClient.close(); @@ -346,8 +346,7 @@ describe("Streaming with sessions", () => { await createReceiverForTests(testClientType); - const batch = await receiverClient.receiveBatch(1); - const receivedMsgs = batch.messages; + const receivedMsgs = await receiverClient.receiveBatch(1); should.equal(receivedMsgs.length, 1, "Unexpected number of messages"); should.equal( receivedMsgs[0].messageId, @@ -355,7 +354,7 @@ describe("Streaming with sessions", () => { "MessageId is different than expected" ); should.equal(receivedMsgs[0].deliveryCount, 1, "DeliveryCount is different than expected"); - await batch.context.complete(receivedMsgs[0]); + await receivedMsgs[0].complete(); await testPeekMsgsLength(receiverClient, 0); } it("Partitioned Queue: abandon() retains message with incremented deliveryCount(with sessions)", async function(): Promise< @@ -419,8 +418,8 @@ describe("Streaming with sessions", () => { let sequenceNum: any = 0; receiverClient.subscribe( { - async processMessage(msg: ReceivedMessage, context: ContextWithSettlement) { - await context.defer(msg); + async processMessage(msg: ReceivedMessageWithLock) { + await msg.defer(); sequenceNum = msg.sequenceNumber; }, processError @@ -522,7 +521,7 @@ describe("Streaming with sessions", () => { let msgCount = 0; receiverClient.subscribe( { - async processMessage(msg: ReceivedMessage, context: ContextWithSettlement) { + async processMessage(msg: ReceivedMessageWithLock) { await msg.deadLetter(); msgCount++; }, @@ -538,8 +537,7 @@ describe("Streaming with sessions", () => { should.equal(msgCount, 1, "Unexpected number of messages"); await testPeekMsgsLength(receiverClient, 0); - const batch = await deadLetterClient.receiveBatch(1); - const deadLetterMsgs = batch.messages; + const deadLetterMsgs = await deadLetterClient.receiveBatch(1); should.equal(Array.isArray(deadLetterMsgs), true, "`ReceivedMessages` is not an array"); should.equal(deadLetterMsgs.length, 1, "Unexpected number of messages"); should.equal( @@ -548,7 +546,7 @@ describe("Streaming with sessions", () => { "MessageId is different than expected" ); - await batch.context.complete(deadLetterMsgs[0]); + await deadLetterMsgs[0].complete(); await testPeekMsgsLength(deadLetterClient, 0); } @@ -621,8 +619,8 @@ describe("Streaming with sessions", () => { TestMessage.sessionId ); receiverClient.subscribe({ - async processMessage(msg: ReceivedMessage, context: ContextWithSettlement) { - return context.complete(msg); + async processMessage(msg: ReceivedMessageWithLock) { + return msg.complete(); }, processError }); @@ -683,19 +681,18 @@ describe("Streaming with sessions", () => { const testMessage = TestMessage.getSessionSample(); await senderClient.send(testMessage); - const receivedMsgs: ReceivedMessage[] = []; - let contextToSettle: ContextWithSettlement; + const receivedMsgs: ReceivedMessageWithLock[] = []; receiverClient.subscribe({ - async processMessage(msg: ReceivedMessage, context: ContextWithSettlement) { + async processMessage(msg: ReceivedMessageWithLock) { receivedMsgs.push(msg); - contextToSettle = context; return Promise.resolve(); }, processError }); const msgsCheck = await checkWithTimeout( - () => receivedMsgs.length === 1 && receivedMsgs[0].delivery.remote_settled === true + () => + receivedMsgs.length === 1 && getDeliveryProperty(receivedMsgs[0]).remote_settled === true ); should.equal( msgsCheck, @@ -721,15 +718,13 @@ describe("Streaming with sessions", () => { await testPeekMsgsLength(receiverClient, 0); if (operation === DispositionType.complete) { - await contextToSettle!.complete(receivedMsgs[0]).catch((err) => testError(err, operation)); + await receivedMsgs[0].complete().catch((err) => testError(err, operation)); } else if (operation === DispositionType.abandon) { - await contextToSettle!.abandon(receivedMsgs[0]).catch((err) => testError(err, operation)); + await receivedMsgs[0].abandon().catch((err) => testError(err, operation)); } else if (operation === DispositionType.deadletter) { - await contextToSettle! - .deadLetter(receivedMsgs[0]) - .catch((err) => testError(err, operation)); + await receivedMsgs[0].deadLetter().catch((err) => testError(err, operation)); } else if (operation === DispositionType.defer) { - await contextToSettle!.defer(receivedMsgs[0]).catch((err) => testError(err, operation)); + await receivedMsgs[0].defer().catch((err) => testError(err, operation)); } should.equal(errorWasThrown, true, "Error thrown flag must be true"); @@ -802,10 +797,10 @@ describe("Streaming with sessions", () => { await senderClient.send(testMessage); const errorMessage = "Will we see this error message?"; - const receivedMsgs: ReceivedMessage[] = []; + const receivedMsgs: ReceivedMessageWithLock[] = []; receiverClient.subscribe({ - async processMessage(msg: ReceivedMessage, context: ContextWithSettlement) { - await context.complete(msg); + async processMessage(msg: ReceivedMessageWithLock) { + await msg.complete(); receivedMsgs.push(msg); throw new Error(errorMessage); }, @@ -851,12 +846,12 @@ describe("Streaming with sessions", () => { const testMessages = [TestMessage.getSessionSample(), TestMessage.getSessionSample()]; await senderClient.sendBatch(testMessages); - const settledMsgs: ReceivedMessage[] = []; - const receivedMsgs: ReceivedMessage[] = []; + const settledMsgs: ReceivedMessageWithLock[] = []; + const receivedMsgs: ReceivedMessageWithLock[] = []; receiverClient.subscribe( { - async processMessage(msg: ReceivedMessage, context: ContextWithSettlement) { + async processMessage(msg: ReceivedMessageWithLock) { if (receivedMsgs.length === 1) { if ((!maxConcurrentCalls || maxConcurrentCalls === 1) && settledMsgs.length === 0) { throw new Error( @@ -873,7 +868,7 @@ describe("Streaming with sessions", () => { receivedMsgs.push(msg); await delay(2000); - await context.complete(msg); + await msg.complete(); settledMsgs.push(msg); }, processError @@ -1001,7 +996,7 @@ describe("Streaming with sessions", () => { // receiverClient.subscribe( // { - // async processMessage(brokeredMessage: ReceivedMessage, context: ContextWithSettlement) { + // async processMessage(brokeredMessage: ReceivedMessage, context: ReceivedSettleableMessage) { // receivedMsgs.push(brokeredMessage); // await context.complete(brokeredMessage); // }, diff --git a/sdk/servicebus/service-bus/test/topicFilters.spec.ts b/sdk/servicebus/service-bus/test/topicFilters.spec.ts index 7c0c2e224ad6..e7c45b0dcde8 100644 --- a/sdk/servicebus/service-bus/test/topicFilters.spec.ts +++ b/sdk/servicebus/service-bus/test/topicFilters.spec.ts @@ -5,12 +5,7 @@ import chai from "chai"; const should = chai.should(); import chaiAsPromised from "chai-as-promised"; chai.use(chaiAsPromised); -import { - ServiceBusMessage, - SendableMessageInfo, - CorrelationFilter, - ContextWithSettlement -} from "../src"; +import { ServiceBusMessage, CorrelationFilter } from "../src"; import { TestClientType, checkWithTimeout } from "./utils/testUtils"; import { Receiver, SubscriptionRuleManagement } from "../src/receivers/receiver"; @@ -20,9 +15,10 @@ import { createServiceBusClientForTests, testPeekMsgsLength } from "./utils/testutils2"; +import { ReceivedMessageWithLock } from "../src/serviceBusMessage"; describe("topic filters", () => { - let subscriptionClient: Receiver & SubscriptionRuleManagement; + let subscriptionClient: Receiver & SubscriptionRuleManagement; let topicClient: Sender; let serviceBusClient: ServiceBusClientForTests; @@ -97,7 +93,7 @@ describe("topic filters", () => { async function sendOrders(): Promise { for (let index = 0; index < data.length; index++) { const element = data[index]; - const message: SendableMessageInfo = { + const message: ServiceBusMessage = { body: "", messageId: `messageId: ${Math.random()}`, correlationId: `${element.Priority}`, @@ -114,13 +110,13 @@ describe("topic filters", () => { } async function receiveOrders( - client: Receiver & SubscriptionRuleManagement, + client: Receiver & SubscriptionRuleManagement, expectedMessageCount: number - ): Promise { + ): Promise { let errorFromErrorHandler: Error | undefined; - const receivedMsgs: ServiceBusMessage[] = []; + const receivedMsgs: ReceivedMessageWithLock[] = []; client.subscribe({ - async processMessage(msg: ServiceBusMessage) { + async processMessage(msg: ReceivedMessageWithLock) { await msg.complete(); receivedMsgs.push(msg); }, @@ -397,7 +393,7 @@ describe("topic filters", () => { async function addFilterAndReceiveOrders( bool: boolean, - client: Receiver & SubscriptionRuleManagement, + client: Receiver & SubscriptionRuleManagement, expectedMessageCount: number ): Promise { await subscriptionClient.addRule("BooleanFilter", bool); diff --git a/sdk/servicebus/service-bus/test/track2.samples.spec.ts b/sdk/servicebus/service-bus/test/track2.samples.spec.ts index fdb16fe7b066..84e7c8a25d29 100644 --- a/sdk/servicebus/service-bus/test/track2.samples.spec.ts +++ b/sdk/servicebus/service-bus/test/track2.samples.spec.ts @@ -1,14 +1,14 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -import { ReceivedMessage, ContextWithSettlement } from "../src/models"; -import { delay, SendableMessageInfo } from "../src"; +import { delay, ServiceBusMessage, ReceivedMessage } from "../src"; import { TestClientType } from "./utils/testUtils"; import chai from "chai"; import chaiAsPromised from "chai-as-promised"; import { getEntityNameFromConnectionString } from "../src/constructorHelpers"; import { createServiceBusClientForTests, ServiceBusClientForTests } from "./utils/testutils2"; import { Sender } from "../src/sender"; +import { ReceivedMessageWithLock } from "../src/serviceBusMessage"; chai.use(chaiAsPromised); const assert = chai.assert; @@ -53,11 +53,8 @@ describe("Sample scenarios for track 2 #RunInBrowser", () => { const receivedBodies: string[] = []; receiver.subscribe({ - async processMessage( - message: ReceivedMessage, - context: ContextWithSettlement - ): Promise { - await context.complete(message); + async processMessage(message: ReceivedMessageWithLock): Promise { + await message.complete(); receivedBodies.push(message.body); }, async processError(err: Error): Promise { @@ -77,7 +74,7 @@ describe("Sample scenarios for track 2 #RunInBrowser", () => { const receivedBodies: string[] = []; - for (const message of (await receiver.receiveBatch(1, 5)).messages) { + for (const message of await receiver.receiveBatch(1, { maxWaitTimeSeconds: 5 })) { receivedBodies.push(message.body); } @@ -98,7 +95,7 @@ describe("Sample scenarios for track 2 #RunInBrowser", () => { const receivedBodies: string[] = []; // TODO: error handling? Does the iterate just terminate? - for await (const { message, context } of receiver.getMessageIterator()) { + for await (const message of receiver.getMessageIterator()) { if (message == null) { // user has the option of handling "no messages arrived by the maximum wait time" console.log(`No message arrived within our max wait time`); @@ -106,11 +103,11 @@ describe("Sample scenarios for track 2 #RunInBrowser", () => { } try { - await context.complete(message); + await message.complete(); receivedBodies.push(message.body); break; } catch (err) { - await context.abandon(message); + await message.abandon(); throw err; } } @@ -153,9 +150,10 @@ describe("Sample scenarios for track 2 #RunInBrowser", () => { const receivedBodies: string[] = []; // TODO: error handling? Does the iterate just terminate? - for await (const { message, context } of receiver.getMessageIterator()) { - assert.notOk((context as any).complete); - + for await (const message of receiver.getMessageIterator()) { + // TODO: temporary - ultimately this method should throw an error if they manage + // to call it on a receiveAndDelete receiver. + // message.complete() if (message == null) { // user has the option of handling "no messages arrived by the maximum wait time" console.log(`No message arrived within our max wait time`); @@ -214,11 +212,8 @@ describe("Sample scenarios for track 2 #RunInBrowser", () => { const receivedBodies: string[] = []; receiver.subscribe({ - async processMessage( - message: ReceivedMessage, - context: ContextWithSettlement - ): Promise { - await context.complete(message); + async processMessage(message: ReceivedMessageWithLock): Promise { + await message.complete(); receivedBodies.push(message.body); }, async processError(err: Error): Promise { @@ -266,7 +261,7 @@ describe("Sample scenarios for track 2 #RunInBrowser", () => { const receivedBodies: string[] = []; // TODO: error handling? Does the iterate just terminate? - for await (const { message, context } of receiver.getMessageIterator()) { + for await (const message of receiver.getMessageIterator()) { if (message == null) { // user has the option of handling "no messages arrived by the maximum wait time" console.log(`No message arrived within our max wait time`); @@ -274,11 +269,11 @@ describe("Sample scenarios for track 2 #RunInBrowser", () => { } try { - await context.complete(message); + await message.complete(); receivedBodies.push(message.body); break; } catch (err) { - await context.abandon(message); + await message.abandon(); throw err; } } @@ -304,9 +299,7 @@ describe("Sample scenarios for track 2 #RunInBrowser", () => { const receivedBodies: string[] = []; // TODO: error handling? Does the iterate just terminate? - for await (const { message, context } of receiver.getMessageIterator()) { - assert.notOk((context as any).complete); - + for await (const message of receiver.getMessageIterator()) { if (message == null) { // user has the option of handling "no messages arrived by the maximum wait time" console.log(`No message arrived within our max wait time`); @@ -374,7 +367,7 @@ describe("Sample scenarios for track 2 #RunInBrowser", () => { ); }); - it("Queue, peek/lock, sessions", async () => { + it("Queue, peek/lock, sessions using an iterator", async () => { const sessionId = Date.now().toString(); const receiver = serviceBusClient.test.addToCleanup( @@ -390,21 +383,18 @@ describe("Sample scenarios for track 2 #RunInBrowser", () => { const errors: string[] = []; const receivedBodies: string[] = []; - receiver.subscribe({ - async processMessage(message: ReceivedMessage): Promise { - receivedBodies.push(message.body); - }, - async processError(err: Error): Promise { - errors.push(err.message); - } - }); + for await (const message of receiver.getMessageIterator()) { + receivedBodies.push(message.body); + await message.complete(); + break; + } await waitAndValidate("Queue, peek/lock, sessions", receivedBodies, errors, receiver); }); }); async function sendSampleMessage(senderClient: Sender, body: string, sessionId?: string) { - const message: SendableMessageInfo = { + const message: ServiceBusMessage = { body }; diff --git a/sdk/servicebus/service-bus/test/utils/misc.ts b/sdk/servicebus/service-bus/test/utils/misc.ts new file mode 100644 index 000000000000..b84d69a8cbc6 --- /dev/null +++ b/sdk/servicebus/service-bus/test/utils/misc.ts @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +import { ReceivedMessage, Delivery } from "../../src"; +import { ServiceBusMessageImpl } from "../../src/serviceBusMessage"; + +// some functions useful as we transition between interfaces and classes. + +export function getDeliveryProperty(message: ReceivedMessage): Delivery { + if ( + message && + (message as ServiceBusMessageImpl).delivery && + typeof (message as ServiceBusMessageImpl).delivery === "object" + ) { + return (message as ServiceBusMessageImpl).delivery; + } + + throw new Error( + "Received message does not contain a .delivery member - not a ServiceBusMessageImpl instance." + ); +} diff --git a/sdk/servicebus/service-bus/test/utils/testUtils.ts b/sdk/servicebus/service-bus/test/utils/testUtils.ts index 7ffe85df2649..820b07ddc53c 100644 --- a/sdk/servicebus/service-bus/test/utils/testUtils.ts +++ b/sdk/servicebus/service-bus/test/utils/testUtils.ts @@ -2,14 +2,14 @@ // Licensed under the MIT License. import chai from "chai"; -import { SendableMessageInfo, delay, MessagingError, ReceivedMessage } from "../../src"; +import { ServiceBusMessage, delay, MessagingError, ReceivedMessage } from "../../src"; import * as dotenv from "dotenv"; dotenv.config(); export class TestMessage { static sessionId: string = "my-session"; - static getSample(): SendableMessageInfo { + static getSample(): ServiceBusMessage { const randomNumber = Math.random(); return { body: `message body ${randomNumber}`, @@ -30,7 +30,7 @@ export class TestMessage { }; } - static getSessionSample(): SendableMessageInfo { + static getSessionSample(): ServiceBusMessage { const randomNumber = Math.random(); return { body: `message body ${randomNumber}`, @@ -58,7 +58,7 @@ export class TestMessage { * on the received message */ static checkMessageContents( - sent: SendableMessageInfo, + sent: ServiceBusMessage, received: ReceivedMessage, useSessions?: boolean, usePartitions?: boolean diff --git a/sdk/servicebus/service-bus/test/utils/testutils2.ts b/sdk/servicebus/service-bus/test/utils/testutils2.ts index ae9efef1f534..b3215a4e1024 100644 --- a/sdk/servicebus/service-bus/test/utils/testutils2.ts +++ b/sdk/servicebus/service-bus/test/utils/testutils2.ts @@ -15,8 +15,9 @@ import { TestClientType, TestMessage } from "./testUtils"; import { getEnvVars, EnvVarNames } from "./envVarUtils"; import * as dotenv from "dotenv"; import { recreateQueue, recreateTopic, recreateSubscription } from "./managementUtils"; -import { ServiceBusClientOptions, ContextWithSettlement } from "../../src"; +import { ServiceBusClientOptions } from "../../src"; import chai from "chai"; +import { ReceivedMessageWithLock, ReceivedMessage } from "../../src/serviceBusMessage"; dotenv.config(); const env = getEnvVars(); @@ -113,9 +114,9 @@ async function createTestEntities( export async function drainAllMessages(receiver: Receiver<{}>): Promise { while (true) { - const messages = await receiver.receiveBatch(10, 1); + const messages = await receiver.receiveBatch(10, { maxWaitTimeSeconds: 1 }); - if (messages.messages.length === 0) { + if (messages.length === 0) { break; } } @@ -185,7 +186,7 @@ export class ServiceBusTestHelpers { */ getPeekLockReceiver( entityNames: ReturnType - ): Receiver { + ): Receiver { try { // if you're creating a receiver this way then you'll just use the default // session ID for your receiver. @@ -213,7 +214,7 @@ export class ServiceBusTestHelpers { getSubscriptionPeekLockReceiver( entityNames: ReturnType - ): Receiver & SubscriptionRuleManagement { + ): Receiver & SubscriptionRuleManagement { if (entityNames.topic == null || entityNames.subscription == null) { throw new TypeError("Not subscription entity - can't create a subscription receiver for it"); } @@ -226,7 +227,7 @@ export class ServiceBusTestHelpers { getSessionPeekLockReceiver( entityNames: ReturnType, getSessionReceiverOptions?: GetSessionReceiverOptions - ): SessionReceiver { + ): SessionReceiver { if (!entityNames.usesSessions) { throw new TypeError( "Not a session-full entity - can't create a session receiver type for it" @@ -255,7 +256,9 @@ export class ServiceBusTestHelpers { * * The receiver created by this method will be cleaned up by `afterEach()` */ - getReceiveAndDeleteReceiver(entityNames: ReturnType): Receiver<{}> { + getReceiveAndDeleteReceiver( + entityNames: ReturnType + ): Receiver { // TODO: we should generate a random ID here - there's no harm in // creating as many sessions as we wish. Some tests will need to change. const sessionId = TestMessage.sessionId; @@ -296,7 +299,7 @@ async function purgeForTestClientType( serviceBusClient: ServiceBusClient, testClientType: TestClientType ): Promise { - let receiver: Receiver<{}> | SessionReceiver<{}> | undefined; + let receiver: Receiver | SessionReceiver | undefined; let entityPaths = getEntityNames(testClientType); if (entityPaths.queue) { @@ -336,9 +339,9 @@ export function createServiceBusClientForTests( export async function drainReceiveAndDeleteReceiver(receiver: Receiver<{}>): Promise { try { while (true) { - const messages = await receiver.receiveBatch(10, 1); + const messages = await receiver.receiveBatch(10, { maxWaitTimeSeconds: 1000 }); - if (messages.messages.length === 0) { + if (messages.length === 0) { break; } } From 565c7a00787817a4031c89b8c0ae8e1c31d11d88 Mon Sep 17 00:00:00 2001 From: Will Temple Date: Tue, 17 Mar 2020 16:46:52 -0700 Subject: [PATCH 19/28] [service-bus] Remove samples frontmatter and reference old version in README (#7824) * [service-bus] Remove samples frontmatter * [service-bus] add links to older samples/npm packages for version 2 preview --- sdk/servicebus/service-bus/README.md | 6 ++++++ .../service-bus/samples/javascript/README.md | 10 ---------- .../service-bus/samples/typescript/README.md | 10 ---------- 3 files changed, 6 insertions(+), 20 deletions(-) diff --git a/sdk/servicebus/service-bus/README.md b/sdk/servicebus/service-bus/README.md index 83fc52f04837..87433599a574 100644 --- a/sdk/servicebus/service-bus/README.md +++ b/sdk/servicebus/service-bus/README.md @@ -9,6 +9,12 @@ Use the client library for Azure Service Bus in your Node.js application to [Source code](https://github.com/Azure/azure-sdk-for-js/tree/master/sdk/servicebus/service-bus) | [Package (npm)](https://www.npmjs.com/package/@azure/service-bus) | [API Reference Documentation](https://docs.microsoft.com/en-us/javascript/api/%40azure/service-bus/) | [Product documentation](https://azure.microsoft.com/en-us/services/service-bus/) | [Samples](https://github.com/Azure/azure-sdk-for-js/tree/master/sdk/servicebus/service-bus/samples) +**NOTE**: If you are using version 1.1.x or lower, then please use the below links instead + +[Source code for v1.1.3](https://github.com/Azure/azure-sdk-for-js/tree/%40azure/service-bus_1.1.3/sdk/servicebus/service-bus) | +[Package for v1.1.3 (npm)](https://www.npmjs.com/package/@azure/service-bus/v/1.1.3) | +[Samples for v1.1.3](https://github.com/Azure/azure-sdk-for-js/tree/%40azure/service-bus_1.1.3/sdk/servicebus/service-bus/samples) + ## Getting Started ### Install the package diff --git a/sdk/servicebus/service-bus/samples/javascript/README.md b/sdk/servicebus/service-bus/samples/javascript/README.md index 28ebd51a61a5..5d1b3008a8d4 100644 --- a/sdk/servicebus/service-bus/samples/javascript/README.md +++ b/sdk/servicebus/service-bus/samples/javascript/README.md @@ -1,13 +1,3 @@ ---- -page_type: sample -languages: - - javascript -products: - - azure - - azure-service-bus -urlFragment: service-bus-javascript ---- - # Azure Service Bus client library samples for JavaScript These sample programs show how to use the JavaScript client libraries for Azure Service Bus in some common scenarios. diff --git a/sdk/servicebus/service-bus/samples/typescript/README.md b/sdk/servicebus/service-bus/samples/typescript/README.md index cded0576815d..38a4ab2abf33 100644 --- a/sdk/servicebus/service-bus/samples/typescript/README.md +++ b/sdk/servicebus/service-bus/samples/typescript/README.md @@ -1,13 +1,3 @@ ---- -page_type: sample -languages: - - typescript -products: - - azure - - azure-service-bus -urlFragment: service-bus-typescript ---- - # Azure Service Bus client library samples for TypeScript These sample programs show how to use the TypeScript client libraries for Azure Service Bus in some common scenarios. From edbb698239a17c6f6aca28ff408a194705dc6b86 Mon Sep 17 00:00:00 2001 From: Harsha Nalluru Date: Tue, 17 Mar 2020 18:03:05 -0700 Subject: [PATCH 20/28] [Service Bus] Update test scripts with `npm run clean` (#7862) --- sdk/servicebus/service-bus/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/servicebus/service-bus/package.json b/sdk/servicebus/service-bus/package.json index 3614f298e2b3..3443e02c01d5 100644 --- a/sdk/servicebus/service-bus/package.json +++ b/sdk/servicebus/service-bus/package.json @@ -58,8 +58,8 @@ "lint-fix": "eslint package.json tsconfig.json src test samples --ext .ts --fix --fix-type [problem,suggestion]", "pack": "npm pack 2>&1", "prebuild": "npm run clean", - "test:browser": "npm run build:test:browser && npm run integration-test:browser", - "test:node": "npm run build:test:node && npm run integration-test:node", + "test:browser": "npm run clean && npm run build:test:browser && npm run integration-test:browser", + "test:node": "npm run clean && npm run build:test:node && npm run integration-test:node", "test": "npm run test:node && npm run test:browser", "unit-test:browser": "echo skipped", "unit-test:node": "echo skipped", From 0d50e9b6a5c46ce09b4577756cb95b9adf4c3b80 Mon Sep 17 00:00:00 2001 From: Harsha Nalluru Date: Tue, 17 Mar 2020 19:10:20 -0700 Subject: [PATCH 21/28] [Service Bus] Merge 'hotfix/service-bus-1.1.4-increase-timeout' into master (#7865) * Pinning to Typedoc 0.16.x until further investigation (#7850) * [Service Bus] [Track 1] Update service-bus with the amqp-common latest (#7823) * update service-bus with the timeout change * Add link to the PR * Add new line * Update sdk/servicebus/service-bus/CHANGELOG.md * Update sdk/servicebus/service-bus/CHANGELOG.md * update pnpm-lock file * Update sdk/servicebus/service-bus/CHANGELOG.md Co-Authored-By: chradek <51000525+chradek@users.noreply.github.com> * Update sdk/servicebus/service-bus/CHANGELOG.md Co-Authored-By: Ramya Rao * update readme * Update sdk/servicebus/service-bus/CHANGELOG.md * Trigger pipeline(dummy commit) * Update package version in constants.ts Co-authored-by: HarshaNalluru <10452642+HarshaNalluru@users.noreply.github.com> Co-authored-by: chradek <51000525+chradek@users.noreply.github.com> Co-authored-by: Ramya Rao Co-authored-by: HarshaNalluru <10452642+HarshaNalluru@users.noreply.github.com> Co-authored-by: chradek <51000525+chradek@users.noreply.github.com> Co-authored-by: Ramya Rao --- sdk/servicebus/service-bus/CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sdk/servicebus/service-bus/CHANGELOG.md b/sdk/servicebus/service-bus/CHANGELOG.md index e3f01f9b2f57..bda136ff8163 100644 --- a/sdk/servicebus/service-bus/CHANGELOG.md +++ b/sdk/servicebus/service-bus/CHANGELOG.md @@ -7,6 +7,11 @@ - Fixes [bug 6816](https://github.com/Azure/azure-sdk-for-js/issues/6816) affecting messages sent using the `scheduleMessage()` and `scheduleMessages()` methods. [PR 7372](https://github.com/Azure/azure-sdk-for-js/pull/7372). - Users on version-`1.x.x` of `@azure/service-bus` library had to rely on the [workaround of encoding the message body with `DefaultDataTransformer`](https://github.com/Azure/azure-sdk-for-js/pull/6983) before calling `scheduleMessage()`/`scheduleMessages()` methods. The workaround is no longer needed since the bug has been fixed here starting from version-`2.0.0-preview.1`. [PR 7372](https://github.com/Azure/azure-sdk-for-js/pull/7372). +## 1.1.4 (2020-03-17) + +- Updated to use the latest version of `@azure/amqp-common` where the timeout for authorization requests sent to the service is increased from 10s to 60s to reduce the frequency of timeout errors. + [PR 7823](https://github.com/Azure/azure-sdk-for-js/issues/7823). + ## 1.1.3 (2020-02-11) - Fixes issue where the promise returned by `receiveMessages` would sometimes fail to settle when the underlying connection From a8002ae32fe4239fb35a4fa45add095077e04ce6 Mon Sep 17 00:00:00 2001 From: Daniel Jurek Date: Tue, 17 Mar 2020 20:50:22 -0700 Subject: [PATCH 22/28] add instructions for running live tests to CONTRIBUTING.md (#7804) --- CONTRIBUTING.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index fef9e167385a..802135f11383 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -159,6 +159,33 @@ Projects may optionally have the following scripts: - `extract-api`: Run API Extractor to show API issues and generate API reports +### Integration Testing with live services + +Integration tests assume a live resource has been created and appropriate +environment variables have been set for the test process. To automate setting up +live resources we use created a script called `New-TestResources.ps1` that +deploys resources for a given service. + +To see what resources will be deployed for a live service, check the +`test-resources.json` ARM template files in the service you wish to deploy for +testing, for example `sdk\keyvault\test-resources.json`. + +To deploy live resources for testing use the steps documented in [`Example 1 of New-TestResources.ps1`](eng/common/TestResources/New-TestResources.ps1.md#example-1) +to set up a service principal and deploy live testing resources. + +The script will provide instructions for setting environment variables before +running live tests. + +To run live tests after deploying live resources for Node: + +``` +rush integration-test:node -t @azure/keyvault-secrets -verbose -pmax +``` + +Some live tests may have additional steps for setting up live testing resources. +See the CONTRIBUTING.md file for the service you wish to test for additional +information or instructions. + ### Getting back to a clean state If you're having problems and want to restore your repo to a clean state without any packages installed, run `rush uninstall`. Downloaded packages will be deleted from the cache and all node_modules directories will be removed. Now you can start clean by re-downloading and installing dependencies from scratch with `rush update`. This will not make any changes to any other files in your working directory. From 8329a7cde7dcd95bc66299c97b06a088f94774a4 Mon Sep 17 00:00:00 2001 From: Ramya Rao Date: Wed, 18 Mar 2020 10:50:46 -0700 Subject: [PATCH 23/28] [Service Bus] Remove lingering artifacts for management apis (#7864) --- .../service-bus/review/service-bus.api.md | 201 ------------------ sdk/servicebus/service-bus/src/index.ts | 18 -- .../service-bus/test/atomManagement.spec.ts | 6 +- .../service-bus/test/utils/managementUtils.ts | 5 +- 4 files changed, 9 insertions(+), 221 deletions(-) diff --git a/sdk/servicebus/service-bus/review/service-bus.api.md b/sdk/servicebus/service-bus/review/service-bus.api.md index 315934d3e8b3..5722c7074838 100644 --- a/sdk/servicebus/service-bus/review/service-bus.api.md +++ b/sdk/servicebus/service-bus/review/service-bus.api.md @@ -8,7 +8,6 @@ import { AmqpMessage } from '@azure/core-amqp'; import { DataTransformer } from '@azure/core-amqp'; import { delay } from '@azure/core-amqp'; import { Delivery } from 'rhea-promise'; -import { HttpOperationResponse } from '@azure/core-http'; import Long from 'long'; import { MessagingError } from '@azure/core-amqp'; import { OperationOptions } from '@azure/core-auth'; @@ -18,18 +17,6 @@ import { TokenType } from '@azure/core-amqp'; import { WebSocketImpl } from 'rhea-promise'; import { WebSocketOptions } from '@azure/core-amqp'; -// @public -export type AuthorizationRule = { - claimType: string; - claimValue: string; - rights: { - accessRights?: string[]; - }; - keyName: string; - primaryKey?: string; - secondaryKey?: string; -}; - // @public export interface CorrelationFilter { contentType?: string; @@ -55,9 +42,6 @@ export { delay } export { Delivery } -// @public -export type EntityStatus = "Active" | "Creating" | "Deleting" | "ReceiveDisabled" | "SendDisabled" | "Disabled" | "Renaming" | "Restoring" | "Unknown"; - // @public export interface GetMessageIteratorOptions extends OperationOptions, WaitTimeOptions { } @@ -68,17 +52,6 @@ export interface GetSessionReceiverOptions extends OperationOptions { sessionId?: string; } -export { HttpOperationResponse } - -// @public -export type MessageCountDetails = { - activeMessageCount: number; - deadLetterMessageCount: number; - scheduledMessageCount: number; - transferMessageCount: number; - transferDeadLetterMessageCount: number; -}; - // @public export interface MessageHandlerOptions { autoComplete?: boolean; @@ -94,57 +67,6 @@ export interface MessageHandlers { export { MessagingError } -// @public -export interface QueueDetails { - accessedOn?: string; - authorizationRules?: AuthorizationRule[]; - autoDeleteOnIdle: string; - createdOn?: string; - deadLetteringOnMessageExpiration: boolean; - defaultMessageTtl: string; - duplicateDetectionHistoryTimeWindow: string; - enableBatchedOperations: boolean; - enableExpress?: boolean; - enablePartitioning: boolean; - entityAvailabilityStatus?: string; - forwardDeadLetteredMessagesTo?: string; - forwardTo?: string; - isAnonymousAccessible?: boolean; - lockDuration: string; - maxDeliveryCount: number; - maxSizeInMegabytes: number; - messageCount?: number; - messageCountDetails?: MessageCountDetails; - queueName: string; - requiresDuplicateDetection: boolean; - requiresSession: boolean; - sizeInBytes?: number; - status?: EntityStatus; - supportOrdering?: boolean; - updatedOn?: string; - userMetadata?: string; -} - -// @public -export interface QueueOptions { - authorizationRules?: AuthorizationRule[]; - autoDeleteOnIdle?: string; - deadLetteringOnMessageExpiration?: boolean; - defaultMessageTtl?: string; - duplicateDetectionHistoryTimeWindow?: string; - enableBatchedOperations?: boolean; - enablePartitioning?: boolean; - forwardDeadLetteredMessagesTo?: string; - forwardTo?: string; - lockDuration?: string; - maxDeliveryCount?: number; - maxSizeInMegabytes?: number; - requiresDuplicateDetection?: boolean; - requiresSession?: boolean; - status?: EntityStatus; - userMetadata?: string; -} - // @public export interface ReceiveBatchOptions extends OperationOptions, WaitTimeOptions { } @@ -209,22 +131,6 @@ export interface RuleDescription { name: string; } -// @public -export interface RuleDetails { - action?: SqlAction; - createdOn: string; - filter?: SqlFilter | CorrelationFilter; - ruleName: string; - subscriptionName: string; - topicName: string; -} - -// @public -export interface RuleOptions { - action?: SqlAction; - filter?: SqlFilter | CorrelationFilter; -} - // @public export interface Sender { cancelScheduledMessage(sequenceNumber: Long): Promise; @@ -304,72 +210,10 @@ export interface SessionReceiverOptions { sessionId: string | undefined; } -// @public -export type SqlAction = SqlFilter; - -// @public -export interface SqlFilter { - compatibilityLevel?: number; - requiresPreprocessing?: boolean; - sqlExpression?: string; - sqlParameters?: SqlParameter[]; -} - -// @public -export type SqlParameter = { - key: string; - value: string | number; - type: string; -}; - // @public export interface SubscribeOptions extends OperationOptions, MessageHandlerOptions { } -// @public -export interface SubscriptionDetails { - accessedOn?: string; - autoDeleteOnIdle: string; - createdOn: string; - deadLetteringOnFilterEvaluationExceptions: boolean; - deadLetteringOnMessageExpiration: boolean; - defaultMessageTtl?: string; - defaultRuleDescription?: any; - enableBatchedOperations: boolean; - enablePartitioning?: boolean; - entityAvailabilityStatus: string; - forwardDeadLetteredMessagesTo?: string; - forwardTo?: string; - lockDuration: string; - maxDeliveryCount: number; - maxSizeInMegabytes?: number; - messageCount: number; - messageCountDetails?: MessageCountDetails; - requiresSession: boolean; - sizeInBytes?: number; - status?: EntityStatus; - subscriptionName: string; - topicName: string; - updatedOn: string; - userMetadata?: string; -} - -// @public -export interface SubscriptionOptions { - autoDeleteOnIdle?: string; - deadLetteringOnFilterEvaluationExceptions?: boolean; - deadLetteringOnMessageExpiration?: boolean; - defaultMessageTtl?: string; - enableBatchedOperations?: boolean; - forwardDeadLetteredMessagesTo?: string; - forwardTo?: string; - lockDuration?: string; - maxDeliveryCount?: number; - requiresSession?: boolean; - status?: EntityStatus; - userMetadata?: string; -} - // @public export interface SubscriptionRuleManagement { addRule(ruleName: string, filter: boolean | string | CorrelationFilter, sqlRuleActionExpression?: string): Promise; @@ -382,51 +226,6 @@ export { TokenCredential } export { TokenType } -// @public -export interface TopicDetails { - accessedOn?: string; - authorizationRules?: AuthorizationRule[]; - autoDeleteOnIdle?: string; - createdOn?: string; - defaultMessageTtl: string; - duplicateDetectionHistoryTimeWindow: string; - enableBatchedOperations: boolean; - enableExpress?: boolean; - enablePartitioning: boolean; - enableSubscriptionPartitioning?: boolean; - entityAvailabilityStatus?: string; - filteringMessagesBeforePublishing?: boolean; - isAnonymousAccessible?: boolean; - isExpress?: boolean; - maxDeliveryCount?: number; - maxSizeInMegabytes: number; - messageCount?: number; - messageCountDetails?: MessageCountDetails; - requiresDuplicateDetection: boolean; - sizeInBytes?: number; - status?: EntityStatus; - subscriptionCount?: number; - supportOrdering: boolean; - topicName: string; - updatedOn?: string; - userMetadata?: string; -} - -// @public -export interface TopicOptions { - authorizationRules?: AuthorizationRule[]; - autoDeleteOnIdle?: string; - defaultMessageTtl?: string; - duplicateDetectionHistoryTimeWindow?: string; - enableBatchedOperations?: boolean; - enablePartitioning?: boolean; - maxSizeInMegabytes?: number; - requiresDuplicateDetection?: boolean; - status?: EntityStatus; - supportOrdering?: boolean; - userMetadata?: string; -} - // @public export interface WaitTimeOptions { maxWaitTimeSeconds: number; diff --git a/sdk/servicebus/service-bus/src/index.ts b/sdk/servicebus/service-bus/src/index.ts index 575858024c79..ab812834118d 100644 --- a/sdk/servicebus/service-bus/src/index.ts +++ b/sdk/servicebus/service-bus/src/index.ts @@ -28,24 +28,6 @@ export { } from "./serviceBusMessage"; export { Delivery, WebSocketImpl } from "rhea-promise"; -export { HttpOperationResponse } from "@azure/core-http"; - -export { QueueDetails, QueueOptions } from "./serializers/queueResourceSerializer"; -export { TopicDetails, TopicOptions } from "./serializers/topicResourceSerializer"; -export { - SubscriptionDetails, - SubscriptionOptions -} from "./serializers/subscriptionResourceSerializer"; -export { - RuleDetails, - RuleOptions, - SqlFilter, - SqlParameter, - SqlAction -} from "./serializers/ruleResourceSerializer"; - -export { MessageCountDetails, AuthorizationRule, EntityStatus } from "./util/utils"; - export { GetMessageIteratorOptions, GetSessionReceiverOptions, diff --git a/sdk/servicebus/service-bus/test/atomManagement.spec.ts b/sdk/servicebus/service-bus/test/atomManagement.spec.ts index 076be79732c7..0bbef325b6ce 100644 --- a/sdk/servicebus/service-bus/test/atomManagement.spec.ts +++ b/sdk/servicebus/service-bus/test/atomManagement.spec.ts @@ -1,7 +1,11 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -import { QueueOptions, TopicOptions, SubscriptionOptions, RuleOptions, EntityStatus } from "../src"; +import { QueueOptions } from "../src/serializers/queueResourceSerializer"; +import { TopicOptions } from "../src/serializers/topicResourceSerializer"; +import { SubscriptionOptions } from "../src/serializers/subscriptionResourceSerializer"; +import { RuleOptions } from "../src/serializers/ruleResourceSerializer"; +import { EntityStatus } from "../src/util/utils"; import { ServiceBusAtomManagementClient } from "../src/serviceBusAtomManagementClient"; import chai from "chai"; diff --git a/sdk/servicebus/service-bus/test/utils/managementUtils.ts b/sdk/servicebus/service-bus/test/utils/managementUtils.ts index 717a1ce2c37b..d24cb2e3397d 100644 --- a/sdk/servicebus/service-bus/test/utils/managementUtils.ts +++ b/sdk/servicebus/service-bus/test/utils/managementUtils.ts @@ -1,7 +1,10 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -import { QueueOptions, TopicOptions, SubscriptionOptions, delay } from "../../src"; +import { delay } from "../../src"; +import { QueueOptions } from "../../src/serializers/queueResourceSerializer"; +import { TopicOptions } from "../../src/serializers/topicResourceSerializer"; +import { SubscriptionOptions } from "../../src/serializers/subscriptionResourceSerializer"; import { ServiceBusAtomManagementClient } from "../../src/serviceBusAtomManagementClient"; import { EnvVarNames, getEnvVars } from "./envVarUtils"; From b5aaa756447689ff3b32ecd86743ac1f1672c508 Mon Sep 17 00:00:00 2001 From: Azure SDK Bot <53356347+azure-sdk@users.noreply.github.com> Date: Wed, 18 Mar 2020 17:27:52 -0700 Subject: [PATCH 24/28] Sync eng/common directory with azure-sdk-tools repository (#7876) --- .../TestResources/New-TestResources.ps1 | 22 ++++++++++++-- .../TestResources/New-TestResources.ps1.md | 25 +++++++++++++--- .../TestResources/Remove-TestResources.ps1 | 10 ++++++- .../TestResources/Remove-TestResources.ps1.md | 29 ++++++++++++++++--- .../TestResources/deploy-test-resources.yml | 10 ++++++- .../TestResources/remove-test-resources.yml | 5 +++- 6 files changed, 87 insertions(+), 14 deletions(-) diff --git a/eng/common/TestResources/New-TestResources.ps1 b/eng/common/TestResources/New-TestResources.ps1 index 90b4faa553dc..19b38c42e21a 100644 --- a/eng/common/TestResources/New-TestResources.ps1 +++ b/eng/common/TestResources/New-TestResources.ps1 @@ -48,6 +48,10 @@ param ( [ValidateNotNullOrEmpty()] [string] $Location = 'westus2', + [Parameter()] + [ValidateNotNullOrEmpty()] + [string] $Environment = 'AzureCloud', + [Parameter()] [ValidateNotNullOrEmpty()] [hashtable] $AdditionalParameters, @@ -128,7 +132,7 @@ if ($ProvisionerApplicationId) { $provisionerCredential = [System.Management.Automation.PSCredential]::new($ProvisionerApplicationId, $provisionerSecret) $provisionerAccount = Retry { - Connect-AzAccount -Tenant $TenantId -Credential $provisionerCredential -ServicePrincipal + Connect-AzAccount -Tenant $TenantId -Credential $provisionerCredential -ServicePrincipal -Environment $Environment } $exitActions += { @@ -153,7 +157,15 @@ $resourceGroupName = if ($CI) { $BaseName = 't' + (New-Guid).ToString('n').Substring(0, 16) Write-Verbose "Generated base name '$BaseName' for CI build" - "rg-{0}-$BaseName" -f ($ServiceDirectory -replace '[\\\/]', '-').Substring(0, [Math]::Min($ServiceDirectory.Length, 90 - $BaseName.Length - 4)).Trim('-') + # If the ServiceDirectory is an absolute path use the last directory name + # (e.g. D:\foo\bar\ -> bar) + $serviceName = if (Split-Path -IsAbsolute $ServiceDirectory) { + Split-Path -Leaf $ServiceDirectory + } else { + $ServiceDirectory + } + + "rg-{0}-$BaseName" -f ($serviceName -replace '[\\\/:]', '-').Substring(0, [Math]::Min($serviceName.Length, 90 - $BaseName.Length - 4)).Trim('-') } else { "rg-$BaseName" } @@ -241,7 +253,7 @@ foreach ($templateFile in $templateFiles) { Log "Deploying template '$templateFile' to resource group '$($resourceGroup.ResourceGroupName)'" $deployment = Retry { - New-AzResourceGroupDeployment -Name $BaseName -ResourceGroupName $resourceGroup.ResourceGroupName -TemplateFile $templateFile -TemplateParameterObject $templateFileParameters + New-AzResourceGroupDeployment -Name $BaseName -ResourceGroupName $resourceGroup.ResourceGroupName -TemplateFile $templateFile -TemplateParameterObject $templateFileParameters -Location $Location } if ($deployment.ProvisioningState -eq 'Succeeded') { @@ -391,6 +403,10 @@ Optional location where resources should be created. By default this is .PARAMETER AdditionalParameters Optional key-value pairs of parameters to pass to the ARM template(s). +.PARAMETER Environment +Name of the cloud environment. The default is the Azure Public Cloud +('PublicCloud') + .PARAMETER CI Indicates the script is run as part of a Continuous Integration / Continuous Deployment (CI/CD) build (only Azure Pipelines is currently supported). diff --git a/eng/common/TestResources/New-TestResources.ps1.md b/eng/common/TestResources/New-TestResources.ps1.md index b6516d63cace..4c978512519b 100644 --- a/eng/common/TestResources/New-TestResources.ps1.md +++ b/eng/common/TestResources/New-TestResources.ps1.md @@ -16,8 +16,8 @@ Deploys live test resources defined for a service directory to Azure. ``` New-TestResources.ps1 [-BaseName] -ServiceDirectory -TestApplicationId [-TestApplicationSecret ] [-TestApplicationOid ] [-DeleteAfterHours ] - [-Location ] [-AdditionalParameters ] [-CI] [-Force] [-WhatIf] [-Confirm] - [] + [-Location ] [-Environment ] [-AdditionalParameters ] [-CI] [-Force] [-WhatIf] + [-Confirm] [] ``` ### Provisioner @@ -25,8 +25,8 @@ New-TestResources.ps1 [-BaseName] -ServiceDirectory -TestAppli New-TestResources.ps1 [-BaseName] -ServiceDirectory -TestApplicationId [-TestApplicationSecret ] [-TestApplicationOid ] -TenantId -ProvisionerApplicationId -ProvisionerApplicationSecret [-DeleteAfterHours ] - [-Location ] [-AdditionalParameters ] [-CI] [-Force] [-WhatIf] [-Confirm] - [] + [-Location ] [-Environment ] [-AdditionalParameters ] [-CI] [-Force] [-WhatIf] + [-Confirm] [] ``` ## DESCRIPTION @@ -298,6 +298,23 @@ Accept pipeline input: False Accept wildcard characters: False ``` +### -Environment +Name of the cloud environment. +The default is the Azure Public Cloud +('PublicCloud') + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: AzureCloud +Accept pipeline input: False +Accept wildcard characters: False +``` + ### -AdditionalParameters Optional key-value pairs of parameters to pass to the ARM template(s). diff --git a/eng/common/TestResources/Remove-TestResources.ps1 b/eng/common/TestResources/Remove-TestResources.ps1 index b0db06abd9ed..494fe1c873d1 100644 --- a/eng/common/TestResources/Remove-TestResources.ps1 +++ b/eng/common/TestResources/Remove-TestResources.ps1 @@ -34,6 +34,10 @@ param ( [Parameter(ParameterSetName = 'ResourceGroup+Provisioner', Mandatory = $true)] [string] $ProvisionerApplicationSecret, + [Parameter()] + [ValidateNotNullOrEmpty()] + [string] $Environment = 'AzureCloud', + [Parameter()] [switch] $Force ) @@ -87,7 +91,7 @@ if ($ProvisionerApplicationId) { $provisionerSecret = ConvertTo-SecureString -String $ProvisionerApplicationSecret -AsPlainText -Force $provisionerCredential = [System.Management.Automation.PSCredential]::new($ProvisionerApplicationId, $provisionerSecret) $provisionerAccount = Retry { - Connect-AzAccount -Tenant $TenantId -Credential $provisionerCredential -ServicePrincipal + Connect-AzAccount -Tenant $TenantId -Credential $provisionerCredential -ServicePrincipal -Environment $Environment } $exitActions += { @@ -138,6 +142,10 @@ A service principal ID to provision test resources when a provisioner is specifi .PARAMETER ProvisionerApplicationSecret A service principal secret (password) to provision test resources when a provisioner is specified. +.PARAMETER Environment +Name of the cloud environment. The default is the Azure Public Cloud +('PublicCloud') + .PARAMETER Force Force removal of resource group without asking for user confirmation diff --git a/eng/common/TestResources/Remove-TestResources.ps1.md b/eng/common/TestResources/Remove-TestResources.ps1.md index 8dc48c85df28..03803d8e591c 100644 --- a/eng/common/TestResources/Remove-TestResources.ps1.md +++ b/eng/common/TestResources/Remove-TestResources.ps1.md @@ -14,24 +14,28 @@ Deletes the resource group deployed for a service directory from Azure. ### Default (Default) ``` -Remove-TestResources.ps1 [-BaseName] [-Force] [-WhatIf] [-Confirm] [] +Remove-TestResources.ps1 [-BaseName] [-Environment ] [-Force] [-WhatIf] [-Confirm] + [] ``` ### Default+Provisioner ``` Remove-TestResources.ps1 [-BaseName] -TenantId -ProvisionerApplicationId - -ProvisionerApplicationSecret [-Force] [-WhatIf] [-Confirm] [] + -ProvisionerApplicationSecret [-Environment ] [-Force] [-WhatIf] [-Confirm] + [] ``` ### ResourceGroup+Provisioner ``` Remove-TestResources.ps1 -ResourceGroupName -TenantId -ProvisionerApplicationId - -ProvisionerApplicationSecret [-Force] [-WhatIf] [-Confirm] [] + -ProvisionerApplicationSecret [-Environment ] [-Force] [-WhatIf] [-Confirm] + [] ``` ### ResourceGroup ``` -Remove-TestResources.ps1 -ResourceGroupName [-Force] [-WhatIf] [-Confirm] [] +Remove-TestResources.ps1 -ResourceGroupName [-Environment ] [-Force] [-WhatIf] [-Confirm] + [] ``` ## DESCRIPTION @@ -148,6 +152,23 @@ Accept pipeline input: False Accept wildcard characters: False ``` +### -Environment +Name of the cloud environment. +The default is the Azure Public Cloud +('PublicCloud') + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: AzureCloud +Accept pipeline input: False +Accept wildcard characters: False +``` + ### -Force Force removal of resource group without asking for user confirmation diff --git a/eng/common/TestResources/deploy-test-resources.yml b/eng/common/TestResources/deploy-test-resources.yml index d63c0e1ac161..5844d2d39b06 100644 --- a/eng/common/TestResources/deploy-test-resources.yml +++ b/eng/common/TestResources/deploy-test-resources.yml @@ -6,13 +6,17 @@ parameters: TestApplicationObjectId: not-specified ProvisionerApplicationId: not-specified ProvisionerApplicationSecret: not-specified + AdditionalParameters: '' + Location: 'westus2' + Environment: 'AzureCloud' DeleteAfterHours: 24 - + Condition: succeeded() steps: # New-TestResources command requires Az module - pwsh: Install-Module -Name Az -Scope CurrentUser -AllowClobber -Force -Verbose displayName: Install Azure PowerShell module + condition: ${{ parameters.Condition }} # Command sets an environment variable in the pipeline's context: # AZURE_RESOURCEGROUP_NAME @@ -26,8 +30,12 @@ steps: -TestApplicationOid '${{ parameters.TestApplicationObjectId }}' -ProvisionerApplicationId '${{ parameters.ProvisionerApplicationId }}' -ProvisionerApplicationSecret '${{ parameters.ProvisionerApplicationSecret }}' + -AdditionalParameters ${{ parameters.AdditionalParameters }} -DeleteAfterHours ${{ parameters.DeleteAfterHours }} + -Location '${{ parameters.Location }}' + -Environment '${{ parameters.Environment }}' -CI -Force -Verbose displayName: Deploy test resources + condition: ${{ parameters.Condition }} \ No newline at end of file diff --git a/eng/common/TestResources/remove-test-resources.yml b/eng/common/TestResources/remove-test-resources.yml index 8fb6177a38c3..90fe597e0fa1 100644 --- a/eng/common/TestResources/remove-test-resources.yml +++ b/eng/common/TestResources/remove-test-resources.yml @@ -4,6 +4,8 @@ parameters: TenantId: not-specified ProvisionerApplicationId: not-specified ProvisionerApplicationSecret: not-specified + Environment: 'AzureCloud' + Condition: true steps: - pwsh: > @@ -12,8 +14,9 @@ steps: -TenantId '${{ parameters.TenantId }}' -ProvisionerApplicationId '${{ parameters.ProvisionerApplicationId }}' -ProvisionerApplicationSecret '${{ parameters.ProvisionerApplicationSecret }}' + -Environment '${{ parameters.Environment }}' -Force -Verbose displayName: Remove test resources - condition: ne(variables['AZURE_RESOURCEGROUP_NAME'], '') + condition: and(ne(variables['AZURE_RESOURCEGROUP_NAME'], ''), ${{ parameters.Condition }}) continueOnError: true From e9f339a3544bf3b080e850e9f4af4733567485eb Mon Sep 17 00:00:00 2001 From: chradek <51000525+chradek@users.noreply.github.com> Date: Thu, 19 Mar 2020 11:45:40 -0700 Subject: [PATCH 25/28] [eventhubs-checkpointstore-blob] improve samples (#7859) * [eventhubs-checkpointstore-blob] improve samples * remove locale from url * update azure-stack link to remove version * add readmes to samples * improve urlFragments --- .../samples/javascript/README.md | 58 +++++++++++++++ .../apiSpecificContainerClient.js} | 19 +++-- .../samples/javascript/package.json | 38 ++++++++++ .../receiveEventsUsingCheckpointStore.js | 0 .../receiveEventsWithApiSpecificStorage.js} | 9 +-- .../samples/typescript/README.md | 70 +++++++++++++++++++ .../samples/typescript/package.json | 43 ++++++++++++ .../src/apiSpecificContainerClient.ts} | 15 ++-- .../src}/receiveEventsUsingCheckpointStore.ts | 0 .../receiveEventsWithApiSpecificStorage.ts} | 9 +-- .../samples/typescript/tsconfig.json | 11 +++ 11 files changed, 254 insertions(+), 18 deletions(-) create mode 100644 sdk/eventhub/eventhubs-checkpointstore-blob/samples/javascript/README.md rename sdk/eventhub/eventhubs-checkpointstore-blob/samples/{downlevelContainerClient.js => javascript/apiSpecificContainerClient.js} (59%) create mode 100644 sdk/eventhub/eventhubs-checkpointstore-blob/samples/javascript/package.json rename sdk/eventhub/eventhubs-checkpointstore-blob/samples/{ => javascript}/receiveEventsUsingCheckpointStore.js (100%) rename sdk/eventhub/eventhubs-checkpointstore-blob/samples/{receiveEventsWithDownleveledStorage.js => javascript/receiveEventsWithApiSpecificStorage.js} (88%) create mode 100644 sdk/eventhub/eventhubs-checkpointstore-blob/samples/typescript/README.md create mode 100644 sdk/eventhub/eventhubs-checkpointstore-blob/samples/typescript/package.json rename sdk/eventhub/eventhubs-checkpointstore-blob/samples/{downlevelContainerClient.ts => typescript/src/apiSpecificContainerClient.ts} (66%) rename sdk/eventhub/eventhubs-checkpointstore-blob/samples/{ => typescript/src}/receiveEventsUsingCheckpointStore.ts (100%) rename sdk/eventhub/eventhubs-checkpointstore-blob/samples/{receiveEventsWithDownleveledStorage.ts => typescript/src/receiveEventsWithApiSpecificStorage.ts} (88%) create mode 100644 sdk/eventhub/eventhubs-checkpointstore-blob/samples/typescript/tsconfig.json diff --git a/sdk/eventhub/eventhubs-checkpointstore-blob/samples/javascript/README.md b/sdk/eventhub/eventhubs-checkpointstore-blob/samples/javascript/README.md new file mode 100644 index 000000000000..4cd7d043ced3 --- /dev/null +++ b/sdk/eventhub/eventhubs-checkpointstore-blob/samples/javascript/README.md @@ -0,0 +1,58 @@ +--- +page_type: sample +languages: + - javascript +products: + - azure + - azure-event-hubs + - azure-storage +urlFragment: eventhubs-checkpointstore-blob-javascript +--- + +# Azure Event Hubs client library samples with persistent checkpointing for JavaScript + +These sample programs show how to use the JavaScript client libraries for Azure Event Hubs in some common scenarios. + +| **File Name** | **Description** | +| ------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| [receiveEventsUsingCheckpointStore.js][checkpointing] | Demonstrates how to use the BlobCheckpointStore with EventHubConsumerClient to process events from all partitions of a consumer group in an Event Hubs instance. | +| [receiveEventsWithApiSpecificStorage.js][apispecificstorage] | Demonstrates how to use a specific Azure Storage Blobs API version with BlobCheckpointStore. | + +## Prerequisites + +The samples are compatible with Node.js >= 8.0.0. + +You need [an Azure subscription][freesub], [an Azure Event Hub resource][azhubacct] and [an Azure Storage account][azstorage] to run these sample programs. The IOT Hub sample additionally requires an [IOT Hub resource][aziothub]. Samples retrieve credentials to access the event hub from environment variables. Alternatively, edit the source code to include the appropriate credentials. See each individual sample for details on which environment variables/credentials it requires to function. + +Adapting the samples to run in the browser requires some additional consideration. For details, please see the [package README][package]. + +## Setup + +To run the samples using the published version of the package: + +1. Install the dependencies using `npm`: + +```bash +npm install +``` + +2. Edit the sample file you plan to run to use the correct credentials to access the Azure services. + +3. Run whichever samples you like (note that some samples may require additional setup): + +```bash +node receiveEventsUsingCheckpointStore.js +``` + +## Next Steps + +Take a look at our [API Documentation][apiref] for more information about the APIs that are available in the clients. + +[azstorage]: https://docs.microsoft.com/azure/storage/common/storage-account-overview +[apiref]: https://docs.microsoft.com/javascript/api/@azure/event-hubs +[checkpointing]: https://github.com/Azure/azure-sdk-for-js/blob/master/sdk/eventhub/eventhubs-checkpointstore-blob/samples/javascript/receiveEventsUsingCheckpointStore.js +[apispecificstorage]: https://github.com/Azure/azure-sdk-for-js/blob/master/sdk/eventhub/eventhubs-checkpointstore-blob/samples/javascript/receiveEventsWithApiSpecificStorage.js +[azhubacct]: https://docs.microsoft.com/azure/event-hubs/event-hubs-node-get-started-send +[freesub]: https://azure.microsoft.com/free/ +[package]: https://github.com/Azure/azure-sdk-for-js/tree/master/sdk/eventhub/event-hubs/README.md +[typescript]: https://www.typescriptlang.org/docs/home.html diff --git a/sdk/eventhub/eventhubs-checkpointstore-blob/samples/downlevelContainerClient.js b/sdk/eventhub/eventhubs-checkpointstore-blob/samples/javascript/apiSpecificContainerClient.js similarity index 59% rename from sdk/eventhub/eventhubs-checkpointstore-blob/samples/downlevelContainerClient.js rename to sdk/eventhub/eventhubs-checkpointstore-blob/samples/javascript/apiSpecificContainerClient.js index c6157757733a..6d22e0f47627 100644 --- a/sdk/eventhub/eventhubs-checkpointstore-blob/samples/downlevelContainerClient.js +++ b/sdk/eventhub/eventhubs-checkpointstore-blob/samples/javascript/apiSpecificContainerClient.js @@ -3,20 +3,27 @@ Licensed under the MIT Licence. This sample demonstrates how to extend the Storage Blob Container Client - to downlevel the API version used when communicating with the service. + to change the API version used when communicating with the service. This may be useful if the environment you are targetting supports an older version of Storage Blob than is officially supported by the @azure/storage-blob SDK. + + For example, if you are running Event Hubs on Azure Stack Hub, + the Azure Stack Hub version 2002 supports up to version 2017-11-09 + of the Azure Storage service. + + For more information on the Azure Storage versions supported on Azure Stack, + please refer to https://docs.microsoft.com/azure-stack/user/azure-stack-acs-differences. */ const { ContainerClient } = require("@azure/storage-blob"); /** - * The DownlevelContainerClient overwrites the API version sent via the + * The ApiSpecificContainerClient overwrites the API version sent via the * `x-ms-version` header to "2017-11-09". */ -class DownlevelContainerClient extends ContainerClient { +class ApiSpecificContainerClient extends ContainerClient { constructor(connectionString, containerName, options) { super(connectionString, containerName, options); @@ -24,7 +31,7 @@ class DownlevelContainerClient extends ContainerClient { create(nextPolicy) { return { async sendRequest(httpRequest) { - httpRequest.headers.set("x-ms-version", DownlevelContainerClient.API_VERSION); + httpRequest.headers.set("x-ms-version", ApiSpecificContainerClient.API_VERSION); const response = await nextPolicy.sendRequest(httpRequest); return response; } @@ -38,5 +45,5 @@ class DownlevelContainerClient extends ContainerClient { this.pipeline.factories.splice(pipelineFactoriesCount - 1, 0, storageVersionPolicy); } } -DownlevelContainerClient.API_VERSION = "2017-11-09"; -exports.DownlevelContainerClient = DownlevelContainerClient; +ApiSpecificContainerClient.API_VERSION = "2017-11-09"; +exports.ApiSpecificContainerClient = ApiSpecificContainerClient; diff --git a/sdk/eventhub/eventhubs-checkpointstore-blob/samples/javascript/package.json b/sdk/eventhub/eventhubs-checkpointstore-blob/samples/javascript/package.json new file mode 100644 index 000000000000..ccae15103a63 --- /dev/null +++ b/sdk/eventhub/eventhubs-checkpointstore-blob/samples/javascript/package.json @@ -0,0 +1,38 @@ +{ + "name": "azure-eventhubs-checkpointstore-blob-samples-js", + "private": true, + "version": "0.1.0", + "description": "Azure Event Hubs Checkpoint Store Blob library samples for JavaScript", + "engine": { + "node": ">=8.0.0" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/Azure/azure-sdk-for-js.git" + }, + "keywords": [ + "Azure", + "Event Hubs", + "Checkpoint Store", + "Node.js", + "JavaScript" + ], + "author": "Microsoft Corporation", + "license": "MIT", + "bugs": { + "url": "https://github.com/Azure/azure-sdk-for-js/issues" + }, + "homepage": "https://github.com/Azure/azure-sdk-for-js#readme", + "sideEffects": false, + "dependencies": { + "@azure/event-hubs": "^5.0.2", + "@azure/eventhubs-checkpointstore-blob": "^1.0.0", + "@azure/storage-blob": "^12.1.1", + "@types/dotenv": "^8.2.0", + "dotenv": "^8.2.0", + "tslib": "^1.9.3" + }, + "devDependencies": { + "rimraf": "^3.0.0" + } +} diff --git a/sdk/eventhub/eventhubs-checkpointstore-blob/samples/receiveEventsUsingCheckpointStore.js b/sdk/eventhub/eventhubs-checkpointstore-blob/samples/javascript/receiveEventsUsingCheckpointStore.js similarity index 100% rename from sdk/eventhub/eventhubs-checkpointstore-blob/samples/receiveEventsUsingCheckpointStore.js rename to sdk/eventhub/eventhubs-checkpointstore-blob/samples/javascript/receiveEventsUsingCheckpointStore.js diff --git a/sdk/eventhub/eventhubs-checkpointstore-blob/samples/receiveEventsWithDownleveledStorage.js b/sdk/eventhub/eventhubs-checkpointstore-blob/samples/javascript/receiveEventsWithApiSpecificStorage.js similarity index 88% rename from sdk/eventhub/eventhubs-checkpointstore-blob/samples/receiveEventsWithDownleveledStorage.js rename to sdk/eventhub/eventhubs-checkpointstore-blob/samples/javascript/receiveEventsWithApiSpecificStorage.js index c0bd5d62872f..5ce7c64edf2c 100644 --- a/sdk/eventhub/eventhubs-checkpointstore-blob/samples/receiveEventsWithDownleveledStorage.js +++ b/sdk/eventhub/eventhubs-checkpointstore-blob/samples/javascript/receiveEventsWithApiSpecificStorage.js @@ -3,9 +3,10 @@ Licensed under the MIT Licence. This sample demonstrates how to use the EventHubConsumerClient to process events from all partitions - of a consumer group in an Event Hubs instance, as well as checkpointing along the way. + of a consumer group in an Event Hubs instance, as well as checkpointing - synonymous with persisting + your event offsets - along the way. - This sample uses the `DownLevelContainerClient` to target an older version of the Storage Blob service. + This sample uses the `ApiSpecificContainerClient` to use a specific version of the Storage Blob service. Checkpointing using a durable store allows your application to be more resilient. When you restart your application after a crash (or an intentional stop), your application can continue consuming @@ -20,7 +21,7 @@ const { EventHubConsumerClient } = require("@azure/event-hubs"); const { BlobCheckpointStore } = require("@azure/eventhubs-checkpointstore-blob"); -const { DownlevelContainerClient } = require("./downlevelContainerClient"); +const { ApiSpecificContainerClient } = require("./apiSpecificContainerClient"); const connectionString = ""; const eventHubName = ""; @@ -31,7 +32,7 @@ const consumerGroup = ""; async function main() { // This client will be used by our eventhubs-checkpointstore-blob, which // persists any checkpoints from this session in Azure Storage. - const containerClient = new DownlevelContainerClient(storageConnectionString, containerName); + const containerClient = new ApiSpecificContainerClient(storageConnectionString, containerName); if (!containerClient.exists()) { await containerClient.create(); diff --git a/sdk/eventhub/eventhubs-checkpointstore-blob/samples/typescript/README.md b/sdk/eventhub/eventhubs-checkpointstore-blob/samples/typescript/README.md new file mode 100644 index 000000000000..5d89f991cba8 --- /dev/null +++ b/sdk/eventhub/eventhubs-checkpointstore-blob/samples/typescript/README.md @@ -0,0 +1,70 @@ +--- +page_type: sample +languages: + - typescript +products: + - azure + - azure-event-hubs + - azure-storage +urlFragment: eventhubs-checkpointstore-blob-typescript +--- + +# Azure Event Hubs client library samples with persistent checkpointing for TypeScript + +These sample programs show how to use the TypeScript client libraries for Azure Event Hubs in some common scenarios. + +| **File Name** | **Description** | +| ------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| [receiveEventsUsingCheckpointStore.ts][checkpointing] | Demonstrates how to use the BlobCheckpointStore with EventHubConsumerClient to process events from all partitions of a consumer group in an Event Hubs instance. | +| [receiveEventsWithApiSpecificStorage.ts][apispecificstorage] | Demonstrates how to use a specific Azure Storage Blobs API version with BlobCheckpointStore. | + +## Prerequisites + +The samples are compatible with Node.js >= 8.0.0. + +Before running the samples in Node, they must be compiled to JavaScript using the TypeScript compiler. For more information on TypeScript, see the [TypeScript documentation][typescript]. Install the TypeScript compiler using + +```bash +npm install -g typescript +``` + +You need [an Azure subscription][freesub], [an Azure Event Hub resource][azhubacct] and [an Azure Storage account][azstorage] to run these sample programs. The IOT Hub sample additionally requires an [IOT Hub resource][aziothub]. Samples retrieve credentials to access the event hub from environment variables. Alternatively, edit the source code to include the appropriate credentials. See each individual sample for details on which environment variables/credentials it requires to function. + +Adapting the samples to run in the browser requires some additional consideration. For details, please see the [package README][package]. + +## Setup + +To run the samples using the published version of the package: + +1. Install the dependencies using `npm`: + +```bash +npm install +``` + +2. Edit the sample file you plan to run to use the correct credentials to access the Azure services. + +3. Compile the samples + +```bash +npm run build +``` + +4. Run whichever samples you like (note that some samples may require additional setup): + +```bash +node dist/receiveEventsUsingCheckpointStore.js +``` + +## Next Steps + +Take a look at our [API Documentation][apiref] for more information about the APIs that are available in the clients. + +[azstorage]: https://docs.microsoft.com/azure/storage/common/storage-account-overview +[apiref]: https://docs.microsoft.com/javascript/api/@azure/event-hubs +[checkpointing]: https://github.com/Azure/azure-sdk-for-js/blob/master/sdk/eventhub/eventhubs-checkpointstore-blob/samples/typescript/src/receiveEventsUsingCheckpointStore.ts +[apispecificstorage]: https://github.com/Azure/azure-sdk-for-js/blob/master/sdk/eventhub/eventhubs-checkpointstore-blob/samples/typescript/src/receiveEventsWithApiSpecificStorage.ts +[azhubacct]: https://docs.microsoft.com/azure/event-hubs/event-hubs-node-get-started-send +[freesub]: https://azure.microsoft.com/free/ +[package]: https://github.com/Azure/azure-sdk-for-js/tree/master/sdk/eventhub/event-hubs/README.md +[typescript]: https://www.typescriptlang.org/docs/home.html diff --git a/sdk/eventhub/eventhubs-checkpointstore-blob/samples/typescript/package.json b/sdk/eventhub/eventhubs-checkpointstore-blob/samples/typescript/package.json new file mode 100644 index 000000000000..a416bad13314 --- /dev/null +++ b/sdk/eventhub/eventhubs-checkpointstore-blob/samples/typescript/package.json @@ -0,0 +1,43 @@ +{ + "name": "azure-eventhubs-checkpointstore-blob-samples-ts", + "private": true, + "version": "0.1.0", + "description": "Azure Event Hubs Checkpoint Store Blob library samples for TypeScript", + "engine": { + "node": ">=8.0.0" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/Azure/azure-sdk-for-js.git" + }, + "keywords": [ + "Azure", + "Event Hubs", + "Checkpoint Store", + "Node.js", + "TypeScript" + ], + "author": "Microsoft Corporation", + "license": "MIT", + "bugs": { + "url": "https://github.com/Azure/azure-sdk-for-js/issues" + }, + "homepage": "https://github.com/Azure/azure-sdk-for-js#readme", + "sideEffects": false, + "scripts": { + "build": "tsc -p ." + }, + "dependencies": { + "@azure/event-hubs": "^5.0.2", + "@azure/eventhubs-checkpointstore-blob": "^1.0.0", + "@azure/storage-blob": "^12.1.1", + "@types/dotenv": "^8.2.0", + "dotenv": "^8.2.0", + "tslib": "^1.9.3" + }, + "devDependencies": { + "@types/node": "^12.12.17", + "rimraf": "^3.0.0", + "typescript": "^3.7.2" + } +} diff --git a/sdk/eventhub/eventhubs-checkpointstore-blob/samples/downlevelContainerClient.ts b/sdk/eventhub/eventhubs-checkpointstore-blob/samples/typescript/src/apiSpecificContainerClient.ts similarity index 66% rename from sdk/eventhub/eventhubs-checkpointstore-blob/samples/downlevelContainerClient.ts rename to sdk/eventhub/eventhubs-checkpointstore-blob/samples/typescript/src/apiSpecificContainerClient.ts index 0456d120c893..f6b2c274cae8 100644 --- a/sdk/eventhub/eventhubs-checkpointstore-blob/samples/downlevelContainerClient.ts +++ b/sdk/eventhub/eventhubs-checkpointstore-blob/samples/typescript/src/apiSpecificContainerClient.ts @@ -3,20 +3,27 @@ Licensed under the MIT Licence. This sample demonstrates how to extend the Storage Blob Container Client - to downlevel the API version used when communicating with the service. + to change the API version used when communicating with the service. This may be useful if the environment you are targetting supports an older version of Storage Blob than is officially supported by the @azure/storage-blob SDK. + + For example, if you are running Event Hubs on Azure Stack Hub, + the Azure Stack Hub version 2002 supports up to version 2017-11-09 + of the Azure Storage service. + + For more information on the Azure Storage versions supported on Azure Stack, + please refer to https://docs.microsoft.com/azure-stack/user/azure-stack-acs-differences. */ import { ContainerClient, StoragePipelineOptions, RequestPolicyFactory } from "@azure/storage-blob"; /** - * The DownlevelContainerClient overwrites the API version sent via the + * The ApiSpecificContainerClient overwrites the API version sent via the * `x-ms-version` header to "2017-11-09". */ -export class DownlevelContainerClient extends ContainerClient { +export class ApiSpecificContainerClient extends ContainerClient { private static API_VERSION = "2017-11-09"; constructor(connectionString: string, containerName: string, options?: StoragePipelineOptions) { super(connectionString, containerName, options); @@ -25,7 +32,7 @@ export class DownlevelContainerClient extends ContainerClient { create(nextPolicy) { return { async sendRequest(httpRequest) { - httpRequest.headers.set("x-ms-version", DownlevelContainerClient.API_VERSION); + httpRequest.headers.set("x-ms-version", ApiSpecificContainerClient.API_VERSION); const response = await nextPolicy.sendRequest(httpRequest); return response; } diff --git a/sdk/eventhub/eventhubs-checkpointstore-blob/samples/receiveEventsUsingCheckpointStore.ts b/sdk/eventhub/eventhubs-checkpointstore-blob/samples/typescript/src/receiveEventsUsingCheckpointStore.ts similarity index 100% rename from sdk/eventhub/eventhubs-checkpointstore-blob/samples/receiveEventsUsingCheckpointStore.ts rename to sdk/eventhub/eventhubs-checkpointstore-blob/samples/typescript/src/receiveEventsUsingCheckpointStore.ts diff --git a/sdk/eventhub/eventhubs-checkpointstore-blob/samples/receiveEventsWithDownleveledStorage.ts b/sdk/eventhub/eventhubs-checkpointstore-blob/samples/typescript/src/receiveEventsWithApiSpecificStorage.ts similarity index 88% rename from sdk/eventhub/eventhubs-checkpointstore-blob/samples/receiveEventsWithDownleveledStorage.ts rename to sdk/eventhub/eventhubs-checkpointstore-blob/samples/typescript/src/receiveEventsWithApiSpecificStorage.ts index ccdbf55d481f..9088fcddca09 100644 --- a/sdk/eventhub/eventhubs-checkpointstore-blob/samples/receiveEventsWithDownleveledStorage.ts +++ b/sdk/eventhub/eventhubs-checkpointstore-blob/samples/typescript/src/receiveEventsWithApiSpecificStorage.ts @@ -3,9 +3,10 @@ Licensed under the MIT Licence. This sample demonstrates how to use the EventHubConsumerClient to process events from all partitions - of a consumer group in an Event Hubs instance, as well as checkpointing along the way. - + of a consumer group in an Event Hubs instance, as well as checkpointing - synonymous with persisting + your event offsets - along the way. + This sample uses the `ApiSpecificContainerClient` to use a specific version of the Storage Blob service. Checkpointing using a durable store allows your application to be more resilient. When you restart your application after a crash (or an intentional stop), your application can continue consuming @@ -20,7 +21,7 @@ import { EventHubConsumerClient, CheckpointStore } from "@azure/event-hubs"; import { BlobCheckpointStore } from "@azure/eventhubs-checkpointstore-blob"; -import { DownlevelContainerClient } from "./downlevelContainerClient"; +import { ApiSpecificContainerClient } from "./apiSpecificContainerClient"; const connectionString = ""; const eventHubName = ""; @@ -31,7 +32,7 @@ const consumerGroup = ""; async function main() { // This client will be used by our eventhubs-checkpointstore-blob, which // persists any checkpoints from this session in Azure Storage. - const containerClient = new DownlevelContainerClient(storageConnectionString, containerName); + const containerClient = new ApiSpecificContainerClient(storageConnectionString, containerName); if (!containerClient.exists()) { await containerClient.create(); diff --git a/sdk/eventhub/eventhubs-checkpointstore-blob/samples/typescript/tsconfig.json b/sdk/eventhub/eventhubs-checkpointstore-blob/samples/typescript/tsconfig.json new file mode 100644 index 000000000000..f9cc2a09e855 --- /dev/null +++ b/sdk/eventhub/eventhubs-checkpointstore-blob/samples/typescript/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "module": "commonjs", + "moduleResolution": "node", + "allowSyntheticDefaultImports": true, + "outDir": "dist", + "rootDir": "src" + }, + "include": ["src/**.ts"], + "exclude": ["node_modules"] +} From b2a067636d2e8fef8104342a8a616c8c0785d2b2 Mon Sep 17 00:00:00 2001 From: Harsha Nalluru Date: Thu, 19 Mar 2020 12:56:29 -0700 Subject: [PATCH 26/28] [Service Bus] sendBatch() for service-bus (#7527) * SendableMessageInfoBatch - draft 1 * SendableMessageInfoBatch raw implementation - test failed * SendableMessageInfoBatch works fine * updated test * SendableMessageInfoBatch * sendBatch2 * CreateBatchOptions * getMaxMessageSize, createBatch, sendBatch2 on MessageSender * Simple Send Batch 2 tests - draft * update test file name * senbatch tests - draft 1 * sendbatch API * separate out the methods - testing sendBatch * remove .only and console.logs * remove console.log * _batchSender -> _sender * // let numberOfMessagesInBatch = 0; * Update Api report * Add a lot more tests * remove .only from individual tests * (craete and send) batch - docs * Update tryAdd docs * update API report * export CreateBatchOptions and SendableMessageInfoBatch * update API report * update src code as per the api updates to the message * Update API report * Update test files asper the API updates * CreateBatchOptions extends OperationOptions * docs for ServiceBusMessageBatch * remove un-need setting default null options - options = {} * verify if sender is Open() before sending * update API report * // sendBatch() - Commented, senBatch2 -> sendBatch * update API shape * fix tests * Update sdk/servicebus/service-bus/test/sendAndSchedule.spec.ts * Update sdk/servicebus/service-bus/test/sendBatch.spec.ts * retryOptions to getmaxsize * move CreateBatchOptions to models * remove unnecessary if checks * remove _count private property in favour of _encodedMessages.length * use sendBatch to send multiple messages * update getMaxMessageSize description * sendArrayOfMessages -> sendMessages * move verifyAndDeleteAllSentMessages from sendbatch.spec to testutils * Update sdk/servicebus/service-bus/test/utils/testutils2.ts * test update - add checks for tryadd * fix messageBatch error message in test when Invalid parameters are passed * Update sdk/servicebus/service-bus/test/utils/testutils2.ts Co-authored-by: HarshaNalluru <10452642+HarshaNalluru@users.noreply.github.com> --- .../service-bus/review/service-bus.api.md | 17 +- .../service-bus/src/core/messageSender.ts | 104 ++- sdk/servicebus/service-bus/src/index.ts | 5 +- sdk/servicebus/service-bus/src/models.ts | 27 + sdk/servicebus/service-bus/src/sender.ts | 87 ++- .../service-bus/src/serviceBusMessage.ts | 4 +- .../service-bus/src/serviceBusMessageBatch.ts | 179 +++++ .../service-bus/test/batchReceiver.spec.ts | 6 +- .../test/invalidParameters.spec.ts | 4 +- ...hedule.spec.ts => sendAndSchedule.spec.ts} | 206 +++--- .../service-bus/test/sendBatch.spec.ts | 642 ++++++++++++++++++ .../test/streamingReceiver.spec.ts | 6 +- .../test/streamingReceiverSessions.spec.ts | 12 +- .../service-bus/test/utils/testutils2.ts | 83 ++- 14 files changed, 1243 insertions(+), 139 deletions(-) create mode 100644 sdk/servicebus/service-bus/src/serviceBusMessageBatch.ts rename sdk/servicebus/service-bus/test/{sendSchedule.spec.ts => sendAndSchedule.spec.ts} (83%) create mode 100644 sdk/servicebus/service-bus/test/sendBatch.spec.ts diff --git a/sdk/servicebus/service-bus/review/service-bus.api.md b/sdk/servicebus/service-bus/review/service-bus.api.md index 5722c7074838..662b2b479394 100644 --- a/sdk/servicebus/service-bus/review/service-bus.api.md +++ b/sdk/servicebus/service-bus/review/service-bus.api.md @@ -30,6 +30,12 @@ export interface CorrelationFilter { userProperties?: any; } +// @public +export interface CreateBatchOptions extends OperationOptions { + maxSizeInBytes?: number; + retryOptions?: RetryOptions; +} + export { DataTransformer } // @public @@ -136,11 +142,12 @@ export interface Sender { cancelScheduledMessage(sequenceNumber: Long): Promise; cancelScheduledMessages(sequenceNumbers: Long[]): Promise; close(): Promise; + createBatch(options?: CreateBatchOptions): Promise; isClosed: boolean; scheduleMessage(scheduledEnqueueTimeUtc: Date, message: ServiceBusMessage): Promise; scheduleMessages(scheduledEnqueueTimeUtc: Date, messages: ServiceBusMessage[]): Promise; send(message: ServiceBusMessage): Promise; - sendBatch(messages: ServiceBusMessage[]): Promise; + sendBatch(messageBatch: ServiceBusMessageBatch): Promise; } // @public @@ -185,6 +192,14 @@ export interface ServiceBusMessage { viaPartitionKey?: string; } +// @public +export interface ServiceBusMessageBatch { + readonly count: number; + readonly maxSizeInBytes: number; + readonly sizeInBytes: number; + tryAdd(message: ServiceBusMessage): boolean; +} + // @public export interface SessionMessageHandlerOptions { autoComplete?: boolean; diff --git a/sdk/servicebus/service-bus/src/core/messageSender.ts b/sdk/servicebus/service-bus/src/core/messageSender.ts index 5af236a79031..98c4dce5e584 100644 --- a/sdk/servicebus/service-bus/src/core/messageSender.ts +++ b/sdk/servicebus/service-bus/src/core/messageSender.ts @@ -22,7 +22,8 @@ import { RetryOperationType, Constants, delay, - MessagingError + MessagingError, + RetryOptions } from "@azure/core-amqp"; import { ServiceBusMessage, @@ -33,6 +34,8 @@ import { ClientEntityContext } from "../clientEntityContext"; import { LinkEntity } from "./linkEntity"; import { getUniqueName } from "../util/utils"; import { throwErrorIfConnectionClosed } from "../util/errors"; +import { ServiceBusMessageBatch, ServiceBusMessageBatchImpl } from "../serviceBusMessageBatch"; +import { CreateBatchOptions } from "../models"; /** * @internal @@ -581,6 +584,7 @@ export class MessageSender extends LinkEntity { } } + // Not exposed to the users /** * Send a batch of Message to the ServiceBus in a single AMQP message. The "message_annotations", * "application_properties" and "properties" of the first message will be set as that @@ -589,7 +593,7 @@ export class MessageSender extends LinkEntity { * Batch message. * @return {Promise} */ - async sendBatch(inputMessages: ServiceBusMessage[]): Promise { + async sendMessages(inputMessages: ServiceBusMessage[]): Promise { throwErrorIfConnectionClosed(this._context.namespace); try { if (!Array.isArray(inputMessages)) { @@ -649,6 +653,7 @@ export class MessageSender extends LinkEntity { // Finally encode the envelope (batch message). const encodedBatchMessage = RheaMessageUtil.encode(batchMessage); + log.sender( "[%s]Sender '%s', sending encoded batch message.", this._context.namespace.connectionId, @@ -668,6 +673,101 @@ export class MessageSender extends LinkEntity { } } + /** + * Returns maximum message size on the AMQP sender link. + * + * Options to configure the `createBatch` method on the `Sender`. + * - `maxSizeInBytes`: The upper limit for the size of batch. + * + * Example usage: + * ```js + * { + * retryOptions: { maxRetries: 5; timeoutInMs: 10 } + * } + * ``` + * @param {{retryOptions?: RetryOptions}} [options={}] + * @returns {Promise} + * @memberof MessageSender + */ + async getMaxMessageSize( + options: { + retryOptions?: RetryOptions; + } = {} + ): Promise { + const retryOptions = options.retryOptions || {}; + if (this.isOpen()) { + return this._sender!.maxMessageSize; + } + return new Promise(async (resolve, reject) => { + try { + const senderOptions = this._createSenderOptions(Constants.defaultOperationTimeoutInMs); + await defaultLock.acquire(this.senderLock, () => { + const config: RetryConfig = { + operation: () => this._init(senderOptions), + connectionId: this._context.namespace.connectionId, + operationType: RetryOperationType.senderLink, + retryOptions: retryOptions + }; + + return retry(config); + }); + resolve(this._sender!.maxMessageSize); + } catch (err) { + reject(err); + } + }); + } + + async createBatch(options?: CreateBatchOptions): Promise { + throwErrorIfConnectionClosed(this._context.namespace); + if (!options) { + options = {}; + } + let maxMessageSize = await this.getMaxMessageSize({ retryOptions: options.retryOptions }); + if (options.maxSizeInBytes) { + if (options.maxSizeInBytes > maxMessageSize!) { + const error = new Error( + `Max message size (${options.maxSizeInBytes} bytes) is greater than maximum message size (${maxMessageSize} bytes) on the AMQP sender link.` + ); + throw error; + } + maxMessageSize = options.maxSizeInBytes; + } + return new ServiceBusMessageBatchImpl(this._context, maxMessageSize!); + } + + async sendBatch(batchMessage: ServiceBusMessageBatch): Promise { + throwErrorIfConnectionClosed(this._context.namespace); + try { + if (!this.isOpen()) { + log.sender( + "Acquiring lock %s for initializing the session, sender and " + + "possibly the connection.", + this.senderLock + ); + await defaultLock.acquire(this.senderLock, () => { + return this._init(); + }); + } + log.sender( + "[%s]Sender '%s', sending encoded batch message.", + this._context.namespace.connectionId, + this.name, + batchMessage + ); + return await this._trySend(batchMessage._message!, true); + } catch (err) { + log.error( + "[%s] Sender '%s': An error occurred while sending the messages: %O\nError: %O", + this._context.namespace.connectionId, + this.name, + batchMessage, + err + ); + throw err; + } + } + /** * Creates a new sender to the specific ServiceBus entity, and optionally to a given * partition if it is not present in the context or returns the one present in the context. diff --git a/sdk/servicebus/service-bus/src/index.ts b/sdk/servicebus/service-bus/src/index.ts index ab812834118d..a98353b02894 100644 --- a/sdk/servicebus/service-bus/src/index.ts +++ b/sdk/servicebus/service-bus/src/index.ts @@ -26,6 +26,8 @@ export { ReceiveMode, ReceivedMessageWithLock } from "./serviceBusMessage"; +export { ServiceBusMessageBatch } from "./serviceBusMessageBatch"; + export { Delivery, WebSocketImpl } from "rhea-promise"; export { @@ -35,7 +37,8 @@ export { MessageHandlers, ReceiveBatchOptions, SubscribeOptions, - WaitTimeOptions + WaitTimeOptions, + CreateBatchOptions } from "./models"; export { Receiver, SubscriptionRuleManagement } from "./receivers/receiver"; diff --git a/sdk/servicebus/service-bus/src/models.ts b/sdk/servicebus/service-bus/src/models.ts index 8daf644f5593..cd113434eda4 100644 --- a/sdk/servicebus/service-bus/src/models.ts +++ b/sdk/servicebus/service-bus/src/models.ts @@ -2,6 +2,7 @@ // Licensed under the MIT License. import { OperationOptions } from "@azure/core-auth"; +import { RetryOptions } from "@azure/core-amqp"; /** * The general message handler interface (used for streamMessages). @@ -32,6 +33,32 @@ export interface WaitTimeOptions { maxWaitTimeSeconds: number; } +/** + * Options to configure the `createBatch` method on the `Sender`. + * - `maxSizeInBytes`: The upper limit for the size of batch. + * + * Example usage: + * ```js + * { + * maxSizeInBytes: 1024 * 1024 // 1 MB + * } + * ``` + */ +export interface CreateBatchOptions extends OperationOptions { + /** + * @property + * The upper limit for the size of batch. The `tryAdd` function will return `false` after this limit is reached. + */ + maxSizeInBytes?: number; + /** + * Retry policy options that determine the mode, number of retries, retry interval etc. + * + * @type {RetryOptions} + * @memberof CreateBatchOptions + */ + retryOptions?: RetryOptions; +} + /** * Options when receiving a batch of messages from Service Bus. */ diff --git a/sdk/servicebus/service-bus/src/sender.ts b/sdk/servicebus/service-bus/src/sender.ts index 808287e4f674..18b36d240d33 100644 --- a/sdk/servicebus/service-bus/src/sender.ts +++ b/sdk/servicebus/service-bus/src/sender.ts @@ -13,6 +13,8 @@ import { throwTypeErrorIfParameterNotLong, throwTypeErrorIfParameterNotLongArray } from "./util/errors"; +import { ServiceBusMessageBatch } from "./serviceBusMessageBatch"; +import { CreateBatchOptions } from "./models"; /** * A Sender can be used to send messages, schedule messages to be sent at a later time @@ -34,22 +36,49 @@ export interface Sender { */ send(message: ServiceBusMessage): Promise; + // sendBatch() - Commented + // /** + // * Sends the given messages in a single batch i.e. in a single AMQP message after creating an AMQP + // * Sender link if it doesnt already exists. + // * + // * - To send messages to a `session` and/or `partition` enabled Queue/Topic, set the `sessionId` + // * and/or `partitionKey` properties respectively on the messages. + // * - When doing so, all + // * messages in the batch should have the same `sessionId` (if using sessions) and the same + // * `parititionKey` (if using paritions). + // * + // * @param messages - An array of ServiceBusMessage objects to be sent in a Batch message. + // * @return Promise + // * @throws Error if the underlying connection, client or sender is closed. + // * @throws MessagingError if the service returns an error while sending messages to the service. + // */ + // sendBatch(messages: ServiceBusMessage[]): Promise; + /** - * Sends the given messages in a single batch i.e. in a single AMQP message after creating an AMQP - * Sender link if it doesnt already exists. + * Creates an instance of `ServiceBusMessageBatch` to which one can add messages until the maximum supported size is reached. + * The batch can be passed to the {@link sendBatch} method to send the messages to Azure Service Bus. + * @param options Configures the behavior of the batch. + * - `maxSizeInBytes`: The upper limit for the size of batch. The `tryAdd` function will return `false` after this limit is reached. * - * - To send messages to a `session` and/or `partition` enabled Queue/Topic, set the `sessionId` - * and/or `partitionKey` properties respectively on the messages. - * - When doing so, all - * messages in the batch should have the same `sessionId` (if using sessions) and the same - * `parititionKey` (if using paritions). + * @param {CreateBatchOptions} [options] + * @returns {Promise} + * @throws MessagingError if an error is encountered while sending a message. + * @throws Error if the underlying connection or sender has been closed. + * @memberof Sender + */ + createBatch(options?: CreateBatchOptions): Promise; + + /** + * Sends a batch of messages to the associated service-bus entity. * - * @param messages - An array of SendableMessageInfo objects to be sent in a Batch message. - * @return Promise - * @throws Error if the underlying connection, client or sender is closed. - * @throws MessagingError if the service returns an error while sending messages to the service. + * @param {ServiceBusMessageBatch} messageBatch A batch of messages that you can create using the {@link createBatch} method. + * @returns {Promise} + * @throws MessagingError if an error is encountered while sending a message. + * @throws Error if the underlying connection or sender has been closed. + * @memberof Sender */ - sendBatch(messages: ServiceBusMessage[]): Promise; + sendBatch(messageBatch: ServiceBusMessageBatch): Promise; + /** * @property Returns `true` if either the sender or the client that created it has been closed * @readonly @@ -119,6 +148,7 @@ export class SenderImpl implements Sender { * @property Denotes if close() was called on this sender */ private _isClosed: boolean = false; + private _sender: MessageSender; /** * @internal @@ -127,6 +157,7 @@ export class SenderImpl implements Sender { constructor(context: ClientEntityContext) { throwErrorIfConnectionClosed(context.namespace); this._context = context; + this._sender = MessageSender.create(this._context); } private _throwIfSenderOrConnectionClosed(): void { @@ -150,18 +181,32 @@ export class SenderImpl implements Sender { async send(message: ServiceBusMessage): Promise { this._throwIfSenderOrConnectionClosed(); throwTypeErrorIfParameterMissing(this._context.namespace.connectionId, "message", message); - const sender = MessageSender.create(this._context); - return sender.send(message); + return this._sender.send(message); } - async sendBatch(messages: ServiceBusMessage[]): Promise { + // sendBatch() - Commented + // async sendBatch(messages: ServiceBusMessage[]): Promise { + // this._throwIfSenderOrConnectionClosed(); + // throwTypeErrorIfParameterMissing(this._context.namespace.connectionId, "messages", messages); + // if (!Array.isArray(messages)) { + // messages = [messages]; + // } + // return this._sender.sendBatch(messages); + // } + + async createBatch(options?: CreateBatchOptions): Promise { this._throwIfSenderOrConnectionClosed(); - throwTypeErrorIfParameterMissing(this._context.namespace.connectionId, "messages", messages); - if (!Array.isArray(messages)) { - messages = [messages]; - } - const sender = MessageSender.create(this._context); - return sender.sendBatch(messages); + return this._sender.createBatch(options); + } + + async sendBatch(messageBatch: ServiceBusMessageBatch): Promise { + this._throwIfSenderOrConnectionClosed(); + throwTypeErrorIfParameterMissing( + this._context.namespace.connectionId, + "messageBatch", + messageBatch + ); + return this._sender.sendBatch(messageBatch); } /** diff --git a/sdk/servicebus/service-bus/src/serviceBusMessage.ts b/sdk/servicebus/service-bus/src/serviceBusMessage.ts index 27b190051363..44c8cba92c1d 100644 --- a/sdk/servicebus/service-bus/src/serviceBusMessage.ts +++ b/sdk/servicebus/service-bus/src/serviceBusMessage.ts @@ -301,7 +301,7 @@ export function getMessagePropertyTypeMismatchError(msg: ServiceBusMessage): Err /** * @internal - * Converts given SendableMessageInfo to AmqpMessage + * Converts given ServiceBusMessage to AmqpMessage */ export function toAmqpMessage(msg: ServiceBusMessage): AmqpMessage { const amqpMsg: AmqpMessage = { @@ -1043,7 +1043,7 @@ export class ServiceBusMessageImpl implements ReceivedMessageWithLock { * @returns ServiceBusMessage */ clone(): ServiceBusMessage { - // We are returning a SendableMessageInfo object because that object can then be sent to Service Bus + // We are returning a ServiceBusMessage object because that object can then be sent to Service Bus const clone: ServiceBusMessage = { body: this.body, contentType: this.contentType, diff --git a/sdk/servicebus/service-bus/src/serviceBusMessageBatch.ts b/sdk/servicebus/service-bus/src/serviceBusMessageBatch.ts new file mode 100644 index 000000000000..24d53d4b6806 --- /dev/null +++ b/sdk/servicebus/service-bus/src/serviceBusMessageBatch.ts @@ -0,0 +1,179 @@ +import { ServiceBusMessage, toAmqpMessage } from "./serviceBusMessage"; +import { throwTypeErrorIfParameterMissing } from "./util/errors"; +import { ClientEntityContext } from "./clientEntityContext"; +import { message as RheaMessageUtil, messageProperties } from "rhea-promise"; +import { AmqpMessage } from "@azure/core-amqp"; + +/** + * A batch of messages that you can create using the {@link createBatch} method. + * + * @export + * @interface ServiceBusMessageBatch + */ +export interface ServiceBusMessageBatch { + /** + * Size of the batch in bytes after the events added to it have been encoded into a single AMQP + * message. + * @readonly + */ + readonly sizeInBytes: number; + + /** + * Number of messages added to the batch. + * @readonly + */ + readonly count: number; + + /** + * The maximum size of the batch, in bytes. The `tryAdd` function on the batch will return `false` + * if the message being added causes the size of the batch to exceed this limit. Use the `createBatch()` method on + * the `Sender` to set the maxSizeInBytes. + * @readonly. + */ + readonly maxSizeInBytes: number; + + /** + * Adds a message to the batch if permitted by the batch's size limit. + * **NOTE**: Always remember to check the return value of this method, before calling it again + * for the next event. + * + * @param message An individual service bus message. + * @returns A boolean value indicating if the message has been added to the batch or not. + */ + tryAdd(message: ServiceBusMessage): boolean; + + /** + * The AMQP message containing encoded events that were added to the batch. + * Used internally by the `sendBatch()` method on the `Sender`. + * This is not meant for the user to use directly. + * + * @readonly + * @internal + * @ignore + */ + readonly _message: Buffer | undefined; +} + +/** + * An internal class representing a batch of messages which can be used to send messages to Service Bus. + * + * @class + * @internal + * @ignore + */ +export class ServiceBusMessageBatchImpl implements ServiceBusMessageBatch { + /** + * @property Describes the amqp connection context for the Client. + */ + private _context: ClientEntityContext; + /** + * @property The maximum size allowed for the batch. + */ + private _maxSizeInBytes: number; + /** + * @property Current size of the batch in bytes. + */ + private _sizeInBytes: number; + /** + * @property Encoded amqp messages. + */ + private _encodedMessages: Buffer[] = []; + /** + * @property Encoded batch message. + */ + private _batchMessage: Buffer | undefined; + + /** + * ServiceBusMessageBatch should not be constructed using `new ServiceBusMessageBatch()` + * Use the `createBatch()` method on your `Sender` instead. + * @constructor + * @internal + * @ignore + */ + constructor(context: ClientEntityContext, maxSizeInBytes: number) { + this._context = context; + this._maxSizeInBytes = maxSizeInBytes; + this._sizeInBytes = 0; + } + + /** + * @property The maximum size of the batch, in bytes. + * @readonly + */ + get maxSizeInBytes(): number { + return this._maxSizeInBytes; + } + + /** + * @property Size of the `ServiceBusMessageBatch` instance after the messages added to it have been + * encoded into a single AMQP message. + * @readonly + */ + get sizeInBytes(): number { + return this._sizeInBytes; + } + + /** + * @property Number of messages in the `ServiceBusMessageBatch` instance. + * @readonly + */ + get count(): number { + return this._encodedMessages.length; + } + + /** + * @property Represents the single AMQP message which is the result of encoding all the events + * added into the `ServiceBusMessageBatch` instance. + * + * This is not meant for the user to use directly. + * + * When the `ServiceBusMessageBatch` instance is passed to the `sendBatch()` method on the `Sender`, + * this single batched AMQP message is what gets sent over the wire to the service. + * @readonly + */ + get _message(): Buffer | undefined { + return this._batchMessage; + } + + /** + * Tries to add a message to the batch if permitted by the batch's size limit. + * **NOTE**: Always remember to check the return value of this method, before calling it again + * for the next message. + * + * @param message An individual service bus message. + * @returns A boolean value indicating if the message has been added to the batch or not. + */ + public tryAdd(message: ServiceBusMessage): boolean { + throwTypeErrorIfParameterMissing(this._context.namespace.connectionId, "tryAdd", "message"); + + // Convert ServiceBusMessage to AmqpMessage. + const amqpMessage = toAmqpMessage(message); + amqpMessage.body = this._context.namespace.dataTransformer.encode(message.body); + + // Encode every amqp message and then convert every encoded message to amqp data section + this._encodedMessages.push(RheaMessageUtil.encode(amqpMessage)); + + const batchMessage: AmqpMessage = { + body: RheaMessageUtil.data_sections(this._encodedMessages) + }; + + batchMessage.message_annotations = amqpMessage.message_annotations; + batchMessage.application_properties = amqpMessage.application_properties; + + for (const prop of messageProperties) { + (batchMessage as any)[prop] = (amqpMessage as any)[prop]; + } + + const encodedBatchMessage = RheaMessageUtil.encode(batchMessage); + const currentSize = encodedBatchMessage.length; + + // this._batchMessage will be used for final send operation + if (currentSize > this._maxSizeInBytes) { + this._encodedMessages.pop(); + return false; + } + this._batchMessage = encodedBatchMessage; + this._sizeInBytes = currentSize; + return true; + } +} diff --git a/sdk/servicebus/service-bus/test/batchReceiver.spec.ts b/sdk/servicebus/service-bus/test/batchReceiver.spec.ts index 6de1d82661c6..fb98efd14754 100644 --- a/sdk/servicebus/service-bus/test/batchReceiver.spec.ts +++ b/sdk/servicebus/service-bus/test/batchReceiver.spec.ts @@ -795,7 +795,11 @@ describe("batchReceiver", () => { // See https://github.com/Azure/azure-service-bus-node/issues/31 async function testSequentialReceiveBatchCalls(useSessions?: boolean): Promise { const testMessages = useSessions ? messageWithSessions : messages; - await senderClient.sendBatch(testMessages); + const batchMessageToSend = await senderClient.createBatch(); + for (const message of testMessages) { + batchMessageToSend.tryAdd(message); + } + await senderClient.sendBatch(batchMessageToSend); const msgs1 = await receiverClient.receiveBatch(1); const msgs2 = await receiverClient.receiveBatch(1); diff --git a/sdk/servicebus/service-bus/test/invalidParameters.spec.ts b/sdk/servicebus/service-bus/test/invalidParameters.spec.ts index 4b6e4d178e77..59a915b51630 100644 --- a/sdk/servicebus/service-bus/test/invalidParameters.spec.ts +++ b/sdk/servicebus/service-bus/test/invalidParameters.spec.ts @@ -817,7 +817,7 @@ describe("invalid parameters", () => { should.equal(caughtError && caughtError.message, `Missing parameter "message"`); }); - it("Sendbatch: Missing messages in Sender", async function(): Promise { + it("Sendbatch: Missing messageBatch in Sender", async function(): Promise { let caughtError: Error | undefined; try { await sender.sendBatch(undefined as any); @@ -825,7 +825,7 @@ describe("invalid parameters", () => { caughtError = error; } should.equal(caughtError && caughtError.name, "TypeError"); - should.equal(caughtError && caughtError.message, `Missing parameter "messages"`); + should.equal(caughtError && caughtError.message, `Missing parameter "messageBatch"`); }); it("ScheduledMessage: Missing date in Sender", async function(): Promise { diff --git a/sdk/servicebus/service-bus/test/sendSchedule.spec.ts b/sdk/servicebus/service-bus/test/sendAndSchedule.spec.ts similarity index 83% rename from sdk/servicebus/service-bus/test/sendSchedule.spec.ts rename to sdk/servicebus/service-bus/test/sendAndSchedule.spec.ts index b1583da2490d..5424e616657c 100644 --- a/sdk/servicebus/service-bus/test/sendSchedule.spec.ts +++ b/sdk/servicebus/service-bus/test/sendAndSchedule.spec.ts @@ -105,79 +105,80 @@ describe("send scheduled messages", () => { }); }); - describe("Simple Send Batch", function(): void { - afterEach(async () => { - await afterEachTest(); - }); - - async function testSimpleSendBatch( - useSessions: boolean, - usePartitions: boolean - ): Promise { - const testMessages = []; - testMessages.push(useSessions ? TestMessage.getSessionSample() : TestMessage.getSample()); - testMessages.push(useSessions ? TestMessage.getSessionSample() : TestMessage.getSample()); - - await senderClient.sendBatch(testMessages); - const msgs = await receiverClient.receiveBatch(2); - - should.equal(Array.isArray(msgs), true, "`ReceivedMessages` is not an array"); - should.equal(msgs.length, 2, "Unexpected number of messages"); - - if (testMessages[0].messageId === msgs[0].messageId) { - TestMessage.checkMessageContents(testMessages[0], msgs[0], useSessions, usePartitions); - TestMessage.checkMessageContents(testMessages[1], msgs[1], useSessions, usePartitions); - } else { - TestMessage.checkMessageContents(testMessages[1], msgs[0], useSessions, usePartitions); - TestMessage.checkMessageContents(testMessages[0], msgs[1], useSessions, usePartitions); - } - - await msgs[0].complete(); - await msgs[1].complete(); - - await testPeekMsgsLength(receiverClient, 0); - } - - it("Partitioned Queue: Simple SendBatch", async function(): Promise { - await beforeEachTest(TestClientType.PartitionedQueue); - await testSimpleSendBatch(false, true); - }); - - it("Partitioned Topic: Simple SendBatch", async function(): Promise { - await beforeEachTest(TestClientType.PartitionedSubscription); - await testSimpleSendBatch(false, true); - }); - - it("Unpartitioned Queue: Simple SendBatch #RunInBrowser", async function(): Promise { - await beforeEachTest(TestClientType.UnpartitionedQueue); - await testSimpleSendBatch(false, false); - }); - - it("Unpartitioned Topic: Simple SendBatch", async function(): Promise { - await beforeEachTest(TestClientType.UnpartitionedSubscription); - await testSimpleSendBatch(false, false); - }); - - it("Partitioned Queue with Sessions: Simple SendBatch", async function(): Promise { - await beforeEachTest(TestClientType.PartitionedQueueWithSessions); - await testSimpleSendBatch(true, true); - }); - - it("Partitioned Topic with Sessions: Simple SendBatch", async function(): Promise { - await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); - await testSimpleSendBatch(true, true); - }); - - it("Unpartitioned Queue with Sessions: Simple SendBatch", async function(): Promise { - await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); - await testSimpleSendBatch(true, false); - }); - - it("Unpartitioned Topic with Sessions: Simple SendBatch", async function(): Promise { - await beforeEachTest(TestClientType.UnpartitionedSubscriptionWithSessions); - await testSimpleSendBatch(true, false); - }); - }); + // sendBatch() - Commented + // describe("Simple Send Batch", function(): void { + // afterEach(async () => { + // await afterEachTest(); + // }); + + // async function testSimpleSendBatch( + // useSessions: boolean, + // usePartitions: boolean + // ): Promise { + // const testMessages = []; + // testMessages.push(useSessions ? TestMessage.getSessionSample() : TestMessage.getSample()); + // testMessages.push(useSessions ? TestMessage.getSessionSample() : TestMessage.getSample()); + + // await senderClient.sendBatch(testMessages); + // const msgs = await receiverClient.receiveBatch(2); + + // should.equal(Array.isArray(msgs), true, "`ReceivedMessages` is not an array"); + // should.equal(msgs.length, 2, "Unexpected number of messages"); + + // if (testMessages[0].messageId === msgs[0].messageId) { + // TestMessage.checkMessageContents(testMessages[0], msgs[0], useSessions, usePartitions); + // TestMessage.checkMessageContents(testMessages[1], msgs[1], useSessions, usePartitions); + // } else { + // TestMessage.checkMessageContents(testMessages[1], msgs[0], useSessions, usePartitions); + // TestMessage.checkMessageContents(testMessages[0], msgs[1], useSessions, usePartitions); + // } + + // await msgs[0].complete(); + // await msgs[1].complete(); + + // await testPeekMsgsLength(receiverClient, 0); + // } + + // it("Partitioned Queue: Simple SendBatch", async function(): Promise { + // await beforeEachTest(TestClientType.PartitionedQueue); + // await testSimpleSendBatch(false, true); + // }); + + // it("Partitioned Topic: Simple SendBatch", async function(): Promise { + // await beforeEachTest(TestClientType.PartitionedSubscription); + // await testSimpleSendBatch(false, true); + // }); + + // it("Unpartitioned Queue: Simple SendBatch #RunInBrowser", async function(): Promise { + // await beforeEachTest(TestClientType.UnpartitionedQueue); + // await testSimpleSendBatch(false, false); + // }); + + // it("Unpartitioned Topic: Simple SendBatch", async function(): Promise { + // await beforeEachTest(TestClientType.UnpartitionedSubscription); + // await testSimpleSendBatch(false, false); + // }); + + // it("Partitioned Queue with Sessions: Simple SendBatch", async function(): Promise { + // await beforeEachTest(TestClientType.PartitionedQueueWithSessions); + // await testSimpleSendBatch(true, true); + // }); + + // it("Partitioned Topic with Sessions: Simple SendBatch", async function(): Promise { + // await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); + // await testSimpleSendBatch(true, true); + // }); + + // it("Unpartitioned Queue with Sessions: Simple SendBatch", async function(): Promise { + // await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); + // await testSimpleSendBatch(true, false); + // }); + + // it("Unpartitioned Topic with Sessions: Simple SendBatch", async function(): Promise { + // await beforeEachTest(TestClientType.UnpartitionedSubscriptionWithSessions); + // await testSimpleSendBatch(true, false); + // }); + // }); describe("Schedule single message", function(): void { afterEach(async () => { @@ -636,35 +637,36 @@ describe("send scheduled messages", () => { ); }); - it( - "SendBatch() throws if in the first message, " + testInput.title, - async function(): Promise { - let actualErrorMsg = ""; - await senderClient.sendBatch([testInput.message, { body: "random" }]).catch((err) => { - actualErrorMsg = err.message; - }); - should.equal( - actualErrorMsg, - testInput.expectedErrorMessage, - "Error not thrown as expected" - ); - } - ); - - it( - "SendBatch() throws if in the subsequent message, " + testInput.title, - async function(): Promise { - let actualErrorMsg = ""; - await senderClient.sendBatch([{ body: "random" }, testInput.message]).catch((err) => { - actualErrorMsg = err.message; - }); - should.equal( - actualErrorMsg, - testInput.expectedErrorMessage, - "Error not thrown as expected" - ); - } - ); + // sendBatch() - Commented + // it( + // "SendBatch() throws if in the first message, " + testInput.title, + // async function(): Promise { + // let actualErrorMsg = ""; + // await senderClient.sendBatch([testInput.message, { body: "random" }]).catch((err) => { + // actualErrorMsg = err.message; + // }); + // should.equal( + // actualErrorMsg, + // testInput.expectedErrorMessage, + // "Error not thrown as expected" + // ); + // } + // ); + + // it( + // "SendBatch() throws if in the subsequent message, " + testInput.title, + // async function(): Promise { + // let actualErrorMsg = ""; + // await senderClient.sendBatch([{ body: "random" }, testInput.message]).catch((err) => { + // actualErrorMsg = err.message; + // }); + // should.equal( + // actualErrorMsg, + // testInput.expectedErrorMessage, + // "Error not thrown as expected" + // ); + // } + // ); it("ScheduleMessage() throws if " + testInput.title, async function(): Promise { let actualErrorMsg = ""; diff --git a/sdk/servicebus/service-bus/test/sendBatch.spec.ts b/sdk/servicebus/service-bus/test/sendBatch.spec.ts new file mode 100644 index 000000000000..94e68cf4aa6e --- /dev/null +++ b/sdk/servicebus/service-bus/test/sendBatch.spec.ts @@ -0,0 +1,642 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +import chai from "chai"; +const should = chai.should(); +import chaiAsPromised from "chai-as-promised"; +chai.use(chaiAsPromised); +import { ServiceBusMessage } from "../src"; +import { TestClientType } from "./utils/testUtils"; +import { + ServiceBusClientForTests, + createServiceBusClientForTests, + EntityName +} from "./utils/testutils2"; +import { Sender } from "../src/sender"; + +describe("Send Batch", () => { + let senderClient: Sender; + let serviceBusClient: ServiceBusClientForTests; + + let entityNames: EntityName; + + before(() => { + serviceBusClient = createServiceBusClientForTests(); + }); + + after(() => { + return serviceBusClient.test.after(); + }); + + async function beforeEachTest(entityType: TestClientType): Promise { + entityNames = await serviceBusClient.test.createTestEntities(entityType); + + senderClient = serviceBusClient.test.addToCleanup( + serviceBusClient.getSender(entityNames.queue ?? entityNames.topic!) + ); + } + + async function afterEachTest(): Promise { + await senderClient.close(); + } + + describe("Send multiple homogeneous messages - size > max_batch_size_allowed", function(): void { + afterEach(async () => { + await afterEachTest(); + }); + + function prepareMessages(useSessions: boolean): ServiceBusMessage[] { + const messagesToSend: ServiceBusMessage[] = []; + for (let i = 0; i < 1000; i++) { + messagesToSend.push({ + body: Buffer.alloc(2000), + messageId: `message ${i}`, + sessionId: useSessions ? `someSession` : undefined + }); + } + return messagesToSend; + } + + async function testSendBatch( + useSessions: boolean, + // Max batch size + maxSizeInBytes?: number + ): Promise { + // Prepare messages to send + const messagesToSend = prepareMessages(useSessions); + const sentMessages: ServiceBusMessage[] = []; + const batchMessage = await senderClient.createBatch({ maxSizeInBytes }); + + for (const messageToSend of messagesToSend) { + const batchHasCapacity = batchMessage.tryAdd(messageToSend); + if (!batchHasCapacity) { + break; + } else { + sentMessages.push(messageToSend); + } + } + await senderClient.sendBatch(batchMessage); + // receive all the messages in receive and delete mode + await serviceBusClient.test.verifyAndDeleteAllSentMessages( + entityNames, + useSessions, + sentMessages + ); + } + + it("Partitioned Queue: SendBatch", async function(): Promise { + await beforeEachTest(TestClientType.PartitionedQueue); + await testSendBatch(false); + }); + + it("Partitioned Topic: SendBatch", async function(): Promise { + await beforeEachTest(TestClientType.PartitionedSubscription); + await testSendBatch(false); + }); + + it("Unpartitioned Queue: SendBatch #RunInBrowser", async function(): Promise { + await beforeEachTest(TestClientType.UnpartitionedQueue); + await testSendBatch(false); + }); + + it("Unpartitioned Topic: SendBatch", async function(): Promise { + await beforeEachTest(TestClientType.UnpartitionedSubscription); + await testSendBatch(false); + }); + + it("Partitioned Queue with Sessions: SendBatch", async function(): Promise { + await beforeEachTest(TestClientType.PartitionedQueueWithSessions); + await testSendBatch(true); + }); + + it("Partitioned Topic with Sessions: SendBatch", async function(): Promise { + await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); + await testSendBatch(true); + }); + + it("Unpartitioned Queue with Sessions: SendBatch", async function(): Promise { + await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); + await testSendBatch(true); + }); + + it("Unpartitioned Topic with Sessions: SendBatch", async function(): Promise { + await beforeEachTest(TestClientType.UnpartitionedSubscriptionWithSessions); + await testSendBatch(true); + }); + }); + + describe("Send multiple homogeneous messages - Multiple Sessions - size > max_batch_size_allowed", function(): void { + afterEach(async () => { + await afterEachTest(); + }); + + function prepareMessages(useSessions: boolean): ServiceBusMessage[] { + const messagesToSend: ServiceBusMessage[] = []; + for (let i = 0; i < 1000; i++) { + messagesToSend.push({ + body: Buffer.alloc(2000), + messageId: `message ${i}`, + sessionId: useSessions ? `someSession ${i}` : undefined + }); + } + return messagesToSend; + } + + async function testSendBatch( + useSessions: boolean, + // Max batch size + maxSizeInBytes?: number + ): Promise { + // Prepare messages to send + const messagesToSend = prepareMessages(useSessions); + const sentMessages: ServiceBusMessage[] = []; + const batchMessage = await senderClient.createBatch({ maxSizeInBytes }); + + for (const messageToSend of messagesToSend) { + const batchHasCapacity = batchMessage.tryAdd(messageToSend); + if (!batchHasCapacity) { + break; + } else { + sentMessages.push(messageToSend); + } + } + await senderClient.sendBatch(batchMessage); + // receive all the messages in receive and delete mode + await serviceBusClient.test.verifyAndDeleteAllSentMessages( + entityNames, + useSessions, + sentMessages + ); + } + + // Not allowed for partitioned entities + /* + it("Partitioned Queue with Sessions: SendBatch", async function(): Promise { + await beforeEachTest(TestClientType.PartitionedQueueWithSessions); + await testSendBatch(true); + }); + + it("Partitioned Topic with Sessions: SendBatch", async function(): Promise { + await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); + await testSendBatch(true); + }); + */ + + it("Unpartitioned Queue with Sessions: SendBatch", async function(): Promise { + await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); + await testSendBatch(true); + }); + + it("Unpartitioned Topic with Sessions: SendBatch", async function(): Promise { + await beforeEachTest(TestClientType.UnpartitionedSubscriptionWithSessions); + await testSendBatch(true); + }); + }); + + describe("Send multiple homogeneous messages - size < max_batch_size_allowed", function(): void { + afterEach(async () => { + await afterEachTest(); + }); + + function prepareMessages(useSessions: boolean): ServiceBusMessage[] { + const messagesToSend: ServiceBusMessage[] = []; + messagesToSend.push({ + body: Buffer.alloc(2000), + messageId: `message-1`, + sessionId: useSessions ? `someSession` : undefined + }); + messagesToSend.push({ + body: Buffer.alloc(2000), + messageId: `message-2`, + sessionId: useSessions ? `someSession` : undefined + }); + messagesToSend.push({ + body: Buffer.alloc(2000), + messageId: `message-3`, + sessionId: useSessions ? `someSession` : undefined + }); + return messagesToSend; + } + + async function testSendBatch( + useSessions: boolean, + // Max batch size + maxSizeInBytes?: number + ): Promise { + // Prepare messages to send + const messagesToSend = prepareMessages(useSessions); + const sentMessages: ServiceBusMessage[] = []; + const batchMessage = await senderClient.createBatch({ maxSizeInBytes }); + + for (const messageToSend of messagesToSend) { + const batchHasCapacity = batchMessage.tryAdd(messageToSend); + if (!batchHasCapacity) { + break; + } else { + sentMessages.push(messageToSend); + } + } + await senderClient.sendBatch(batchMessage); + // receive all the messages in receive and delete mode + await serviceBusClient.test.verifyAndDeleteAllSentMessages( + entityNames, + useSessions, + sentMessages + ); + } + + it("Partitioned Queue: SendBatch", async function(): Promise { + await beforeEachTest(TestClientType.PartitionedQueue); + await testSendBatch(false); + }); + + it("Partitioned Topic: SendBatch", async function(): Promise { + await beforeEachTest(TestClientType.PartitionedSubscription); + await testSendBatch(false); + }); + + it("Unpartitioned Queue: SendBatch #RunInBrowser", async function(): Promise { + await beforeEachTest(TestClientType.UnpartitionedQueue); + await testSendBatch(false); + }); + + it("Unpartitioned Topic: SendBatch", async function(): Promise { + await beforeEachTest(TestClientType.UnpartitionedSubscription); + await testSendBatch(false); + }); + + it("Partitioned Queue with Sessions: SendBatch", async function(): Promise { + await beforeEachTest(TestClientType.PartitionedQueueWithSessions); + await testSendBatch(true); + }); + + it("Partitioned Topic with Sessions: SendBatch", async function(): Promise { + await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); + await testSendBatch(true); + }); + + it("Unpartitioned Queue with Sessions: SendBatch", async function(): Promise { + await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); + await testSendBatch(true); + }); + + it("Unpartitioned Topic with Sessions: SendBatch", async function(): Promise { + await beforeEachTest(TestClientType.UnpartitionedSubscriptionWithSessions); + await testSendBatch(true); + }); + }); + + describe("Send single message - size < max_batch_size_allowed", function(): void { + afterEach(async () => { + await afterEachTest(); + }); + + function prepareMessages(useSessions: boolean): ServiceBusMessage[] { + const messagesToSend: ServiceBusMessage[] = []; + messagesToSend.push({ + body: Buffer.alloc(20000), + messageId: `random-message-id`, + sessionId: useSessions ? `someSession` : undefined + }); + return messagesToSend; + } + + async function testSendBatch( + useSessions: boolean, + // Max batch size + maxSizeInBytes?: number + ): Promise { + // Prepare messages to send + const messagesToSend = prepareMessages(useSessions); + const sentMessages: ServiceBusMessage[] = []; + const batchMessage = await senderClient.createBatch({ maxSizeInBytes }); + + for (const messageToSend of messagesToSend) { + const batchHasCapacity = batchMessage.tryAdd(messageToSend); + if (!batchHasCapacity) { + break; + } else { + sentMessages.push(messageToSend); + } + } + await senderClient.sendBatch(batchMessage); + // receive all the messages in receive and delete mode + await serviceBusClient.test.verifyAndDeleteAllSentMessages( + entityNames, + useSessions, + sentMessages + ); + } + + it("Partitioned Queue: SendBatch", async function(): Promise { + await beforeEachTest(TestClientType.PartitionedQueue); + await testSendBatch(false); + }); + + it("Partitioned Topic: SendBatch", async function(): Promise { + await beforeEachTest(TestClientType.PartitionedSubscription); + await testSendBatch(false); + }); + + it("Unpartitioned Queue: SendBatch #RunInBrowser", async function(): Promise { + await beforeEachTest(TestClientType.UnpartitionedQueue); + await testSendBatch(false); + }); + + it("Unpartitioned Topic: SendBatch", async function(): Promise { + await beforeEachTest(TestClientType.UnpartitionedSubscription); + await testSendBatch(false); + }); + + it("Partitioned Queue with Sessions: SendBatch", async function(): Promise { + await beforeEachTest(TestClientType.PartitionedQueueWithSessions); + await testSendBatch(true); + }); + + it("Partitioned Topic with Sessions: SendBatch", async function(): Promise { + await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); + await testSendBatch(true); + }); + + it("Unpartitioned Queue with Sessions: SendBatch", async function(): Promise { + await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); + await testSendBatch(true); + }); + + it("Unpartitioned Topic with Sessions: SendBatch", async function(): Promise { + await beforeEachTest(TestClientType.UnpartitionedSubscriptionWithSessions); + await testSendBatch(true); + }); + }); + + describe("Send multiple heterogenous messages - size > max_batch_size_allowed", function(): void { + afterEach(async () => { + await afterEachTest(); + }); + + function prepareMessages(useSessions: boolean): ServiceBusMessage[] { + const messagesToSend: ServiceBusMessage[] = []; + messagesToSend.push({ + body: Buffer.alloc(2000), + messageId: `message-1`, + sessionId: useSessions ? `someSession` : undefined + }); + messagesToSend.push({ + body: Buffer.alloc(200000), + messageId: `message-2`, + sessionId: useSessions ? `someSession` : undefined + }); + messagesToSend.push({ + body: Buffer.alloc(40000), + messageId: `message-2`, + sessionId: useSessions ? `someSession` : undefined + }); + messagesToSend.push({ + body: Buffer.alloc(20000), + messageId: `message-3`, + sessionId: useSessions ? `someSession` : undefined + }); + return messagesToSend; + } + + async function testSendBatch( + useSessions: boolean, + // Max batch size + maxSizeInBytes?: number + ): Promise { + // Prepare messages to send + const messagesToSend = prepareMessages(useSessions); + const sentMessages: ServiceBusMessage[] = []; + const batchMessage = await senderClient.createBatch({ maxSizeInBytes }); + + for (const messageToSend of messagesToSend) { + const batchHasCapacity = batchMessage.tryAdd(messageToSend); + if (!batchHasCapacity) { + break; + } else { + sentMessages.push(messageToSend); + } + } + await senderClient.sendBatch(batchMessage); + // receive all the messages in receive and delete mode + await serviceBusClient.test.verifyAndDeleteAllSentMessages( + entityNames, + useSessions, + sentMessages + ); + } + + it("Partitioned Queue: SendBatch", async function(): Promise { + await beforeEachTest(TestClientType.PartitionedQueue); + await testSendBatch(false); + }); + + it("Partitioned Topic: SendBatch", async function(): Promise { + await beforeEachTest(TestClientType.PartitionedSubscription); + await testSendBatch(false); + }); + + it("Unpartitioned Queue: SendBatch #RunInBrowser", async function(): Promise { + await beforeEachTest(TestClientType.UnpartitionedQueue); + await testSendBatch(false); + }); + + it("Unpartitioned Topic: SendBatch", async function(): Promise { + await beforeEachTest(TestClientType.UnpartitionedSubscription); + await testSendBatch(false); + }); + + it("Partitioned Queue with Sessions: SendBatch", async function(): Promise { + await beforeEachTest(TestClientType.PartitionedQueueWithSessions); + await testSendBatch(true); + }); + + it("Partitioned Topic with Sessions: SendBatch", async function(): Promise { + await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); + await testSendBatch(true); + }); + + it("Unpartitioned Queue with Sessions: SendBatch", async function(): Promise { + await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); + await testSendBatch(true); + }); + + it("Unpartitioned Topic with Sessions: SendBatch", async function(): Promise { + await beforeEachTest(TestClientType.UnpartitionedSubscriptionWithSessions); + await testSendBatch(true); + }); + }); + + describe("CreateBatch - parameter maxSizeInBytes > max_batch_size_allowed", function(): void { + afterEach(async () => { + await afterEachTest(); + }); + + function prepareMessages(useSessions: boolean): ServiceBusMessage[] { + const messagesToSend: ServiceBusMessage[] = []; + messagesToSend.push({ + body: Buffer.alloc(2000), + messageId: `message-1`, + sessionId: useSessions ? `someSession` : undefined + }); + messagesToSend.push({ + body: Buffer.alloc(200000), + messageId: `message-2`, + sessionId: useSessions ? `someSession` : undefined + }); + messagesToSend.push({ + body: Buffer.alloc(40000), + messageId: `message-3`, + sessionId: useSessions ? `someSession` : undefined + }); + messagesToSend.push({ + body: Buffer.alloc(20000), + messageId: `message-4`, + sessionId: useSessions ? `someSession` : undefined + }); + return messagesToSend; + } + + async function testSendBatch( + useSessions: boolean, + // Max batch size + maxSizeInBytes?: number + ): Promise { + // Prepare messages to send + const messagesToSend = prepareMessages(useSessions); + const batchMessage = await senderClient.createBatch({ maxSizeInBytes }); + + should.equal( + batchMessage.tryAdd(messagesToSend[0]), + true, + "tryAdd should not have failed for the first message" + ); + should.equal( + batchMessage.tryAdd(messagesToSend[1]), + false, + "tryAdd should have failed for the second message" + ); + should.equal( + batchMessage.tryAdd(messagesToSend[2]), + false, + "tryAdd should have failed for the third message" + ); + should.equal( + batchMessage.tryAdd(messagesToSend[3]), + false, + "tryAdd should have failed for the fourth message" + ); + await senderClient.sendBatch(batchMessage); + // receive all the messages in receive and delete mode + await serviceBusClient.test.verifyAndDeleteAllSentMessages(entityNames, useSessions, [ + messagesToSend[0] + ]); + } + + it("Partitioned Queue: SendBatch", async function(): Promise { + await beforeEachTest(TestClientType.PartitionedQueue); + await testSendBatch(false, 5000); + }); + + it("Partitioned Topic: SendBatch", async function(): Promise { + await beforeEachTest(TestClientType.PartitionedSubscription); + await testSendBatch(false, 5000); + }); + + it("Unpartitioned Queue: SendBatch #RunInBrowser", async function(): Promise { + await beforeEachTest(TestClientType.UnpartitionedQueue); + await testSendBatch(false, 5000); + }); + + it("Unpartitioned Topic: SendBatch", async function(): Promise { + await beforeEachTest(TestClientType.UnpartitionedSubscription); + await testSendBatch(false, 5000); + }); + + it("Partitioned Queue with Sessions: SendBatch", async function(): Promise { + await beforeEachTest(TestClientType.PartitionedQueueWithSessions); + await testSendBatch(true, 5000); + }); + + it("Partitioned Topic with Sessions: SendBatch", async function(): Promise { + await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); + await testSendBatch(true, 5000); + }); + + it("Unpartitioned Queue with Sessions: SendBatch", async function(): Promise { + await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); + await testSendBatch(true, 5000); + }); + + it("Unpartitioned Topic with Sessions: SendBatch", async function(): Promise { + await beforeEachTest(TestClientType.UnpartitionedSubscriptionWithSessions); + await testSendBatch(true, 5000); + }); + }); + + describe("CreateBatch should throw error - parameter maxSizeInBytes > max_batch_size_allowed", function(): void { + afterEach(async () => { + await afterEachTest(); + }); + const maxSizeInBytes = 30000000; + + async function testSendBatch(maxSizeInBytes?: number): Promise { + let errorIsThrown = false; + try { + await senderClient.createBatch({ maxSizeInBytes }); + } catch (error) { + should.equal( + error.message, + `Max message size (${maxSizeInBytes} bytes) is greater than maximum message size (262144 bytes) on the AMQP sender link.`, + "Unexpected error message when tried to create a batch of size > maximum message size." + ); + errorIsThrown = true; + } + should.equal( + errorIsThrown, + true, + "Error is not thrown when tried to create a batch of size > maximum message size." + ); + } + + it("Partitioned Queue: SendBatch", async function(): Promise { + await beforeEachTest(TestClientType.PartitionedQueue); + await testSendBatch(maxSizeInBytes); + }); + + it("Partitioned Topic: SendBatch", async function(): Promise { + await beforeEachTest(TestClientType.PartitionedSubscription); + await testSendBatch(maxSizeInBytes); + }); + + it("Unpartitioned Queue: SendBatch #RunInBrowser", async function(): Promise { + await beforeEachTest(TestClientType.UnpartitionedQueue); + await testSendBatch(maxSizeInBytes); + }); + + it("Unpartitioned Topic: SendBatch", async function(): Promise { + await beforeEachTest(TestClientType.UnpartitionedSubscription); + await testSendBatch(maxSizeInBytes); + }); + + it("Partitioned Queue with Sessions: SendBatch", async function(): Promise { + await beforeEachTest(TestClientType.PartitionedQueueWithSessions); + await testSendBatch(maxSizeInBytes); + }); + + it("Partitioned Topic with Sessions: SendBatch", async function(): Promise { + await beforeEachTest(TestClientType.PartitionedSubscriptionWithSessions); + await testSendBatch(maxSizeInBytes); + }); + + it("Unpartitioned Queue with Sessions: SendBatch", async function(): Promise { + await beforeEachTest(TestClientType.UnpartitionedQueueWithSessions); + await testSendBatch(maxSizeInBytes); + }); + + it("Unpartitioned Topic with Sessions: SendBatch", async function(): Promise { + await beforeEachTest(TestClientType.UnpartitionedSubscriptionWithSessions); + await testSendBatch(maxSizeInBytes); + }); + }); +}); diff --git a/sdk/servicebus/service-bus/test/streamingReceiver.spec.ts b/sdk/servicebus/service-bus/test/streamingReceiver.spec.ts index ce3752afdd1a..c57cf020c026 100644 --- a/sdk/servicebus/service-bus/test/streamingReceiver.spec.ts +++ b/sdk/servicebus/service-bus/test/streamingReceiver.spec.ts @@ -948,7 +948,11 @@ describe("Streaming", () => { async function testConcurrency(maxConcurrentCalls?: number): Promise { const testMessages = [TestMessage.getSample(), TestMessage.getSample()]; - await senderClient.sendBatch(testMessages); + const batchMessageToSend = await senderClient.createBatch(); + testMessages.forEach((message) => { + batchMessageToSend.tryAdd(message); + }); + await senderClient.sendBatch(batchMessageToSend); const settledMsgs: ReceivedMessage[] = []; const receivedMsgs: ReceivedMessage[] = []; diff --git a/sdk/servicebus/service-bus/test/streamingReceiverSessions.spec.ts b/sdk/servicebus/service-bus/test/streamingReceiverSessions.spec.ts index b3268938a0a5..e8a206bedbda 100644 --- a/sdk/servicebus/service-bus/test/streamingReceiverSessions.spec.ts +++ b/sdk/servicebus/service-bus/test/streamingReceiverSessions.spec.ts @@ -844,7 +844,11 @@ describe("Streaming with sessions", () => { } const testMessages = [TestMessage.getSessionSample(), TestMessage.getSessionSample()]; - await senderClient.sendBatch(testMessages); + const batchMessageToSend = await senderClient.createBatch(); + for (const message of testMessages) { + batchMessageToSend.tryAdd(message); + } + await senderClient.sendBatch(batchMessageToSend); const settledMsgs: ReceivedMessageWithLock[] = []; const receivedMsgs: ReceivedMessageWithLock[] = []; @@ -873,7 +877,11 @@ describe("Streaming with sessions", () => { }, processError }, - maxConcurrentCalls ? { maxConcurrentCalls } : {} + maxConcurrentCalls + ? { + maxConcurrentCalls + } + : {} ); await checkWithTimeout(() => settledMsgs.length === 2); diff --git a/sdk/servicebus/service-bus/test/utils/testutils2.ts b/sdk/servicebus/service-bus/test/utils/testutils2.ts index b3215a4e1024..2d91b81ae2b8 100644 --- a/sdk/servicebus/service-bus/test/utils/testutils2.ts +++ b/sdk/servicebus/service-bus/test/utils/testutils2.ts @@ -17,7 +17,11 @@ import * as dotenv from "dotenv"; import { recreateQueue, recreateTopic, recreateSubscription } from "./managementUtils"; import { ServiceBusClientOptions } from "../../src"; import chai from "chai"; -import { ReceivedMessageWithLock, ReceivedMessage } from "../../src/serviceBusMessage"; +import { + ReceivedMessageWithLock, + ReceivedMessage, + ServiceBusMessage +} from "../../src/serviceBusMessage"; dotenv.config(); const env = getEnvVars(); @@ -124,6 +128,8 @@ export async function drainAllMessages(receiver: Receiver<{}>): Promise { await receiver.close(); } +export type EntityName = Omit, "isPartitioned" | "usesSessions">; + export interface ServiceBusClientForTests extends ServiceBusClient { test: ServiceBusTestHelpers; } @@ -142,6 +148,72 @@ export class ServiceBusTestHelpers { await Promise.all(closePromises); } + async verifyAndDeleteAllSentMessages( + entityNames: EntityName, + useSessions: boolean, + sentMessages: ServiceBusMessage[] + ): Promise { + let receiverClient: Receiver | SessionReceiver; + let receivedMsgs: ReceivedMessage[]; + if (!useSessions) { + receiverClient = this.getReceiveAndDeleteReceiver({ + queue: entityNames.queue, + topic: entityNames.topic, + subscription: entityNames.subscription, + usesSessions: false + }); + receivedMsgs = await receiverClient.receiveBatch(sentMessages.length, { + // To Do - Maybe change the maxWaitTime + // Currently set same as numberOfMessages being received + maxWaitTimeSeconds: sentMessages.length + }); + await receiverClient.close(); + } else { + // From the sentMessages array, creating a set of all the `session-id`s + const setOfSessionIds: Set = new Set(); + // numOfMsgsWithSessionId - To keep track of number of messages sent per session in the sent messages + let numOfMsgsWithSessionId: { [sessionId: string]: number } = {}; + sentMessages.forEach((msg) => { + setOfSessionIds.add(msg.sessionId!); + numOfMsgsWithSessionId[msg.sessionId!] = numOfMsgsWithSessionId[msg.sessionId!] + ? numOfMsgsWithSessionId[msg.sessionId!] + 1 + : 1; + }); + // for-loop to receive messages from those `session-id`s + for (const id of setOfSessionIds) { + receiverClient = this.getReceiveAndDeleteReceiver({ + queue: entityNames.queue, + topic: entityNames.topic, + subscription: entityNames.subscription, + usesSessions: true, + sessionId: id + }); + const msgs = await receiverClient.receiveBatch(numOfMsgsWithSessionId[id], { + // To Do - Maybe change the maxWaitTime + // Currently set same as numberOfMessages being received + maxWaitTimeSeconds: numOfMsgsWithSessionId[id] + }); + receivedMsgs = !receivedMsgs! ? msgs : receivedMsgs!.concat(msgs); + await receiverClient.close(); + } + } + should.equal( + sentMessages.length, + receivedMsgs!.length, + "Unexpected number of messages received." + ); + receivedMsgs!.forEach((receivedMessage) => { + sentMessages = sentMessages.filter( + (sentMessage) => + sentMessage.messageId !== receivedMessage.messageId && + sentMessage.body !== receivedMessage.body + // To Do - Can check more properties here other than just messageId and body + ); + }); + should.equal(sentMessages.length, 0, "Unexpected messages received."); + // To Do - Maybe peek into the entity to make sure there are no messages left in the entity + } + async after(): Promise { // TODO: purge any of the dynamically created entities created in `createTestEntities` await this._serviceBusClient.close(); @@ -252,16 +324,19 @@ export class ServiceBusTestHelpers { /** * Gets a receiveAndDelete receiver for the specified `TestClientType` - * NOTE: the underlying receiver may be a `SessionReceiverImpl` + * NOTE: the underlying receiver may be a `SessionReceiverImpl`. + * For sessions, if the sessionId is not provided, SessionReceiver returned from this method is meant only for the default sessionId: `TestMessage.sessionId` * * The receiver created by this method will be cleaned up by `afterEach()` */ getReceiveAndDeleteReceiver( - entityNames: ReturnType + entityNames: Omit, "isPartitioned"> & { + sessionId?: string | undefined; + } ): Receiver { // TODO: we should generate a random ID here - there's no harm in // creating as many sessions as we wish. Some tests will need to change. - const sessionId = TestMessage.sessionId; + const sessionId = entityNames.sessionId ?? TestMessage.sessionId; if (entityNames.usesSessions) { return this.addToCleanup( From aca6b230501ce14f3e91fa336df36185b9db79ac Mon Sep 17 00:00:00 2001 From: chunyu3 Date: Fri, 20 Mar 2020 10:57:12 +0800 Subject: [PATCH 27/28] Generated from 93b740901ffff1df0fe936b9c6a2d9d83961df1d (#7910) Fix typo Co-authored-by: SDK Automation --- .../arm-policyinsights/LICENSE.txt | 2 +- .../arm-policyinsights/src/models/index.ts | 15 +++ .../arm-policyinsights/src/models/mappers.ts | 21 ++++ .../src/operations/policyStates.ts | 103 ++++++++++++++++++ .../src/policyInsightsClientContext.ts | 6 +- 5 files changed, 143 insertions(+), 4 deletions(-) diff --git a/sdk/policyinsights/arm-policyinsights/LICENSE.txt b/sdk/policyinsights/arm-policyinsights/LICENSE.txt index b73b4a1293c3..ea8fb1516028 100644 --- a/sdk/policyinsights/arm-policyinsights/LICENSE.txt +++ b/sdk/policyinsights/arm-policyinsights/LICENSE.txt @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2019 Microsoft +Copyright (c) 2020 Microsoft Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/sdk/policyinsights/arm-policyinsights/src/models/index.ts b/sdk/policyinsights/arm-policyinsights/src/models/index.ts index 9dfba0ad1c33..091f0a349986 100644 --- a/sdk/policyinsights/arm-policyinsights/src/models/index.ts +++ b/sdk/policyinsights/arm-policyinsights/src/models/index.ts @@ -646,6 +646,21 @@ export interface PolicyState { * Policy definition group names. */ policyDefinitionGroupNames?: string[]; + /** + * Evaluated policy definition version. + * **NOTE: This property will not be serialized. It can only be populated by the server.** + */ + readonly policyDefinitionVersion?: string; + /** + * Evaluated policy set definition version. + * **NOTE: This property will not be serialized. It can only be populated by the server.** + */ + readonly policySetDefinitionVersion?: string; + /** + * Evaluated policy assignment version. + * **NOTE: This property will not be serialized. It can only be populated by the server.** + */ + readonly policyAssignmentVersion?: string; /** * Describes unknown properties. The value of an unknown property can be of "any" type. */ diff --git a/sdk/policyinsights/arm-policyinsights/src/models/mappers.ts b/sdk/policyinsights/arm-policyinsights/src/models/mappers.ts index 8dd3e033ae5f..909fcae126c3 100644 --- a/sdk/policyinsights/arm-policyinsights/src/models/mappers.ts +++ b/sdk/policyinsights/arm-policyinsights/src/models/mappers.ts @@ -999,6 +999,27 @@ export const PolicyState: msRest.CompositeMapper = { } } } + }, + policyDefinitionVersion: { + readOnly: true, + serializedName: "policyDefinitionVersion", + type: { + name: "String" + } + }, + policySetDefinitionVersion: { + readOnly: true, + serializedName: "policySetDefinitionVersion", + type: { + name: "String" + } + }, + policyAssignmentVersion: { + readOnly: true, + serializedName: "policyAssignmentVersion", + type: { + name: "String" + } } }, additionalProperties: { diff --git a/sdk/policyinsights/arm-policyinsights/src/operations/policyStates.ts b/sdk/policyinsights/arm-policyinsights/src/operations/policyStates.ts index aee3af18c6d1..6dc820943728 100644 --- a/sdk/policyinsights/arm-policyinsights/src/operations/policyStates.ts +++ b/sdk/policyinsights/arm-policyinsights/src/operations/policyStates.ts @@ -9,6 +9,7 @@ */ import * as msRest from "@azure/ms-rest-js"; +import * as msRestAzure from "@azure/ms-rest-azure-js"; import * as Models from "../models"; import * as Mappers from "../models/policyStatesMappers"; import * as Parameters from "../models/parameters"; @@ -298,6 +299,29 @@ export class PolicyStates { callback) as Promise; } + /** + * Triggers a policy evaluation scan for all the resources under the subscription + * @param subscriptionId Microsoft Azure subscription ID. + * @param [options] The optional parameters + * @returns Promise + */ + triggerSubscriptionEvaluation(subscriptionId: string, options?: msRest.RequestOptionsBase): Promise { + return this.beginTriggerSubscriptionEvaluation(subscriptionId,options) + .then(lroPoller => lroPoller.pollUntilFinished()); + } + + /** + * Triggers a policy evaluation scan for all the resources under the resource group. + * @param subscriptionId Microsoft Azure subscription ID. + * @param resourceGroupName Resource group name. + * @param [options] The optional parameters + * @returns Promise + */ + triggerResourceGroupEvaluation(subscriptionId: string, resourceGroupName: string, options?: msRest.RequestOptionsBase): Promise { + return this.beginTriggerResourceGroupEvaluation(subscriptionId,resourceGroupName,options) + .then(lroPoller => lroPoller.pollUntilFinished()); + } + /** * Queries policy states for the subscription level policy set definition. * @param policyStatesResource The virtual resource under PolicyStates resource type. In a given @@ -603,6 +627,40 @@ export class PolicyStates { summarizeForResourceGroupLevelPolicyAssignmentOperationSpec, callback) as Promise; } + + /** + * Triggers a policy evaluation scan for all the resources under the subscription + * @param subscriptionId Microsoft Azure subscription ID. + * @param [options] The optional parameters + * @returns Promise + */ + beginTriggerSubscriptionEvaluation(subscriptionId: string, options?: msRest.RequestOptionsBase): Promise { + return this.client.sendLRORequest( + { + subscriptionId, + options + }, + beginTriggerSubscriptionEvaluationOperationSpec, + options); + } + + /** + * Triggers a policy evaluation scan for all the resources under the resource group. + * @param subscriptionId Microsoft Azure subscription ID. + * @param resourceGroupName Resource group name. + * @param [options] The optional parameters + * @returns Promise + */ + beginTriggerResourceGroupEvaluation(subscriptionId: string, resourceGroupName: string, options?: msRest.RequestOptionsBase): Promise { + return this.client.sendLRORequest( + { + subscriptionId, + resourceGroupName, + options + }, + beginTriggerResourceGroupEvaluationOperationSpec, + options); + } } // Operation Specifications @@ -1101,3 +1159,48 @@ const summarizeForResourceGroupLevelPolicyAssignmentOperationSpec: msRest.Operat }, serializer }; + +const beginTriggerSubscriptionEvaluationOperationSpec: msRest.OperationSpec = { + httpMethod: "POST", + path: "subscriptions/{subscriptionId}/providers/Microsoft.PolicyInsights/policyStates/latest/triggerEvaluation", + urlParameters: [ + Parameters.subscriptionId + ], + queryParameters: [ + Parameters.apiVersion3 + ], + headerParameters: [ + Parameters.acceptLanguage + ], + responses: { + 200: {}, + 202: {}, + default: { + bodyMapper: Mappers.QueryFailure + } + }, + serializer +}; + +const beginTriggerResourceGroupEvaluationOperationSpec: msRest.OperationSpec = { + httpMethod: "POST", + path: "subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.PolicyInsights/policyStates/latest/triggerEvaluation", + urlParameters: [ + Parameters.subscriptionId, + Parameters.resourceGroupName + ], + queryParameters: [ + Parameters.apiVersion3 + ], + headerParameters: [ + Parameters.acceptLanguage + ], + responses: { + 200: {}, + 202: {}, + default: { + bodyMapper: Mappers.QueryFailure + } + }, + serializer +}; diff --git a/sdk/policyinsights/arm-policyinsights/src/policyInsightsClientContext.ts b/sdk/policyinsights/arm-policyinsights/src/policyInsightsClientContext.ts index d5d57f5536e4..75ab8f0ef0ba 100644 --- a/sdk/policyinsights/arm-policyinsights/src/policyInsightsClientContext.ts +++ b/sdk/policyinsights/arm-policyinsights/src/policyInsightsClientContext.ts @@ -31,7 +31,7 @@ export class PolicyInsightsClientContext extends msRestAzure.AzureServiceClient if (!options) { options = {}; } - if (!options.userAgent) { + if(!options.userAgent) { const defaultUserAgent = msRestAzure.getDefaultUserAgentValue(); options.userAgent = `${packageName}/${packageVersion} ${defaultUserAgent}`; } @@ -44,10 +44,10 @@ export class PolicyInsightsClientContext extends msRestAzure.AzureServiceClient this.requestContentType = "application/json; charset=utf-8"; this.credentials = credentials; - if (options.acceptLanguage !== null && options.acceptLanguage !== undefined) { + if(options.acceptLanguage !== null && options.acceptLanguage !== undefined) { this.acceptLanguage = options.acceptLanguage; } - if (options.longRunningOperationRetryTimeout !== null && options.longRunningOperationRetryTimeout !== undefined) { + if(options.longRunningOperationRetryTimeout !== null && options.longRunningOperationRetryTimeout !== undefined) { this.longRunningOperationRetryTimeout = options.longRunningOperationRetryTimeout; } } From badc847aa85b6f627871023bcc3f0fd3fde519d4 Mon Sep 17 00:00:00 2001 From: "openapi-sdkautomation[bot]" <37845953+openapi-sdkautomation[bot]@users.noreply.github.com> Date: Fri, 20 Mar 2020 14:42:34 +0800 Subject: [PATCH 28/28] =?UTF-8?q?[ReleasePR=20@azure/arm-support]=20update?= =?UTF-8?q?=20package=20name=20and=20output=20f=E2=80=A6=20(#7917)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit update package name and output folder in readme.typescript.md Co-authored-by: SDK Automation --- sdk/support/arm-support/LICENSE.txt | 21 + sdk/support/arm-support/README.md | 99 ++ sdk/support/arm-support/package.json | 58 + sdk/support/arm-support/rollup.config.js | 37 + .../arm-support/src/microsoftSupport.ts | 50 + .../src/microsoftSupportContext.ts | 62 + .../src/models/communicationsMappers.ts | 25 + sdk/support/arm-support/src/models/index.ts | 1050 +++++++++++++++++ sdk/support/arm-support/src/models/mappers.ts | 964 +++++++++++++++ .../src/models/operationsMappers.ts | 16 + .../arm-support/src/models/parameters.ts | 117 ++ .../models/problemClassificationsMappers.ts | 15 + .../arm-support/src/models/servicesMappers.ts | 15 + .../src/models/supportTicketsMappers.ts | 27 + .../src/operations/communications.ts | 331 ++++++ .../arm-support/src/operations/index.ts | 15 + .../arm-support/src/operations/operations.ts | 74 ++ .../src/operations/problemClassifications.ts | 142 +++ .../arm-support/src/operations/services.ts | 131 ++ .../src/operations/supportTickets.ts | 415 +++++++ sdk/support/arm-support/tsconfig.json | 19 + 21 files changed, 3683 insertions(+) create mode 100644 sdk/support/arm-support/LICENSE.txt create mode 100644 sdk/support/arm-support/README.md create mode 100644 sdk/support/arm-support/package.json create mode 100644 sdk/support/arm-support/rollup.config.js create mode 100644 sdk/support/arm-support/src/microsoftSupport.ts create mode 100644 sdk/support/arm-support/src/microsoftSupportContext.ts create mode 100644 sdk/support/arm-support/src/models/communicationsMappers.ts create mode 100644 sdk/support/arm-support/src/models/index.ts create mode 100644 sdk/support/arm-support/src/models/mappers.ts create mode 100644 sdk/support/arm-support/src/models/operationsMappers.ts create mode 100644 sdk/support/arm-support/src/models/parameters.ts create mode 100644 sdk/support/arm-support/src/models/problemClassificationsMappers.ts create mode 100644 sdk/support/arm-support/src/models/servicesMappers.ts create mode 100644 sdk/support/arm-support/src/models/supportTicketsMappers.ts create mode 100644 sdk/support/arm-support/src/operations/communications.ts create mode 100644 sdk/support/arm-support/src/operations/index.ts create mode 100644 sdk/support/arm-support/src/operations/operations.ts create mode 100644 sdk/support/arm-support/src/operations/problemClassifications.ts create mode 100644 sdk/support/arm-support/src/operations/services.ts create mode 100644 sdk/support/arm-support/src/operations/supportTickets.ts create mode 100644 sdk/support/arm-support/tsconfig.json diff --git a/sdk/support/arm-support/LICENSE.txt b/sdk/support/arm-support/LICENSE.txt new file mode 100644 index 000000000000..ea8fb1516028 --- /dev/null +++ b/sdk/support/arm-support/LICENSE.txt @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2020 Microsoft + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/sdk/support/arm-support/README.md b/sdk/support/arm-support/README.md new file mode 100644 index 000000000000..967c7b23d6d9 --- /dev/null +++ b/sdk/support/arm-support/README.md @@ -0,0 +1,99 @@ +## Azure MicrosoftSupport SDK for JavaScript + +This package contains an isomorphic SDK for MicrosoftSupport. + +### Currently supported environments + +- Node.js version 6.x.x or higher +- Browser JavaScript + +### How to Install + +```bash +npm install @azure/arm-support +``` + +### How to use + +#### nodejs - Authentication, client creation and list operations as an example written in TypeScript. + +##### Install @azure/ms-rest-nodeauth + +- Please install minimum version of `"@azure/ms-rest-nodeauth": "^3.0.0"`. +```bash +npm install @azure/ms-rest-nodeauth@"^3.0.0" +``` + +##### Sample code + +```typescript +import * as msRest from "@azure/ms-rest-js"; +import * as msRestAzure from "@azure/ms-rest-azure-js"; +import * as msRestNodeAuth from "@azure/ms-rest-nodeauth"; +import { MicrosoftSupport, MicrosoftSupportModels, MicrosoftSupportMappers } from "@azure/arm-support"; +const subscriptionId = process.env["AZURE_SUBSCRIPTION_ID"]; + +msRestNodeAuth.interactiveLogin().then((creds) => { + const client = new MicrosoftSupport(creds, subscriptionId); + client.operations.list().then((result) => { + console.log("The result is:"); + console.log(result); + }); +}).catch((err) => { + console.error(err); +}); +``` + +#### browser - Authentication, client creation and list operations as an example written in JavaScript. + +##### Install @azure/ms-rest-browserauth + +```bash +npm install @azure/ms-rest-browserauth +``` + +##### Sample code + +See https://github.com/Azure/ms-rest-browserauth to learn how to authenticate to Azure in the browser. + +- index.html +```html + + + + @azure/arm-support sample + + + + + + + + +``` + +## Related projects + +- [Microsoft Azure SDK for Javascript](https://github.com/Azure/azure-sdk-for-js) + +![Impressions](https://azure-sdk-impressions.azurewebsites.net/api/impressions/azure-sdk-for-js/sdk/support/arm-support/README.png) diff --git a/sdk/support/arm-support/package.json b/sdk/support/arm-support/package.json new file mode 100644 index 000000000000..bf509c08ccf9 --- /dev/null +++ b/sdk/support/arm-support/package.json @@ -0,0 +1,58 @@ +{ + "name": "@azure/arm-support", + "author": "Microsoft Corporation", + "description": "MicrosoftSupport Library with typescript type definitions for node.js and browser.", + "version": "1.0.0", + "dependencies": { + "@azure/ms-rest-azure-js": "^2.0.1", + "@azure/ms-rest-js": "^2.0.4", + "tslib": "^1.10.0" + }, + "keywords": [ + "node", + "azure", + "typescript", + "browser", + "isomorphic" + ], + "license": "MIT", + "main": "./dist/arm-support.js", + "module": "./esm/microsoftSupport.js", + "types": "./esm/microsoftSupport.d.ts", + "devDependencies": { + "typescript": "^3.5.3", + "rollup": "^1.18.0", + "rollup-plugin-node-resolve": "^5.2.0", + "rollup-plugin-sourcemaps": "^0.4.2", + "uglify-js": "^3.6.0" + }, + "homepage": "https://github.com/Azure/azure-sdk-for-js/tree/master/sdk/support/arm-support", + "repository": { + "type": "git", + "url": "https://github.com/Azure/azure-sdk-for-js.git" + }, + "bugs": { + "url": "https://github.com/Azure/azure-sdk-for-js/issues" + }, + "files": [ + "dist/**/*.js", + "dist/**/*.js.map", + "dist/**/*.d.ts", + "dist/**/*.d.ts.map", + "esm/**/*.js", + "esm/**/*.js.map", + "esm/**/*.d.ts", + "esm/**/*.d.ts.map", + "src/**/*.ts", + "README.md", + "rollup.config.js", + "tsconfig.json" + ], + "scripts": { + "build": "tsc && rollup -c rollup.config.js && npm run minify", + "minify": "uglifyjs -c -m --comments --source-map \"content='./dist/arm-support.js.map'\" -o ./dist/arm-support.min.js ./dist/arm-support.js", + "prepack": "npm install && npm run build" + }, + "sideEffects": false, + "autoPublish": true +} diff --git a/sdk/support/arm-support/rollup.config.js b/sdk/support/arm-support/rollup.config.js new file mode 100644 index 000000000000..d1cdb7454709 --- /dev/null +++ b/sdk/support/arm-support/rollup.config.js @@ -0,0 +1,37 @@ +import rollup from "rollup"; +import nodeResolve from "rollup-plugin-node-resolve"; +import sourcemaps from "rollup-plugin-sourcemaps"; + +/** + * @type {rollup.RollupFileOptions} + */ +const config = { + input: "./esm/microsoftSupport.js", + external: [ + "@azure/ms-rest-js", + "@azure/ms-rest-azure-js" + ], + output: { + file: "./dist/arm-support.js", + format: "umd", + name: "Azure.ArmSupport", + sourcemap: true, + globals: { + "@azure/ms-rest-js": "msRest", + "@azure/ms-rest-azure-js": "msRestAzure" + }, + banner: `/* + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + * + * Code generated by Microsoft (R) AutoRest Code Generator. + * Changes may cause incorrect behavior and will be lost if the code is regenerated. + */` + }, + plugins: [ + nodeResolve({ mainFields: ['module', 'main'] }), + sourcemaps() + ] +}; + +export default config; diff --git a/sdk/support/arm-support/src/microsoftSupport.ts b/sdk/support/arm-support/src/microsoftSupport.ts new file mode 100644 index 000000000000..4b52aeb0dd1e --- /dev/null +++ b/sdk/support/arm-support/src/microsoftSupport.ts @@ -0,0 +1,50 @@ +/* + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for + * license information. + * + * Code generated by Microsoft (R) AutoRest Code Generator. + * Changes may cause incorrect behavior and will be lost if the code is + * regenerated. + */ + +import * as msRest from "@azure/ms-rest-js"; +import * as Models from "./models"; +import * as Mappers from "./models/mappers"; +import * as operations from "./operations"; +import { MicrosoftSupportContext } from "./microsoftSupportContext"; + + +class MicrosoftSupport extends MicrosoftSupportContext { + // Operation groups + operations: operations.Operations; + services: operations.Services; + problemClassifications: operations.ProblemClassifications; + supportTickets: operations.SupportTickets; + communications: operations.Communications; + + /** + * Initializes a new instance of the MicrosoftSupport class. + * @param credentials Credentials needed for the client to connect to Azure. + * @param subscriptionId Azure subscription Id. + * @param [options] The parameter options + */ + constructor(credentials: msRest.ServiceClientCredentials, subscriptionId: string, options?: Models.MicrosoftSupportOptions) { + super(credentials, subscriptionId, options); + this.operations = new operations.Operations(this); + this.services = new operations.Services(this); + this.problemClassifications = new operations.ProblemClassifications(this); + this.supportTickets = new operations.SupportTickets(this); + this.communications = new operations.Communications(this); + } +} + +// Operation Specifications + +export { + MicrosoftSupport, + MicrosoftSupportContext, + Models as MicrosoftSupportModels, + Mappers as MicrosoftSupportMappers +}; +export * from "./operations"; diff --git a/sdk/support/arm-support/src/microsoftSupportContext.ts b/sdk/support/arm-support/src/microsoftSupportContext.ts new file mode 100644 index 000000000000..2ba89d1d59f5 --- /dev/null +++ b/sdk/support/arm-support/src/microsoftSupportContext.ts @@ -0,0 +1,62 @@ +/* + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for + * license information. + * + * Code generated by Microsoft (R) AutoRest Code Generator. + * Changes may cause incorrect behavior and will be lost if the code is + * regenerated. + */ + +import * as Models from "./models"; +import * as msRest from "@azure/ms-rest-js"; +import * as msRestAzure from "@azure/ms-rest-azure-js"; + +const packageName = "@azure/arm-support"; +const packageVersion = "1.0.0"; + +export class MicrosoftSupportContext extends msRestAzure.AzureServiceClient { + credentials: msRest.ServiceClientCredentials; + subscriptionId: string; + apiVersion?: string; + + /** + * Initializes a new instance of the MicrosoftSupport class. + * @param credentials Credentials needed for the client to connect to Azure. + * @param subscriptionId Azure subscription Id. + * @param [options] The parameter options + */ + constructor(credentials: msRest.ServiceClientCredentials, subscriptionId: string, options?: Models.MicrosoftSupportOptions) { + if (credentials == undefined) { + throw new Error('\'credentials\' cannot be null.'); + } + if (subscriptionId == undefined) { + throw new Error('\'subscriptionId\' cannot be null.'); + } + + if (!options) { + options = {}; + } + if(!options.userAgent) { + const defaultUserAgent = msRestAzure.getDefaultUserAgentValue(); + options.userAgent = `${packageName}/${packageVersion} ${defaultUserAgent}`; + } + + super(credentials, options); + + this.apiVersion = '2020-04-01'; + this.acceptLanguage = 'en-US'; + this.longRunningOperationRetryTimeout = 30; + this.baseUri = options.baseUri || this.baseUri || "https://management.azure.com"; + this.requestContentType = "application/json; charset=utf-8"; + this.credentials = credentials; + this.subscriptionId = subscriptionId; + + if(options.acceptLanguage !== null && options.acceptLanguage !== undefined) { + this.acceptLanguage = options.acceptLanguage; + } + if(options.longRunningOperationRetryTimeout !== null && options.longRunningOperationRetryTimeout !== undefined) { + this.longRunningOperationRetryTimeout = options.longRunningOperationRetryTimeout; + } + } +} diff --git a/sdk/support/arm-support/src/models/communicationsMappers.ts b/sdk/support/arm-support/src/models/communicationsMappers.ts new file mode 100644 index 000000000000..1fe4bc2a7c5a --- /dev/null +++ b/sdk/support/arm-support/src/models/communicationsMappers.ts @@ -0,0 +1,25 @@ +/* + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + * + * Code generated by Microsoft (R) AutoRest Code Generator. + * Changes may cause incorrect behavior and will be lost if the code is regenerated. + */ + +export { + BaseResource, + CheckNameAvailabilityInput, + CheckNameAvailabilityOutput, + CommunicationDetails, + CommunicationsListResult, + ContactProfile, + ExceptionResponse, + QuotaChangeRequest, + QuotaTicketDetails, + ServiceError, + ServiceErrorDetail, + ServiceLevelAgreement, + SupportEngineer, + SupportTicketDetails, + TechnicalTicketDetails +} from "../models/mappers"; diff --git a/sdk/support/arm-support/src/models/index.ts b/sdk/support/arm-support/src/models/index.ts new file mode 100644 index 000000000000..0a2c2da2c521 --- /dev/null +++ b/sdk/support/arm-support/src/models/index.ts @@ -0,0 +1,1050 @@ +/* + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + * + * Code generated by Microsoft (R) AutoRest Code Generator. + * Changes may cause incorrect behavior and will be lost if the code is regenerated. + */ + +import { BaseResource, CloudError, AzureServiceClientOptions } from "@azure/ms-rest-azure-js"; +import * as msRest from "@azure/ms-rest-js"; + +export { BaseResource, CloudError }; + +/** + * The object that describes the operation. + */ +export interface OperationDisplay { + /** + * The description of the operation. + * **NOTE: This property will not be serialized. It can only be populated by the server.** + */ + readonly description?: string; + /** + * The action that users can perform, based on their permission level. + * **NOTE: This property will not be serialized. It can only be populated by the server.** + */ + readonly operation?: string; + /** + * Service provider: Microsoft Support. + * **NOTE: This property will not be serialized. It can only be populated by the server.** + */ + readonly provider?: string; + /** + * Resource on which the operation is performed. + * **NOTE: This property will not be serialized. It can only be populated by the server.** + */ + readonly resource?: string; +} + +/** + * The operation supported by Microsoft Support resource provider. + */ +export interface Operation { + /** + * Operation name: {provider}/{resource}/{operation}. + * **NOTE: This property will not be serialized. It can only be populated by the server.** + */ + readonly name?: string; + /** + * The object that describes the operation. + */ + display?: OperationDisplay; +} + +/** + * Object that represents a Service resource. + */ +export interface Service { + /** + * Id of the resource. + * **NOTE: This property will not be serialized. It can only be populated by the server.** + */ + readonly id?: string; + /** + * Name of the resource. + * **NOTE: This property will not be serialized. It can only be populated by the server.** + */ + readonly name?: string; + /** + * Type of the resource 'Microsoft.Support/services'. + * **NOTE: This property will not be serialized. It can only be populated by the server.** + */ + readonly type?: string; + /** + * Localized name of the Azure service. + */ + displayName?: string; + /** + * ARM Resource types. + */ + resourceTypes?: string[]; +} + +/** + * ProblemClassification resource object. + */ +export interface ProblemClassification { + /** + * Id of the resource. + * **NOTE: This property will not be serialized. It can only be populated by the server.** + */ + readonly id?: string; + /** + * Name of the resource. + * **NOTE: This property will not be serialized. It can only be populated by the server.** + */ + readonly name?: string; + /** + * Type of the resource 'Microsoft.Support/problemClassification'. + * **NOTE: This property will not be serialized. It can only be populated by the server.** + */ + readonly type?: string; + /** + * Localized name of problem classification. + */ + displayName?: string; +} + +/** + * Input of CheckNameAvailability API. + */ +export interface CheckNameAvailabilityInput { + /** + * The resource name to validate. + */ + name: string; + /** + * The type of resource. Possible values include: 'Microsoft.Support/supportTickets', + * 'Microsoft.Support/communications' + */ + type: Type; +} + +/** + * Output of check name availability API. + */ +export interface CheckNameAvailabilityOutput { + /** + * Indicates whether the name is available. + * **NOTE: This property will not be serialized. It can only be populated by the server.** + */ + readonly nameAvailable?: boolean; + /** + * The reason why the name is not available. + * **NOTE: This property will not be serialized. It can only be populated by the server.** + */ + readonly reason?: string; + /** + * The detailed error message describing why the name is not available. + * **NOTE: This property will not be serialized. It can only be populated by the server.** + */ + readonly message?: string; +} + +/** + * Contact information associated with the support ticket. + */ +export interface ContactProfile { + /** + * First name. + */ + firstName: string; + /** + * Last name. + */ + lastName: string; + /** + * Preferred contact method. Possible values include: 'email', 'phone' + */ + preferredContactMethod: PreferredContactMethod; + /** + * Primary email address. + */ + primaryEmailAddress: string; + /** + * Additional email addresses listed will be copied on any correspondence about the support + * ticket. + */ + additionalEmailAddresses?: string[]; + /** + * Phone number. This is required if preferred contact method is phone. + */ + phoneNumber?: string; + /** + * Time zone of the user. This is the name of the time zone from [Microsoft Time Zone Index + * Values](https://support.microsoft.com/help/973627/microsoft-time-zone-index-values). + */ + preferredTimeZone: string; + /** + * Country of the user. This is the ISO 3166-1 alpha-3 code. + */ + country: string; + /** + * Preferred language of support from Azure. Support languages vary based on the severity you + * choose for your support ticket. Learn more at [Azure Severity and + * responsiveness](https://azure.microsoft.com/support/plans/response). Use the standard + * language-country code. Valid values are 'en-us' for English, 'zh-hans' for Chinese, 'es-es' + * for Spanish, 'fr-fr' for French, 'ja-jp' for Japanese, 'ko-kr' for Korean, 'ru-ru' for + * Russian, 'pt-br' for Portuguese, 'it-it' for Italian, 'zh-tw' for Chinese and 'de-de' for + * German. + */ + preferredSupportLanguage: string; +} + +/** + * Service Level Agreement details for a support ticket. + */ +export interface ServiceLevelAgreement { + /** + * Time in UTC (ISO 8601 format) when the service level agreement starts. + * **NOTE: This property will not be serialized. It can only be populated by the server.** + */ + readonly startTime?: Date; + /** + * Time in UTC (ISO 8601 format) when the service level agreement expires. + * **NOTE: This property will not be serialized. It can only be populated by the server.** + */ + readonly expirationTime?: Date; + /** + * Service Level Agreement in minutes. + * **NOTE: This property will not be serialized. It can only be populated by the server.** + */ + readonly slaMinutes?: number; +} + +/** + * Support engineer information. + */ +export interface SupportEngineer { + /** + * Email address of the Azure Support engineer assigned to the support ticket. + * **NOTE: This property will not be serialized. It can only be populated by the server.** + */ + readonly emailAddress?: string; +} + +/** + * Additional information for technical support ticket. + */ +export interface TechnicalTicketDetails { + /** + * This is the resource Id of the Azure service resource (For example: A virtual machine resource + * or an HDInsight resource) for which the support ticket is created. + */ + resourceId?: string; +} + +/** + * This property is required for providing the region and new quota limits. + */ +export interface QuotaChangeRequest { + /** + * Region for which the quota increase request is being made. + */ + region?: string; + /** + * Payload of the quota increase request. + */ + payload?: string; +} + +/** + * Additional set of information required for quota increase support ticket for certain quota + * types, e.g.: Virtual machine cores. Get complete details about Quota payload support request + * along with examples at [Support quota request](https://aka.ms/supportrpquotarequestpayload). + */ +export interface QuotaTicketDetails { + /** + * Required for certain quota types when there is a sub type, such as Batch, for which you are + * requesting a quota increase. + */ + quotaChangeRequestSubType?: string; + /** + * Quota change request version. + */ + quotaChangeRequestVersion?: string; + /** + * This property is required for providing the region and new quota limits. + */ + quotaChangeRequests?: QuotaChangeRequest[]; +} + +/** + * Object that represents SupportTicketDetails resource. + */ +export interface SupportTicketDetails extends BaseResource { + /** + * Id of the resource. + * **NOTE: This property will not be serialized. It can only be populated by the server.** + */ + readonly id?: string; + /** + * Name of the resource. + * **NOTE: This property will not be serialized. It can only be populated by the server.** + */ + readonly name?: string; + /** + * Type of the resource 'Microsoft.Support/supportTickets'. + * **NOTE: This property will not be serialized. It can only be populated by the server.** + */ + readonly type?: string; + /** + * System generated support ticket Id that is unique. + */ + supportTicketId?: string; + /** + * Detailed description of the question or issue. + */ + description: string; + /** + * Each Azure service has its own set of issue categories, also known as problem classification. + * This parameter is the unique Id for the type of problem you are experiencing. + */ + problemClassificationId: string; + /** + * Localized name of problem classification. + * **NOTE: This property will not be serialized. It can only be populated by the server.** + */ + readonly problemClassificationDisplayName?: string; + /** + * A value that indicates the urgency of the case, which in turn determines the response time + * according to the service level agreement of the technical support plan you have with Azure. + * Note: 'Highest critical impact' severity is reserved only for our Premium customers. Possible + * values include: 'minimal', 'moderate', 'critical', 'highestcriticalimpact' + */ + severity: SeverityLevel; + /** + * Enrollment Id associated with the support ticket. + * **NOTE: This property will not be serialized. It can only be populated by the server.** + */ + readonly enrollmentId?: string; + /** + * Indicates if this requires a 24x7 response from Azure. + */ + require24X7Response?: boolean; + /** + * Contact information of the user requesting to create a support ticket. + */ + contactDetails: ContactProfile; + /** + * Service Level Agreement information for this support ticket. + */ + serviceLevelAgreement?: ServiceLevelAgreement; + /** + * Information about the support engineer working on this support ticket. + */ + supportEngineer?: SupportEngineer; + /** + * Support plan type associated with the support ticket. + * **NOTE: This property will not be serialized. It can only be populated by the server.** + */ + readonly supportPlanType?: string; + /** + * Title of the support ticket. + */ + title: string; + /** + * Time in UTC (ISO 8601 format) when the problem started. + */ + problemStartTime?: Date; + /** + * This is the resource Id of the Azure service resource associated with the support ticket. + */ + serviceId: string; + /** + * Localized name of the Azure service. + * **NOTE: This property will not be serialized. It can only be populated by the server.** + */ + readonly serviceDisplayName?: string; + /** + * Status of the support ticket. + * **NOTE: This property will not be serialized. It can only be populated by the server.** + */ + readonly status?: string; + /** + * Time in UTC (ISO 8601 format) when the support ticket was created. + * **NOTE: This property will not be serialized. It can only be populated by the server.** + */ + readonly createdDate?: Date; + /** + * Time in UTC (ISO 8601 format) when the support ticket was last modified. + * **NOTE: This property will not be serialized. It can only be populated by the server.** + */ + readonly modifiedDate?: Date; + /** + * Additional ticket details associated with a technical support ticket request. + */ + technicalTicketDetails?: TechnicalTicketDetails; + /** + * Additional ticket details associated with a quota support ticket request. + */ + quotaTicketDetails?: QuotaTicketDetails; +} + +/** + * Object that represents a Communication resource. + */ +export interface CommunicationDetails extends BaseResource { + /** + * Id of the resource. + * **NOTE: This property will not be serialized. It can only be populated by the server.** + */ + readonly id?: string; + /** + * Name of the resource. + * **NOTE: This property will not be serialized. It can only be populated by the server.** + */ + readonly name?: string; + /** + * Type of the resource 'Microsoft.Support/communications'. + * **NOTE: This property will not be serialized. It can only be populated by the server.** + */ + readonly type?: string; + /** + * Communication type. Possible values include: 'web', 'phone' + * **NOTE: This property will not be serialized. It can only be populated by the server.** + */ + readonly communicationType?: CommunicationType; + /** + * Direction of communication. Possible values include: 'inbound', 'outbound' + * **NOTE: This property will not be serialized. It can only be populated by the server.** + */ + readonly communicationDirection?: CommunicationDirection; + /** + * Email address of the sender. This property is required if called by a service principal. + */ + sender?: string; + /** + * Subject of the communication. + */ + subject: string; + /** + * Body of the communication. + */ + body: string; + /** + * Time in UTC (ISO 8601 format) when the communication was created. + * **NOTE: This property will not be serialized. It can only be populated by the server.** + */ + readonly createdDate?: Date; +} + +/** + * The error details. + */ +export interface ServiceErrorDetail { + /** + * The error code. + * **NOTE: This property will not be serialized. It can only be populated by the server.** + */ + readonly code?: string; + /** + * The error message. + * **NOTE: This property will not be serialized. It can only be populated by the server.** + */ + readonly message?: string; + /** + * The target of the error. + */ + target?: string; +} + +/** + * The API error details. + */ +export interface ServiceError { + /** + * The error code. + */ + code?: string; + /** + * The error message. + */ + message?: string; + /** + * The target of the error. + */ + target?: string; + /** + * The list of error details. + * **NOTE: This property will not be serialized. It can only be populated by the server.** + */ + readonly details?: ServiceErrorDetail[]; +} + +/** + * The API error. + */ +export interface ExceptionResponse { + /** + * The API error details. + */ + error?: ServiceError; +} + +/** + * Contact information associated with the support ticket. + */ +export interface UpdateContactProfile { + /** + * First name. + */ + firstName?: string; + /** + * Last name. + */ + lastName?: string; + /** + * Preferred contact method. Possible values include: 'email', 'phone' + */ + preferredContactMethod?: PreferredContactMethod; + /** + * Primary email address. + */ + primaryEmailAddress?: string; + /** + * Email addresses listed will be copied on any correspondence about the support ticket. + */ + additionalEmailAddresses?: string[]; + /** + * Phone number. This is required if preferred contact method is phone. + */ + phoneNumber?: string; + /** + * Time zone of the user. This is the name of the time zone from [Microsoft Time Zone Index + * Values](https://support.microsoft.com/help/973627/microsoft-time-zone-index-values). + */ + preferredTimeZone?: string; + /** + * Country of the user. This is the ISO 3166-1 alpha-3 code. + */ + country?: string; + /** + * Preferred language of support from Azure. Support languages vary based on the severity you + * choose for your support ticket. Learn more at [Azure Severity and + * responsiveness](https://azure.microsoft.com/support/plans/response/). Use the standard + * language-country code. Valid values are 'en-us' for English, 'zh-hans' for Chinese, 'es-es' + * for Spanish, 'fr-fr' for French, 'ja-jp' for Japanese, 'ko-kr' for Korean, 'ru-ru' for + * Russian, 'pt-br' for Portuguese, 'it-it' for Italian, 'zh-tw' for Chinese and 'de-de' for + * German. + */ + preferredSupportLanguage?: string; +} + +/** + * Updates severity, ticket status, and contact details in the support ticket. + */ +export interface UpdateSupportTicket { + /** + * Severity level. Possible values include: 'minimal', 'moderate', 'critical', + * 'highestcriticalimpact' + */ + severity?: SeverityLevel; + /** + * Status to be updated on the ticket. Possible values include: 'open', 'closed' + */ + status?: Status; + /** + * Contact details to be updated on the support ticket. + */ + contactDetails?: UpdateContactProfile; +} + +/** + * Optional Parameters. + */ +export interface SupportTicketsListOptionalParams extends msRest.RequestOptionsBase { + /** + * The number of values to return in the collection. Default is 25 and max is 100. + */ + top?: number; + /** + * The filter to apply on the operation. We support 'odata v4.0' filter semantics. [Learn + * more](https://docs.microsoft.com/odata/concepts/queryoptions-overview). _Status_ filter can + * only be used with Equals ('eq') operator. For _CreatedDate_ filter, the supported operators + * are Greater Than ('gt') and Greater Than or Equals ('ge'). When using both filters, combine + * them using the logical 'AND'. + */ + filter?: string; +} + +/** + * Optional Parameters. + */ +export interface CommunicationsListOptionalParams extends msRest.RequestOptionsBase { + /** + * The number of values to return in the collection. Default is 10 and max is 10. + */ + top?: number; + /** + * The filter to apply on the operation. You can filter by communicationType and createdDate + * properties. CommunicationType supports Equals ('eq') operator and createdDate supports Greater + * Than ('gt') and Greater Than or Equals ('ge') operators. You may combine the CommunicationType + * and CreatedDate filters by Logical And ('and') operator. + */ + filter?: string; +} + +/** + * An interface representing MicrosoftSupportOptions. + */ +export interface MicrosoftSupportOptions extends AzureServiceClientOptions { + baseUri?: string; +} + +/** + * @interface + * The list of operations supported by Microsoft Support resource provider. + * @extends Array + */ +export interface OperationsListResult extends Array { +} + +/** + * @interface + * Collection of Service resources. + * @extends Array + */ +export interface ServicesListResult extends Array { +} + +/** + * @interface + * Collection of ProblemClassification resources. + * @extends Array + */ +export interface ProblemClassificationsListResult extends Array { +} + +/** + * @interface + * Object that represents a collection of SupportTicket resources. + * @extends Array + */ +export interface SupportTicketsListResult extends Array { + /** + * The URI to fetch the next page of SupportTicket resources. + */ + nextLink?: string; +} + +/** + * @interface + * Collection of Communication resources. + * @extends Array + */ +export interface CommunicationsListResult extends Array { + /** + * The URI to fetch the next page of Communication resources. + */ + nextLink?: string; +} + +/** + * Defines values for Type. + * Possible values include: 'Microsoft.Support/supportTickets', 'Microsoft.Support/communications' + * @readonly + * @enum {string} + */ +export type Type = 'Microsoft.Support/supportTickets' | 'Microsoft.Support/communications'; + +/** + * Defines values for SeverityLevel. + * Possible values include: 'minimal', 'moderate', 'critical', 'highestcriticalimpact' + * @readonly + * @enum {string} + */ +export type SeverityLevel = 'minimal' | 'moderate' | 'critical' | 'highestcriticalimpact'; + +/** + * Defines values for PreferredContactMethod. + * Possible values include: 'email', 'phone' + * @readonly + * @enum {string} + */ +export type PreferredContactMethod = 'email' | 'phone'; + +/** + * Defines values for CommunicationType. + * Possible values include: 'web', 'phone' + * @readonly + * @enum {string} + */ +export type CommunicationType = 'web' | 'phone'; + +/** + * Defines values for CommunicationDirection. + * Possible values include: 'inbound', 'outbound' + * @readonly + * @enum {string} + */ +export type CommunicationDirection = 'inbound' | 'outbound'; + +/** + * Defines values for Status. + * Possible values include: 'open', 'closed' + * @readonly + * @enum {string} + */ +export type Status = 'open' | 'closed'; + +/** + * Contains response data for the list operation. + */ +export type OperationsListResponse = OperationsListResult & { + /** + * The underlying HTTP response. + */ + _response: msRest.HttpResponse & { + /** + * The response body as text (string format) + */ + bodyAsText: string; + + /** + * The response body as parsed JSON or XML + */ + parsedBody: OperationsListResult; + }; +}; + +/** + * Contains response data for the list operation. + */ +export type ServicesListResponse = ServicesListResult & { + /** + * The underlying HTTP response. + */ + _response: msRest.HttpResponse & { + /** + * The response body as text (string format) + */ + bodyAsText: string; + + /** + * The response body as parsed JSON or XML + */ + parsedBody: ServicesListResult; + }; +}; + +/** + * Contains response data for the get operation. + */ +export type ServicesGetResponse = Service & { + /** + * The underlying HTTP response. + */ + _response: msRest.HttpResponse & { + /** + * The response body as text (string format) + */ + bodyAsText: string; + + /** + * The response body as parsed JSON or XML + */ + parsedBody: Service; + }; +}; + +/** + * Contains response data for the list operation. + */ +export type ProblemClassificationsListResponse = ProblemClassificationsListResult & { + /** + * The underlying HTTP response. + */ + _response: msRest.HttpResponse & { + /** + * The response body as text (string format) + */ + bodyAsText: string; + + /** + * The response body as parsed JSON or XML + */ + parsedBody: ProblemClassificationsListResult; + }; +}; + +/** + * Contains response data for the get operation. + */ +export type ProblemClassificationsGetResponse = ProblemClassification & { + /** + * The underlying HTTP response. + */ + _response: msRest.HttpResponse & { + /** + * The response body as text (string format) + */ + bodyAsText: string; + + /** + * The response body as parsed JSON or XML + */ + parsedBody: ProblemClassification; + }; +}; + +/** + * Contains response data for the checkNameAvailability operation. + */ +export type SupportTicketsCheckNameAvailabilityResponse = CheckNameAvailabilityOutput & { + /** + * The underlying HTTP response. + */ + _response: msRest.HttpResponse & { + /** + * The response body as text (string format) + */ + bodyAsText: string; + + /** + * The response body as parsed JSON or XML + */ + parsedBody: CheckNameAvailabilityOutput; + }; +}; + +/** + * Contains response data for the list operation. + */ +export type SupportTicketsListResponse = SupportTicketsListResult & { + /** + * The underlying HTTP response. + */ + _response: msRest.HttpResponse & { + /** + * The response body as text (string format) + */ + bodyAsText: string; + + /** + * The response body as parsed JSON or XML + */ + parsedBody: SupportTicketsListResult; + }; +}; + +/** + * Contains response data for the get operation. + */ +export type SupportTicketsGetResponse = SupportTicketDetails & { + /** + * The underlying HTTP response. + */ + _response: msRest.HttpResponse & { + /** + * The response body as text (string format) + */ + bodyAsText: string; + + /** + * The response body as parsed JSON or XML + */ + parsedBody: SupportTicketDetails; + }; +}; + +/** + * Contains response data for the update operation. + */ +export type SupportTicketsUpdateResponse = SupportTicketDetails & { + /** + * The underlying HTTP response. + */ + _response: msRest.HttpResponse & { + /** + * The response body as text (string format) + */ + bodyAsText: string; + + /** + * The response body as parsed JSON or XML + */ + parsedBody: SupportTicketDetails; + }; +}; + +/** + * Contains response data for the create operation. + */ +export type SupportTicketsCreateResponse = SupportTicketDetails & { + /** + * The underlying HTTP response. + */ + _response: msRest.HttpResponse & { + /** + * The response body as text (string format) + */ + bodyAsText: string; + + /** + * The response body as parsed JSON or XML + */ + parsedBody: SupportTicketDetails; + }; +}; + +/** + * Contains response data for the beginCreate operation. + */ +export type SupportTicketsBeginCreateResponse = SupportTicketDetails & { + /** + * The underlying HTTP response. + */ + _response: msRest.HttpResponse & { + /** + * The response body as text (string format) + */ + bodyAsText: string; + + /** + * The response body as parsed JSON or XML + */ + parsedBody: SupportTicketDetails; + }; +}; + +/** + * Contains response data for the listNext operation. + */ +export type SupportTicketsListNextResponse = SupportTicketsListResult & { + /** + * The underlying HTTP response. + */ + _response: msRest.HttpResponse & { + /** + * The response body as text (string format) + */ + bodyAsText: string; + + /** + * The response body as parsed JSON or XML + */ + parsedBody: SupportTicketsListResult; + }; +}; + +/** + * Contains response data for the checkNameAvailability operation. + */ +export type CommunicationsCheckNameAvailabilityResponse = CheckNameAvailabilityOutput & { + /** + * The underlying HTTP response. + */ + _response: msRest.HttpResponse & { + /** + * The response body as text (string format) + */ + bodyAsText: string; + + /** + * The response body as parsed JSON or XML + */ + parsedBody: CheckNameAvailabilityOutput; + }; +}; + +/** + * Contains response data for the list operation. + */ +export type CommunicationsListResponse = CommunicationsListResult & { + /** + * The underlying HTTP response. + */ + _response: msRest.HttpResponse & { + /** + * The response body as text (string format) + */ + bodyAsText: string; + + /** + * The response body as parsed JSON or XML + */ + parsedBody: CommunicationsListResult; + }; +}; + +/** + * Contains response data for the get operation. + */ +export type CommunicationsGetResponse = CommunicationDetails & { + /** + * The underlying HTTP response. + */ + _response: msRest.HttpResponse & { + /** + * The response body as text (string format) + */ + bodyAsText: string; + + /** + * The response body as parsed JSON or XML + */ + parsedBody: CommunicationDetails; + }; +}; + +/** + * Contains response data for the create operation. + */ +export type CommunicationsCreateResponse = CommunicationDetails & { + /** + * The underlying HTTP response. + */ + _response: msRest.HttpResponse & { + /** + * The response body as text (string format) + */ + bodyAsText: string; + + /** + * The response body as parsed JSON or XML + */ + parsedBody: CommunicationDetails; + }; +}; + +/** + * Contains response data for the beginCreate operation. + */ +export type CommunicationsBeginCreateResponse = CommunicationDetails & { + /** + * The underlying HTTP response. + */ + _response: msRest.HttpResponse & { + /** + * The response body as text (string format) + */ + bodyAsText: string; + + /** + * The response body as parsed JSON or XML + */ + parsedBody: CommunicationDetails; + }; +}; + +/** + * Contains response data for the listNext operation. + */ +export type CommunicationsListNextResponse = CommunicationsListResult & { + /** + * The underlying HTTP response. + */ + _response: msRest.HttpResponse & { + /** + * The response body as text (string format) + */ + bodyAsText: string; + + /** + * The response body as parsed JSON or XML + */ + parsedBody: CommunicationsListResult; + }; +}; diff --git a/sdk/support/arm-support/src/models/mappers.ts b/sdk/support/arm-support/src/models/mappers.ts new file mode 100644 index 000000000000..548fdd7e7103 --- /dev/null +++ b/sdk/support/arm-support/src/models/mappers.ts @@ -0,0 +1,964 @@ +/* + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + * + * Code generated by Microsoft (R) AutoRest Code Generator. + * Changes may cause incorrect behavior and will be lost if the code is regenerated. + */ + +import { CloudErrorMapper, BaseResourceMapper } from "@azure/ms-rest-azure-js"; +import * as msRest from "@azure/ms-rest-js"; + +export const CloudError = CloudErrorMapper; +export const BaseResource = BaseResourceMapper; + +export const OperationDisplay: msRest.CompositeMapper = { + serializedName: "Operation_display", + type: { + name: "Composite", + className: "OperationDisplay", + modelProperties: { + description: { + readOnly: true, + serializedName: "description", + type: { + name: "String" + } + }, + operation: { + readOnly: true, + serializedName: "operation", + type: { + name: "String" + } + }, + provider: { + readOnly: true, + serializedName: "provider", + type: { + name: "String" + } + }, + resource: { + readOnly: true, + serializedName: "resource", + type: { + name: "String" + } + } + } + } +}; + +export const Operation: msRest.CompositeMapper = { + serializedName: "Operation", + type: { + name: "Composite", + className: "Operation", + modelProperties: { + name: { + readOnly: true, + serializedName: "name", + type: { + name: "String" + } + }, + display: { + serializedName: "display", + type: { + name: "Composite", + className: "OperationDisplay" + } + } + } + } +}; + +export const Service: msRest.CompositeMapper = { + serializedName: "Service", + type: { + name: "Composite", + className: "Service", + modelProperties: { + id: { + readOnly: true, + serializedName: "id", + type: { + name: "String" + } + }, + name: { + readOnly: true, + serializedName: "name", + type: { + name: "String" + } + }, + type: { + readOnly: true, + serializedName: "type", + type: { + name: "String" + } + }, + displayName: { + serializedName: "properties.displayName", + type: { + name: "String" + } + }, + resourceTypes: { + serializedName: "properties.resourceTypes", + type: { + name: "Sequence", + element: { + type: { + name: "String" + } + } + } + } + } + } +}; + +export const ProblemClassification: msRest.CompositeMapper = { + serializedName: "ProblemClassification", + type: { + name: "Composite", + className: "ProblemClassification", + modelProperties: { + id: { + readOnly: true, + serializedName: "id", + type: { + name: "String" + } + }, + name: { + readOnly: true, + serializedName: "name", + type: { + name: "String" + } + }, + type: { + readOnly: true, + serializedName: "type", + type: { + name: "String" + } + }, + displayName: { + serializedName: "properties.displayName", + type: { + name: "String" + } + } + } + } +}; + +export const CheckNameAvailabilityInput: msRest.CompositeMapper = { + serializedName: "CheckNameAvailabilityInput", + type: { + name: "Composite", + className: "CheckNameAvailabilityInput", + modelProperties: { + name: { + required: true, + serializedName: "name", + type: { + name: "String" + } + }, + type: { + required: true, + serializedName: "type", + type: { + name: "Enum", + allowedValues: [ + "Microsoft.Support/supportTickets", + "Microsoft.Support/communications" + ] + } + } + } + } +}; + +export const CheckNameAvailabilityOutput: msRest.CompositeMapper = { + serializedName: "CheckNameAvailabilityOutput", + type: { + name: "Composite", + className: "CheckNameAvailabilityOutput", + modelProperties: { + nameAvailable: { + readOnly: true, + serializedName: "nameAvailable", + type: { + name: "Boolean" + } + }, + reason: { + readOnly: true, + serializedName: "reason", + type: { + name: "String" + } + }, + message: { + readOnly: true, + serializedName: "message", + type: { + name: "String" + } + } + } + } +}; + +export const ContactProfile: msRest.CompositeMapper = { + serializedName: "ContactProfile", + type: { + name: "Composite", + className: "ContactProfile", + modelProperties: { + firstName: { + required: true, + serializedName: "firstName", + type: { + name: "String" + } + }, + lastName: { + required: true, + serializedName: "lastName", + type: { + name: "String" + } + }, + preferredContactMethod: { + required: true, + serializedName: "preferredContactMethod", + type: { + name: "String" + } + }, + primaryEmailAddress: { + required: true, + serializedName: "primaryEmailAddress", + type: { + name: "String" + } + }, + additionalEmailAddresses: { + serializedName: "additionalEmailAddresses", + type: { + name: "Sequence", + element: { + type: { + name: "String" + } + } + } + }, + phoneNumber: { + serializedName: "phoneNumber", + type: { + name: "String" + } + }, + preferredTimeZone: { + required: true, + serializedName: "preferredTimeZone", + type: { + name: "String" + } + }, + country: { + required: true, + serializedName: "country", + type: { + name: "String" + } + }, + preferredSupportLanguage: { + required: true, + serializedName: "preferredSupportLanguage", + type: { + name: "String" + } + } + } + } +}; + +export const ServiceLevelAgreement: msRest.CompositeMapper = { + serializedName: "ServiceLevelAgreement", + type: { + name: "Composite", + className: "ServiceLevelAgreement", + modelProperties: { + startTime: { + readOnly: true, + serializedName: "startTime", + type: { + name: "DateTime" + } + }, + expirationTime: { + readOnly: true, + serializedName: "expirationTime", + type: { + name: "DateTime" + } + }, + slaMinutes: { + readOnly: true, + serializedName: "slaMinutes", + type: { + name: "Number" + } + } + } + } +}; + +export const SupportEngineer: msRest.CompositeMapper = { + serializedName: "SupportEngineer", + type: { + name: "Composite", + className: "SupportEngineer", + modelProperties: { + emailAddress: { + readOnly: true, + serializedName: "emailAddress", + type: { + name: "String" + } + } + } + } +}; + +export const TechnicalTicketDetails: msRest.CompositeMapper = { + serializedName: "TechnicalTicketDetails", + type: { + name: "Composite", + className: "TechnicalTicketDetails", + modelProperties: { + resourceId: { + serializedName: "resourceId", + type: { + name: "String" + } + } + } + } +}; + +export const QuotaChangeRequest: msRest.CompositeMapper = { + serializedName: "QuotaChangeRequest", + type: { + name: "Composite", + className: "QuotaChangeRequest", + modelProperties: { + region: { + serializedName: "region", + type: { + name: "String" + } + }, + payload: { + serializedName: "payload", + type: { + name: "String" + } + } + } + } +}; + +export const QuotaTicketDetails: msRest.CompositeMapper = { + serializedName: "QuotaTicketDetails", + type: { + name: "Composite", + className: "QuotaTicketDetails", + modelProperties: { + quotaChangeRequestSubType: { + serializedName: "quotaChangeRequestSubType", + type: { + name: "String" + } + }, + quotaChangeRequestVersion: { + serializedName: "quotaChangeRequestVersion", + type: { + name: "String" + } + }, + quotaChangeRequests: { + serializedName: "quotaChangeRequests", + type: { + name: "Sequence", + element: { + type: { + name: "Composite", + className: "QuotaChangeRequest" + } + } + } + } + } + } +}; + +export const SupportTicketDetails: msRest.CompositeMapper = { + serializedName: "SupportTicketDetails", + type: { + name: "Composite", + className: "SupportTicketDetails", + modelProperties: { + id: { + readOnly: true, + serializedName: "id", + type: { + name: "String" + } + }, + name: { + readOnly: true, + serializedName: "name", + type: { + name: "String" + } + }, + type: { + readOnly: true, + serializedName: "type", + type: { + name: "String" + } + }, + supportTicketId: { + serializedName: "properties.supportTicketId", + type: { + name: "String" + } + }, + description: { + required: true, + serializedName: "properties.description", + type: { + name: "String" + } + }, + problemClassificationId: { + required: true, + serializedName: "properties.problemClassificationId", + type: { + name: "String" + } + }, + problemClassificationDisplayName: { + readOnly: true, + serializedName: "properties.problemClassificationDisplayName", + type: { + name: "String" + } + }, + severity: { + required: true, + serializedName: "properties.severity", + type: { + name: "String" + } + }, + enrollmentId: { + readOnly: true, + serializedName: "properties.enrollmentId", + type: { + name: "String" + } + }, + require24X7Response: { + serializedName: "properties.require24X7Response", + type: { + name: "Boolean" + } + }, + contactDetails: { + required: true, + serializedName: "properties.contactDetails", + type: { + name: "Composite", + className: "ContactProfile" + } + }, + serviceLevelAgreement: { + serializedName: "properties.serviceLevelAgreement", + type: { + name: "Composite", + className: "ServiceLevelAgreement" + } + }, + supportEngineer: { + serializedName: "properties.supportEngineer", + type: { + name: "Composite", + className: "SupportEngineer" + } + }, + supportPlanType: { + readOnly: true, + serializedName: "properties.supportPlanType", + type: { + name: "String" + } + }, + title: { + required: true, + serializedName: "properties.title", + type: { + name: "String" + } + }, + problemStartTime: { + serializedName: "properties.problemStartTime", + type: { + name: "DateTime" + } + }, + serviceId: { + required: true, + serializedName: "properties.serviceId", + type: { + name: "String" + } + }, + serviceDisplayName: { + readOnly: true, + serializedName: "properties.serviceDisplayName", + type: { + name: "String" + } + }, + status: { + readOnly: true, + serializedName: "properties.status", + type: { + name: "String" + } + }, + createdDate: { + readOnly: true, + serializedName: "properties.createdDate", + type: { + name: "DateTime" + } + }, + modifiedDate: { + readOnly: true, + serializedName: "properties.modifiedDate", + type: { + name: "DateTime" + } + }, + technicalTicketDetails: { + serializedName: "properties.technicalTicketDetails", + type: { + name: "Composite", + className: "TechnicalTicketDetails" + } + }, + quotaTicketDetails: { + serializedName: "properties.quotaTicketDetails", + type: { + name: "Composite", + className: "QuotaTicketDetails" + } + } + } + } +}; + +export const CommunicationDetails: msRest.CompositeMapper = { + serializedName: "CommunicationDetails", + type: { + name: "Composite", + className: "CommunicationDetails", + modelProperties: { + id: { + readOnly: true, + serializedName: "id", + type: { + name: "String" + } + }, + name: { + readOnly: true, + serializedName: "name", + type: { + name: "String" + } + }, + type: { + readOnly: true, + serializedName: "type", + type: { + name: "String" + } + }, + communicationType: { + readOnly: true, + serializedName: "properties.communicationType", + type: { + name: "String" + } + }, + communicationDirection: { + readOnly: true, + serializedName: "properties.communicationDirection", + type: { + name: "String" + } + }, + sender: { + serializedName: "properties.sender", + type: { + name: "String" + } + }, + subject: { + required: true, + serializedName: "properties.subject", + type: { + name: "String" + } + }, + body: { + required: true, + serializedName: "properties.body", + type: { + name: "String" + } + }, + createdDate: { + readOnly: true, + serializedName: "properties.createdDate", + type: { + name: "DateTime" + } + } + } + } +}; + +export const ServiceErrorDetail: msRest.CompositeMapper = { + serializedName: "ServiceErrorDetail", + type: { + name: "Composite", + className: "ServiceErrorDetail", + modelProperties: { + code: { + readOnly: true, + serializedName: "code", + type: { + name: "String" + } + }, + message: { + readOnly: true, + serializedName: "message", + type: { + name: "String" + } + }, + target: { + serializedName: "target", + type: { + name: "String" + } + } + } + } +}; + +export const ServiceError: msRest.CompositeMapper = { + serializedName: "ServiceError", + type: { + name: "Composite", + className: "ServiceError", + modelProperties: { + code: { + serializedName: "code", + type: { + name: "String" + } + }, + message: { + serializedName: "message", + type: { + name: "String" + } + }, + target: { + serializedName: "target", + type: { + name: "String" + } + }, + details: { + readOnly: true, + serializedName: "details", + type: { + name: "Sequence", + element: { + type: { + name: "Composite", + className: "ServiceErrorDetail" + } + } + } + } + } + } +}; + +export const ExceptionResponse: msRest.CompositeMapper = { + serializedName: "ExceptionResponse", + type: { + name: "Composite", + className: "ExceptionResponse", + modelProperties: { + error: { + serializedName: "error", + type: { + name: "Composite", + className: "ServiceError" + } + } + } + } +}; + +export const UpdateContactProfile: msRest.CompositeMapper = { + serializedName: "UpdateContactProfile", + type: { + name: "Composite", + className: "UpdateContactProfile", + modelProperties: { + firstName: { + serializedName: "firstName", + type: { + name: "String" + } + }, + lastName: { + serializedName: "lastName", + type: { + name: "String" + } + }, + preferredContactMethod: { + serializedName: "preferredContactMethod", + type: { + name: "String" + } + }, + primaryEmailAddress: { + serializedName: "primaryEmailAddress", + type: { + name: "String" + } + }, + additionalEmailAddresses: { + serializedName: "additionalEmailAddresses", + type: { + name: "Sequence", + element: { + type: { + name: "String" + } + } + } + }, + phoneNumber: { + serializedName: "phoneNumber", + type: { + name: "String" + } + }, + preferredTimeZone: { + serializedName: "preferredTimeZone", + type: { + name: "String" + } + }, + country: { + serializedName: "country", + type: { + name: "String" + } + }, + preferredSupportLanguage: { + serializedName: "preferredSupportLanguage", + type: { + name: "String" + } + } + } + } +}; + +export const UpdateSupportTicket: msRest.CompositeMapper = { + serializedName: "UpdateSupportTicket", + type: { + name: "Composite", + className: "UpdateSupportTicket", + modelProperties: { + severity: { + serializedName: "severity", + type: { + name: "String" + } + }, + status: { + serializedName: "status", + type: { + name: "String" + } + }, + contactDetails: { + serializedName: "contactDetails", + type: { + name: "Composite", + className: "UpdateContactProfile" + } + } + } + } +}; + +export const OperationsListResult: msRest.CompositeMapper = { + serializedName: "OperationsListResult", + type: { + name: "Composite", + className: "OperationsListResult", + modelProperties: { + value: { + serializedName: "", + type: { + name: "Sequence", + element: { + type: { + name: "Composite", + className: "Operation" + } + } + } + } + } + } +}; + +export const ServicesListResult: msRest.CompositeMapper = { + serializedName: "ServicesListResult", + type: { + name: "Composite", + className: "ServicesListResult", + modelProperties: { + value: { + serializedName: "", + type: { + name: "Sequence", + element: { + type: { + name: "Composite", + className: "Service" + } + } + } + } + } + } +}; + +export const ProblemClassificationsListResult: msRest.CompositeMapper = { + serializedName: "ProblemClassificationsListResult", + type: { + name: "Composite", + className: "ProblemClassificationsListResult", + modelProperties: { + value: { + serializedName: "", + type: { + name: "Sequence", + element: { + type: { + name: "Composite", + className: "ProblemClassification" + } + } + } + } + } + } +}; + +export const SupportTicketsListResult: msRest.CompositeMapper = { + serializedName: "SupportTicketsListResult", + type: { + name: "Composite", + className: "SupportTicketsListResult", + modelProperties: { + value: { + serializedName: "", + type: { + name: "Sequence", + element: { + type: { + name: "Composite", + className: "SupportTicketDetails" + } + } + } + }, + nextLink: { + serializedName: "nextLink", + type: { + name: "String" + } + } + } + } +}; + +export const CommunicationsListResult: msRest.CompositeMapper = { + serializedName: "CommunicationsListResult", + type: { + name: "Composite", + className: "CommunicationsListResult", + modelProperties: { + value: { + serializedName: "", + type: { + name: "Sequence", + element: { + type: { + name: "Composite", + className: "CommunicationDetails" + } + } + } + }, + nextLink: { + serializedName: "nextLink", + type: { + name: "String" + } + } + } + } +}; diff --git a/sdk/support/arm-support/src/models/operationsMappers.ts b/sdk/support/arm-support/src/models/operationsMappers.ts new file mode 100644 index 000000000000..b6e72b14c6ce --- /dev/null +++ b/sdk/support/arm-support/src/models/operationsMappers.ts @@ -0,0 +1,16 @@ +/* + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + * + * Code generated by Microsoft (R) AutoRest Code Generator. + * Changes may cause incorrect behavior and will be lost if the code is regenerated. + */ + +export { + ExceptionResponse, + Operation, + OperationDisplay, + OperationsListResult, + ServiceError, + ServiceErrorDetail +} from "../models/mappers"; diff --git a/sdk/support/arm-support/src/models/parameters.ts b/sdk/support/arm-support/src/models/parameters.ts new file mode 100644 index 000000000000..aafb28438615 --- /dev/null +++ b/sdk/support/arm-support/src/models/parameters.ts @@ -0,0 +1,117 @@ +/* + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for + * license information. + * + * Code generated by Microsoft (R) AutoRest Code Generator. + * Changes may cause incorrect behavior and will be lost if the code is + * regenerated. + */ + +import * as msRest from "@azure/ms-rest-js"; + +export const acceptLanguage: msRest.OperationParameter = { + parameterPath: "acceptLanguage", + mapper: { + serializedName: "accept-language", + defaultValue: 'en-US', + type: { + name: "String" + } + } +}; +export const apiVersion: msRest.OperationQueryParameter = { + parameterPath: "apiVersion", + mapper: { + required: true, + serializedName: "api-version", + type: { + name: "String" + } + } +}; +export const communicationName: msRest.OperationURLParameter = { + parameterPath: "communicationName", + mapper: { + required: true, + serializedName: "communicationName", + type: { + name: "String" + } + } +}; +export const filter: msRest.OperationQueryParameter = { + parameterPath: [ + "options", + "filter" + ], + mapper: { + serializedName: "$filter", + type: { + name: "String" + } + } +}; +export const nextPageLink: msRest.OperationURLParameter = { + parameterPath: "nextPageLink", + mapper: { + required: true, + serializedName: "nextLink", + type: { + name: "String" + } + }, + skipEncoding: true +}; +export const problemClassificationName: msRest.OperationURLParameter = { + parameterPath: "problemClassificationName", + mapper: { + required: true, + serializedName: "problemClassificationName", + type: { + name: "String" + } + } +}; +export const serviceName: msRest.OperationURLParameter = { + parameterPath: "serviceName", + mapper: { + required: true, + serializedName: "serviceName", + type: { + name: "String" + } + } +}; +export const subscriptionId: msRest.OperationURLParameter = { + parameterPath: "subscriptionId", + mapper: { + required: true, + serializedName: "subscriptionId", + type: { + name: "String" + } + } +}; +export const supportTicketName: msRest.OperationURLParameter = { + parameterPath: "supportTicketName", + mapper: { + required: true, + serializedName: "supportTicketName", + type: { + name: "String" + } + } +}; +export const top: msRest.OperationQueryParameter = { + parameterPath: [ + "options", + "top" + ], + mapper: { + serializedName: "$top", + type: { + name: "Number" + } + } +}; diff --git a/sdk/support/arm-support/src/models/problemClassificationsMappers.ts b/sdk/support/arm-support/src/models/problemClassificationsMappers.ts new file mode 100644 index 000000000000..0df8bd073f1f --- /dev/null +++ b/sdk/support/arm-support/src/models/problemClassificationsMappers.ts @@ -0,0 +1,15 @@ +/* + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + * + * Code generated by Microsoft (R) AutoRest Code Generator. + * Changes may cause incorrect behavior and will be lost if the code is regenerated. + */ + +export { + ExceptionResponse, + ProblemClassification, + ProblemClassificationsListResult, + ServiceError, + ServiceErrorDetail +} from "../models/mappers"; diff --git a/sdk/support/arm-support/src/models/servicesMappers.ts b/sdk/support/arm-support/src/models/servicesMappers.ts new file mode 100644 index 000000000000..69ec00ba73a6 --- /dev/null +++ b/sdk/support/arm-support/src/models/servicesMappers.ts @@ -0,0 +1,15 @@ +/* + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + * + * Code generated by Microsoft (R) AutoRest Code Generator. + * Changes may cause incorrect behavior and will be lost if the code is regenerated. + */ + +export { + ExceptionResponse, + Service, + ServiceError, + ServiceErrorDetail, + ServicesListResult +} from "../models/mappers"; diff --git a/sdk/support/arm-support/src/models/supportTicketsMappers.ts b/sdk/support/arm-support/src/models/supportTicketsMappers.ts new file mode 100644 index 000000000000..25fe954adb6c --- /dev/null +++ b/sdk/support/arm-support/src/models/supportTicketsMappers.ts @@ -0,0 +1,27 @@ +/* + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + * + * Code generated by Microsoft (R) AutoRest Code Generator. + * Changes may cause incorrect behavior and will be lost if the code is regenerated. + */ + +export { + BaseResource, + CheckNameAvailabilityInput, + CheckNameAvailabilityOutput, + CommunicationDetails, + ContactProfile, + ExceptionResponse, + QuotaChangeRequest, + QuotaTicketDetails, + ServiceError, + ServiceErrorDetail, + ServiceLevelAgreement, + SupportEngineer, + SupportTicketDetails, + SupportTicketsListResult, + TechnicalTicketDetails, + UpdateContactProfile, + UpdateSupportTicket +} from "../models/mappers"; diff --git a/sdk/support/arm-support/src/operations/communications.ts b/sdk/support/arm-support/src/operations/communications.ts new file mode 100644 index 000000000000..ce7693cb8649 --- /dev/null +++ b/sdk/support/arm-support/src/operations/communications.ts @@ -0,0 +1,331 @@ +/* + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for + * license information. + * + * Code generated by Microsoft (R) AutoRest Code Generator. + * Changes may cause incorrect behavior and will be lost if the code is + * regenerated. + */ + +import * as msRest from "@azure/ms-rest-js"; +import * as msRestAzure from "@azure/ms-rest-azure-js"; +import * as Models from "../models"; +import * as Mappers from "../models/communicationsMappers"; +import * as Parameters from "../models/parameters"; +import { MicrosoftSupportContext } from "../microsoftSupportContext"; + +/** Class representing a Communications. */ +export class Communications { + private readonly client: MicrosoftSupportContext; + + /** + * Create a Communications. + * @param {MicrosoftSupportContext} client Reference to the service client. + */ + constructor(client: MicrosoftSupportContext) { + this.client = client; + } + + /** + * Check the availability of a resource name. This API should be used to check the uniqueness of + * the name for adding a new communication to the support ticket. + * @param supportTicketName Support ticket name. + * @param checkNameAvailabilityInput Input to check. + * @param [options] The optional parameters + * @returns Promise + */ + checkNameAvailability(supportTicketName: string, checkNameAvailabilityInput: Models.CheckNameAvailabilityInput, options?: msRest.RequestOptionsBase): Promise; + /** + * @param supportTicketName Support ticket name. + * @param checkNameAvailabilityInput Input to check. + * @param callback The callback + */ + checkNameAvailability(supportTicketName: string, checkNameAvailabilityInput: Models.CheckNameAvailabilityInput, callback: msRest.ServiceCallback): void; + /** + * @param supportTicketName Support ticket name. + * @param checkNameAvailabilityInput Input to check. + * @param options The optional parameters + * @param callback The callback + */ + checkNameAvailability(supportTicketName: string, checkNameAvailabilityInput: Models.CheckNameAvailabilityInput, options: msRest.RequestOptionsBase, callback: msRest.ServiceCallback): void; + checkNameAvailability(supportTicketName: string, checkNameAvailabilityInput: Models.CheckNameAvailabilityInput, options?: msRest.RequestOptionsBase | msRest.ServiceCallback, callback?: msRest.ServiceCallback): Promise { + return this.client.sendOperationRequest( + { + supportTicketName, + checkNameAvailabilityInput, + options + }, + checkNameAvailabilityOperationSpec, + callback) as Promise; + } + + /** + * Lists all communications (attachments not included) for a support ticket.

    You can + * also filter support ticket communications by _CreatedDate_ or _CommunicationType_ using the + * $filter parameter. The only type of communication supported today is _Web_. Output will be a + * paged result with _nextLink_, using which you can retrieve the next set of Communication + * results.

    Support ticket data is available for 12 months after ticket creation. If a + * ticket was created more than 12 months ago, a request for data might cause an error. + * @param supportTicketName Support ticket name. + * @param [options] The optional parameters + * @returns Promise + */ + list(supportTicketName: string, options?: Models.CommunicationsListOptionalParams): Promise; + /** + * @param supportTicketName Support ticket name. + * @param callback The callback + */ + list(supportTicketName: string, callback: msRest.ServiceCallback): void; + /** + * @param supportTicketName Support ticket name. + * @param options The optional parameters + * @param callback The callback + */ + list(supportTicketName: string, options: Models.CommunicationsListOptionalParams, callback: msRest.ServiceCallback): void; + list(supportTicketName: string, options?: Models.CommunicationsListOptionalParams | msRest.ServiceCallback, callback?: msRest.ServiceCallback): Promise { + return this.client.sendOperationRequest( + { + supportTicketName, + options + }, + listOperationSpec, + callback) as Promise; + } + + /** + * Returns communication details for a support ticket. + * @param supportTicketName Support ticket name. + * @param communicationName Communication name. + * @param [options] The optional parameters + * @returns Promise + */ + get(supportTicketName: string, communicationName: string, options?: msRest.RequestOptionsBase): Promise; + /** + * @param supportTicketName Support ticket name. + * @param communicationName Communication name. + * @param callback The callback + */ + get(supportTicketName: string, communicationName: string, callback: msRest.ServiceCallback): void; + /** + * @param supportTicketName Support ticket name. + * @param communicationName Communication name. + * @param options The optional parameters + * @param callback The callback + */ + get(supportTicketName: string, communicationName: string, options: msRest.RequestOptionsBase, callback: msRest.ServiceCallback): void; + get(supportTicketName: string, communicationName: string, options?: msRest.RequestOptionsBase | msRest.ServiceCallback, callback?: msRest.ServiceCallback): Promise { + return this.client.sendOperationRequest( + { + supportTicketName, + communicationName, + options + }, + getOperationSpec, + callback) as Promise; + } + + /** + * Adds a new customer communication to an Azure support ticket. + * @param supportTicketName Support ticket name. + * @param communicationName Communication name. + * @param createCommunicationParameters Communication object. + * @param [options] The optional parameters + * @returns Promise + */ + create(supportTicketName: string, communicationName: string, createCommunicationParameters: Models.CommunicationDetails, options?: msRest.RequestOptionsBase): Promise { + return this.beginCreate(supportTicketName,communicationName,createCommunicationParameters,options) + .then(lroPoller => lroPoller.pollUntilFinished()) as Promise; + } + + /** + * Adds a new customer communication to an Azure support ticket. + * @param supportTicketName Support ticket name. + * @param communicationName Communication name. + * @param createCommunicationParameters Communication object. + * @param [options] The optional parameters + * @returns Promise + */ + beginCreate(supportTicketName: string, communicationName: string, createCommunicationParameters: Models.CommunicationDetails, options?: msRest.RequestOptionsBase): Promise { + return this.client.sendLRORequest( + { + supportTicketName, + communicationName, + createCommunicationParameters, + options + }, + beginCreateOperationSpec, + options); + } + + /** + * Lists all communications (attachments not included) for a support ticket.

    You can + * also filter support ticket communications by _CreatedDate_ or _CommunicationType_ using the + * $filter parameter. The only type of communication supported today is _Web_. Output will be a + * paged result with _nextLink_, using which you can retrieve the next set of Communication + * results.

    Support ticket data is available for 12 months after ticket creation. If a + * ticket was created more than 12 months ago, a request for data might cause an error. + * @param nextPageLink The NextLink from the previous successful call to List operation. + * @param [options] The optional parameters + * @returns Promise + */ + listNext(nextPageLink: string, options?: msRest.RequestOptionsBase): Promise; + /** + * @param nextPageLink The NextLink from the previous successful call to List operation. + * @param callback The callback + */ + listNext(nextPageLink: string, callback: msRest.ServiceCallback): void; + /** + * @param nextPageLink The NextLink from the previous successful call to List operation. + * @param options The optional parameters + * @param callback The callback + */ + listNext(nextPageLink: string, options: msRest.RequestOptionsBase, callback: msRest.ServiceCallback): void; + listNext(nextPageLink: string, options?: msRest.RequestOptionsBase | msRest.ServiceCallback, callback?: msRest.ServiceCallback): Promise { + return this.client.sendOperationRequest( + { + nextPageLink, + options + }, + listNextOperationSpec, + callback) as Promise; + } +} + +// Operation Specifications +const serializer = new msRest.Serializer(Mappers); +const checkNameAvailabilityOperationSpec: msRest.OperationSpec = { + httpMethod: "POST", + path: "subscriptions/{subscriptionId}/providers/Microsoft.Support/supportTickets/{supportTicketName}/checkNameAvailability", + urlParameters: [ + Parameters.supportTicketName, + Parameters.subscriptionId + ], + queryParameters: [ + Parameters.apiVersion + ], + headerParameters: [ + Parameters.acceptLanguage + ], + requestBody: { + parameterPath: "checkNameAvailabilityInput", + mapper: { + ...Mappers.CheckNameAvailabilityInput, + required: true + } + }, + responses: { + 200: { + bodyMapper: Mappers.CheckNameAvailabilityOutput + }, + default: { + bodyMapper: Mappers.ExceptionResponse + } + }, + serializer +}; + +const listOperationSpec: msRest.OperationSpec = { + httpMethod: "GET", + path: "subscriptions/{subscriptionId}/providers/Microsoft.Support/supportTickets/{supportTicketName}/communications", + urlParameters: [ + Parameters.supportTicketName, + Parameters.subscriptionId + ], + queryParameters: [ + Parameters.top, + Parameters.filter, + Parameters.apiVersion + ], + headerParameters: [ + Parameters.acceptLanguage + ], + responses: { + 200: { + bodyMapper: Mappers.CommunicationsListResult + }, + default: { + bodyMapper: Mappers.ExceptionResponse + } + }, + serializer +}; + +const getOperationSpec: msRest.OperationSpec = { + httpMethod: "GET", + path: "subscriptions/{subscriptionId}/providers/Microsoft.Support/supportTickets/{supportTicketName}/communications/{communicationName}", + urlParameters: [ + Parameters.supportTicketName, + Parameters.communicationName, + Parameters.subscriptionId + ], + queryParameters: [ + Parameters.apiVersion + ], + headerParameters: [ + Parameters.acceptLanguage + ], + responses: { + 200: { + bodyMapper: Mappers.CommunicationDetails + }, + default: { + bodyMapper: Mappers.ExceptionResponse + } + }, + serializer +}; + +const beginCreateOperationSpec: msRest.OperationSpec = { + httpMethod: "PUT", + path: "subscriptions/{subscriptionId}/providers/Microsoft.Support/supportTickets/{supportTicketName}/communications/{communicationName}", + urlParameters: [ + Parameters.supportTicketName, + Parameters.communicationName, + Parameters.subscriptionId + ], + queryParameters: [ + Parameters.apiVersion + ], + headerParameters: [ + Parameters.acceptLanguage + ], + requestBody: { + parameterPath: "createCommunicationParameters", + mapper: { + ...Mappers.CommunicationDetails, + required: true + } + }, + responses: { + 200: { + bodyMapper: Mappers.CommunicationDetails + }, + 202: {}, + default: { + bodyMapper: Mappers.ExceptionResponse + } + }, + serializer +}; + +const listNextOperationSpec: msRest.OperationSpec = { + httpMethod: "GET", + baseUrl: "https://management.azure.com", + path: "{nextLink}", + urlParameters: [ + Parameters.nextPageLink + ], + headerParameters: [ + Parameters.acceptLanguage + ], + responses: { + 200: { + bodyMapper: Mappers.CommunicationsListResult + }, + default: { + bodyMapper: Mappers.ExceptionResponse + } + }, + serializer +}; diff --git a/sdk/support/arm-support/src/operations/index.ts b/sdk/support/arm-support/src/operations/index.ts new file mode 100644 index 000000000000..c3dc4a3c928e --- /dev/null +++ b/sdk/support/arm-support/src/operations/index.ts @@ -0,0 +1,15 @@ +/* + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for + * license information. + * + * Code generated by Microsoft (R) AutoRest Code Generator. + * Changes may cause incorrect behavior and will be lost if the code is + * regenerated. + */ + +export * from "./operations"; +export * from "./services"; +export * from "./problemClassifications"; +export * from "./supportTickets"; +export * from "./communications"; diff --git a/sdk/support/arm-support/src/operations/operations.ts b/sdk/support/arm-support/src/operations/operations.ts new file mode 100644 index 000000000000..a92e538c1c82 --- /dev/null +++ b/sdk/support/arm-support/src/operations/operations.ts @@ -0,0 +1,74 @@ +/* + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for + * license information. + * + * Code generated by Microsoft (R) AutoRest Code Generator. + * Changes may cause incorrect behavior and will be lost if the code is + * regenerated. + */ + +import * as msRest from "@azure/ms-rest-js"; +import * as Models from "../models"; +import * as Mappers from "../models/operationsMappers"; +import * as Parameters from "../models/parameters"; +import { MicrosoftSupportContext } from "../microsoftSupportContext"; + +/** Class representing a Operations. */ +export class Operations { + private readonly client: MicrosoftSupportContext; + + /** + * Create a Operations. + * @param {MicrosoftSupportContext} client Reference to the service client. + */ + constructor(client: MicrosoftSupportContext) { + this.client = client; + } + + /** + * This lists all the available Microsoft Support REST API operations. + * @param [options] The optional parameters + * @returns Promise + */ + list(options?: msRest.RequestOptionsBase): Promise; + /** + * @param callback The callback + */ + list(callback: msRest.ServiceCallback): void; + /** + * @param options The optional parameters + * @param callback The callback + */ + list(options: msRest.RequestOptionsBase, callback: msRest.ServiceCallback): void; + list(options?: msRest.RequestOptionsBase | msRest.ServiceCallback, callback?: msRest.ServiceCallback): Promise { + return this.client.sendOperationRequest( + { + options + }, + listOperationSpec, + callback) as Promise; + } +} + +// Operation Specifications +const serializer = new msRest.Serializer(Mappers); +const listOperationSpec: msRest.OperationSpec = { + httpMethod: "GET", + path: "providers/Microsoft.Support/operations", + queryParameters: [ + Parameters.apiVersion + ], + headerParameters: [ + Parameters.acceptLanguage + ], + responses: { + 200: { + bodyMapper: Mappers.OperationsListResult + }, + default: { + bodyMapper: Mappers.ExceptionResponse + } + }, + serializer +}; diff --git a/sdk/support/arm-support/src/operations/problemClassifications.ts b/sdk/support/arm-support/src/operations/problemClassifications.ts new file mode 100644 index 000000000000..a853ea0add74 --- /dev/null +++ b/sdk/support/arm-support/src/operations/problemClassifications.ts @@ -0,0 +1,142 @@ +/* + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for + * license information. + * + * Code generated by Microsoft (R) AutoRest Code Generator. + * Changes may cause incorrect behavior and will be lost if the code is + * regenerated. + */ + +import * as msRest from "@azure/ms-rest-js"; +import * as Models from "../models"; +import * as Mappers from "../models/problemClassificationsMappers"; +import * as Parameters from "../models/parameters"; +import { MicrosoftSupportContext } from "../microsoftSupportContext"; + +/** Class representing a ProblemClassifications. */ +export class ProblemClassifications { + private readonly client: MicrosoftSupportContext; + + /** + * Create a ProblemClassifications. + * @param {MicrosoftSupportContext} client Reference to the service client. + */ + constructor(client: MicrosoftSupportContext) { + this.client = client; + } + + /** + * Lists all the problem classifications (categories) available for a specific Azure service. + * Always use the service and problem classifications obtained programmatically. This practice + * ensures that you always have the most recent set of service and problem classification Ids. + * @param serviceName Name of the Azure service for which the problem classifications need to be + * retrieved. + * @param [options] The optional parameters + * @returns Promise + */ + list(serviceName: string, options?: msRest.RequestOptionsBase): Promise; + /** + * @param serviceName Name of the Azure service for which the problem classifications need to be + * retrieved. + * @param callback The callback + */ + list(serviceName: string, callback: msRest.ServiceCallback): void; + /** + * @param serviceName Name of the Azure service for which the problem classifications need to be + * retrieved. + * @param options The optional parameters + * @param callback The callback + */ + list(serviceName: string, options: msRest.RequestOptionsBase, callback: msRest.ServiceCallback): void; + list(serviceName: string, options?: msRest.RequestOptionsBase | msRest.ServiceCallback, callback?: msRest.ServiceCallback): Promise { + return this.client.sendOperationRequest( + { + serviceName, + options + }, + listOperationSpec, + callback) as Promise; + } + + /** + * Get problem classification details for a specific Azure service. + * @param serviceName Name of the Azure service available for support. + * @param problemClassificationName Name of problem classification. + * @param [options] The optional parameters + * @returns Promise + */ + get(serviceName: string, problemClassificationName: string, options?: msRest.RequestOptionsBase): Promise; + /** + * @param serviceName Name of the Azure service available for support. + * @param problemClassificationName Name of problem classification. + * @param callback The callback + */ + get(serviceName: string, problemClassificationName: string, callback: msRest.ServiceCallback): void; + /** + * @param serviceName Name of the Azure service available for support. + * @param problemClassificationName Name of problem classification. + * @param options The optional parameters + * @param callback The callback + */ + get(serviceName: string, problemClassificationName: string, options: msRest.RequestOptionsBase, callback: msRest.ServiceCallback): void; + get(serviceName: string, problemClassificationName: string, options?: msRest.RequestOptionsBase | msRest.ServiceCallback, callback?: msRest.ServiceCallback): Promise { + return this.client.sendOperationRequest( + { + serviceName, + problemClassificationName, + options + }, + getOperationSpec, + callback) as Promise; + } +} + +// Operation Specifications +const serializer = new msRest.Serializer(Mappers); +const listOperationSpec: msRest.OperationSpec = { + httpMethod: "GET", + path: "providers/Microsoft.Support/services/{serviceName}/problemClassifications", + urlParameters: [ + Parameters.serviceName + ], + queryParameters: [ + Parameters.apiVersion + ], + headerParameters: [ + Parameters.acceptLanguage + ], + responses: { + 200: { + bodyMapper: Mappers.ProblemClassificationsListResult + }, + default: { + bodyMapper: Mappers.ExceptionResponse + } + }, + serializer +}; + +const getOperationSpec: msRest.OperationSpec = { + httpMethod: "GET", + path: "providers/Microsoft.Support/services/{serviceName}/problemClassifications/{problemClassificationName}", + urlParameters: [ + Parameters.serviceName, + Parameters.problemClassificationName + ], + queryParameters: [ + Parameters.apiVersion + ], + headerParameters: [ + Parameters.acceptLanguage + ], + responses: { + 200: { + bodyMapper: Mappers.ProblemClassification + }, + default: { + bodyMapper: Mappers.ExceptionResponse + } + }, + serializer +}; diff --git a/sdk/support/arm-support/src/operations/services.ts b/sdk/support/arm-support/src/operations/services.ts new file mode 100644 index 000000000000..c7d891a03a0d --- /dev/null +++ b/sdk/support/arm-support/src/operations/services.ts @@ -0,0 +1,131 @@ +/* + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for + * license information. + * + * Code generated by Microsoft (R) AutoRest Code Generator. + * Changes may cause incorrect behavior and will be lost if the code is + * regenerated. + */ + +import * as msRest from "@azure/ms-rest-js"; +import * as Models from "../models"; +import * as Mappers from "../models/servicesMappers"; +import * as Parameters from "../models/parameters"; +import { MicrosoftSupportContext } from "../microsoftSupportContext"; + +/** Class representing a Services. */ +export class Services { + private readonly client: MicrosoftSupportContext; + + /** + * Create a Services. + * @param {MicrosoftSupportContext} client Reference to the service client. + */ + constructor(client: MicrosoftSupportContext) { + this.client = client; + } + + /** + * Lists all the Azure services available for support ticket creation. For **Technical** issues, + * select the Service Id that maps to the Azure service/product as displayed in the **Services** + * drop-down list on the Azure portal's [New support + * request](https://portal.azure.com/#blade/Microsoft_Azure_Support/HelpAndSupportBlade/overview) + * page. Always use the service and its corresponding problem classification(s) obtained + * programmatically for support ticket creation. This practice ensures that you always have the + * most recent set of service and problem classification Ids. + * @param [options] The optional parameters + * @returns Promise + */ + list(options?: msRest.RequestOptionsBase): Promise; + /** + * @param callback The callback + */ + list(callback: msRest.ServiceCallback): void; + /** + * @param options The optional parameters + * @param callback The callback + */ + list(options: msRest.RequestOptionsBase, callback: msRest.ServiceCallback): void; + list(options?: msRest.RequestOptionsBase | msRest.ServiceCallback, callback?: msRest.ServiceCallback): Promise { + return this.client.sendOperationRequest( + { + options + }, + listOperationSpec, + callback) as Promise; + } + + /** + * Gets a specific Azure service for support ticket creation. + * @param serviceName Name of the Azure service. + * @param [options] The optional parameters + * @returns Promise + */ + get(serviceName: string, options?: msRest.RequestOptionsBase): Promise; + /** + * @param serviceName Name of the Azure service. + * @param callback The callback + */ + get(serviceName: string, callback: msRest.ServiceCallback): void; + /** + * @param serviceName Name of the Azure service. + * @param options The optional parameters + * @param callback The callback + */ + get(serviceName: string, options: msRest.RequestOptionsBase, callback: msRest.ServiceCallback): void; + get(serviceName: string, options?: msRest.RequestOptionsBase | msRest.ServiceCallback, callback?: msRest.ServiceCallback): Promise { + return this.client.sendOperationRequest( + { + serviceName, + options + }, + getOperationSpec, + callback) as Promise; + } +} + +// Operation Specifications +const serializer = new msRest.Serializer(Mappers); +const listOperationSpec: msRest.OperationSpec = { + httpMethod: "GET", + path: "providers/Microsoft.Support/services", + queryParameters: [ + Parameters.apiVersion + ], + headerParameters: [ + Parameters.acceptLanguage + ], + responses: { + 200: { + bodyMapper: Mappers.ServicesListResult + }, + default: { + bodyMapper: Mappers.ExceptionResponse + } + }, + serializer +}; + +const getOperationSpec: msRest.OperationSpec = { + httpMethod: "GET", + path: "providers/Microsoft.Support/services/{serviceName}", + urlParameters: [ + Parameters.serviceName + ], + queryParameters: [ + Parameters.apiVersion + ], + headerParameters: [ + Parameters.acceptLanguage + ], + responses: { + 200: { + bodyMapper: Mappers.Service + }, + default: { + bodyMapper: Mappers.ExceptionResponse + } + }, + serializer +}; diff --git a/sdk/support/arm-support/src/operations/supportTickets.ts b/sdk/support/arm-support/src/operations/supportTickets.ts new file mode 100644 index 000000000000..b6c2eeb6c063 --- /dev/null +++ b/sdk/support/arm-support/src/operations/supportTickets.ts @@ -0,0 +1,415 @@ +/* + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for + * license information. + * + * Code generated by Microsoft (R) AutoRest Code Generator. + * Changes may cause incorrect behavior and will be lost if the code is + * regenerated. + */ + +import * as msRest from "@azure/ms-rest-js"; +import * as msRestAzure from "@azure/ms-rest-azure-js"; +import * as Models from "../models"; +import * as Mappers from "../models/supportTicketsMappers"; +import * as Parameters from "../models/parameters"; +import { MicrosoftSupportContext } from "../microsoftSupportContext"; + +/** Class representing a SupportTickets. */ +export class SupportTickets { + private readonly client: MicrosoftSupportContext; + + /** + * Create a SupportTickets. + * @param {MicrosoftSupportContext} client Reference to the service client. + */ + constructor(client: MicrosoftSupportContext) { + this.client = client; + } + + /** + * Check the availability of a resource name. This API should be used to check the uniqueness of + * the name for support ticket creation for the selected subscription. + * @param checkNameAvailabilityInput Input to check. + * @param [options] The optional parameters + * @returns Promise + */ + checkNameAvailability(checkNameAvailabilityInput: Models.CheckNameAvailabilityInput, options?: msRest.RequestOptionsBase): Promise; + /** + * @param checkNameAvailabilityInput Input to check. + * @param callback The callback + */ + checkNameAvailability(checkNameAvailabilityInput: Models.CheckNameAvailabilityInput, callback: msRest.ServiceCallback): void; + /** + * @param checkNameAvailabilityInput Input to check. + * @param options The optional parameters + * @param callback The callback + */ + checkNameAvailability(checkNameAvailabilityInput: Models.CheckNameAvailabilityInput, options: msRest.RequestOptionsBase, callback: msRest.ServiceCallback): void; + checkNameAvailability(checkNameAvailabilityInput: Models.CheckNameAvailabilityInput, options?: msRest.RequestOptionsBase | msRest.ServiceCallback, callback?: msRest.ServiceCallback): Promise { + return this.client.sendOperationRequest( + { + checkNameAvailabilityInput, + options + }, + checkNameAvailabilityOperationSpec, + callback) as Promise; + } + + /** + * Lists all the support tickets for an Azure subscription. You can also filter the support tickets + * by _Status_ or _CreatedDate_ using the $filter parameter. Output will be a paged result with + * _nextLink_, using which you can retrieve the next set of support tickets.

    Support + * ticket data is available for 12 months after ticket creation. If a ticket was created more than + * 12 months ago, a request for data might cause an error. + * @param [options] The optional parameters + * @returns Promise + */ + list(options?: Models.SupportTicketsListOptionalParams): Promise; + /** + * @param callback The callback + */ + list(callback: msRest.ServiceCallback): void; + /** + * @param options The optional parameters + * @param callback The callback + */ + list(options: Models.SupportTicketsListOptionalParams, callback: msRest.ServiceCallback): void; + list(options?: Models.SupportTicketsListOptionalParams | msRest.ServiceCallback, callback?: msRest.ServiceCallback): Promise { + return this.client.sendOperationRequest( + { + options + }, + listOperationSpec, + callback) as Promise; + } + + /** + * Get ticket details for an Azure subscription. Support ticket data is available for 12 months + * after ticket creation. If a ticket was created more than 12 months ago, a request for data might + * cause an error. + * @param supportTicketName Support ticket name. + * @param [options] The optional parameters + * @returns Promise + */ + get(supportTicketName: string, options?: msRest.RequestOptionsBase): Promise; + /** + * @param supportTicketName Support ticket name. + * @param callback The callback + */ + get(supportTicketName: string, callback: msRest.ServiceCallback): void; + /** + * @param supportTicketName Support ticket name. + * @param options The optional parameters + * @param callback The callback + */ + get(supportTicketName: string, options: msRest.RequestOptionsBase, callback: msRest.ServiceCallback): void; + get(supportTicketName: string, options?: msRest.RequestOptionsBase | msRest.ServiceCallback, callback?: msRest.ServiceCallback): Promise { + return this.client.sendOperationRequest( + { + supportTicketName, + options + }, + getOperationSpec, + callback) as Promise; + } + + /** + * This API allows you to update the severity level, ticket status, and your contact information in + * the support ticket.

    Note: The severity levels cannot be changed if a support ticket is + * actively being worked upon by an Azure support engineer. In such a case, contact your support + * engineer to request severity update by adding a new communication using the Communications + * API.

    Changing the ticket status to _closed_ is allowed only on an unassigned case. When + * an engineer is actively working on the ticket, send your ticket closure request by sending a + * note to your engineer. + * @param supportTicketName Support ticket name. + * @param updateSupportTicket UpdateSupportTicket object. + * @param [options] The optional parameters + * @returns Promise + */ + update(supportTicketName: string, updateSupportTicket: Models.UpdateSupportTicket, options?: msRest.RequestOptionsBase): Promise; + /** + * @param supportTicketName Support ticket name. + * @param updateSupportTicket UpdateSupportTicket object. + * @param callback The callback + */ + update(supportTicketName: string, updateSupportTicket: Models.UpdateSupportTicket, callback: msRest.ServiceCallback): void; + /** + * @param supportTicketName Support ticket name. + * @param updateSupportTicket UpdateSupportTicket object. + * @param options The optional parameters + * @param callback The callback + */ + update(supportTicketName: string, updateSupportTicket: Models.UpdateSupportTicket, options: msRest.RequestOptionsBase, callback: msRest.ServiceCallback): void; + update(supportTicketName: string, updateSupportTicket: Models.UpdateSupportTicket, options?: msRest.RequestOptionsBase | msRest.ServiceCallback, callback?: msRest.ServiceCallback): Promise { + return this.client.sendOperationRequest( + { + supportTicketName, + updateSupportTicket, + options + }, + updateOperationSpec, + callback) as Promise; + } + + /** + * Creates a new support ticket for Subscription and Service limits (Quota), Technical, Billing, + * and Subscription Management issues for the specified subscription. Learn the + * [prerequisites](https://aka.ms/supportAPI) required to create a support ticket.

    Always + * call the Services and ProblemClassifications API to get the most recent set of services and + * problem categories required for support ticket creation.

    Adding attachments is not + * currently supported via the API. To add a file to an existing support ticket, visit the [Manage + * support + * ticket](https://portal.azure.com/#blade/Microsoft_Azure_Support/HelpAndSupportBlade/managesupportrequest) + * page in the Azure portal, select the support ticket, and use the file upload control to add a + * new file.

    Providing consent to share diagnostic information with Azure support is + * currently not supported via the API. The Azure support engineer working on your ticket will + * reach out to you for consent if your issue requires gathering diagnostic information from your + * Azure resources.

    **Creating a support ticket for on-behalf-of**: Include + * _x-ms-authorization-auxiliary_ header to provide an auxiliary token as per + * [documentation](https://docs.microsoft.com/azure/azure-resource-manager/management/authenticate-multi-tenant). + * The primary token will be from the tenant for whom a support ticket is being raised against the + * subscription, i.e. Cloud solution provider (CSP) customer tenant. The auxiliary token will be + * from the Cloud solution provider (CSP) partner tenant. + * @param supportTicketName Support ticket name. + * @param createSupportTicketParameters Support ticket request payload. + * @param [options] The optional parameters + * @returns Promise + */ + create(supportTicketName: string, createSupportTicketParameters: Models.SupportTicketDetails, options?: msRest.RequestOptionsBase): Promise { + return this.beginCreate(supportTicketName,createSupportTicketParameters,options) + .then(lroPoller => lroPoller.pollUntilFinished()) as Promise; + } + + /** + * Creates a new support ticket for Subscription and Service limits (Quota), Technical, Billing, + * and Subscription Management issues for the specified subscription. Learn the + * [prerequisites](https://aka.ms/supportAPI) required to create a support ticket.

    Always + * call the Services and ProblemClassifications API to get the most recent set of services and + * problem categories required for support ticket creation.

    Adding attachments is not + * currently supported via the API. To add a file to an existing support ticket, visit the [Manage + * support + * ticket](https://portal.azure.com/#blade/Microsoft_Azure_Support/HelpAndSupportBlade/managesupportrequest) + * page in the Azure portal, select the support ticket, and use the file upload control to add a + * new file.

    Providing consent to share diagnostic information with Azure support is + * currently not supported via the API. The Azure support engineer working on your ticket will + * reach out to you for consent if your issue requires gathering diagnostic information from your + * Azure resources.

    **Creating a support ticket for on-behalf-of**: Include + * _x-ms-authorization-auxiliary_ header to provide an auxiliary token as per + * [documentation](https://docs.microsoft.com/azure/azure-resource-manager/management/authenticate-multi-tenant). + * The primary token will be from the tenant for whom a support ticket is being raised against the + * subscription, i.e. Cloud solution provider (CSP) customer tenant. The auxiliary token will be + * from the Cloud solution provider (CSP) partner tenant. + * @param supportTicketName Support ticket name. + * @param createSupportTicketParameters Support ticket request payload. + * @param [options] The optional parameters + * @returns Promise + */ + beginCreate(supportTicketName: string, createSupportTicketParameters: Models.SupportTicketDetails, options?: msRest.RequestOptionsBase): Promise { + return this.client.sendLRORequest( + { + supportTicketName, + createSupportTicketParameters, + options + }, + beginCreateOperationSpec, + options); + } + + /** + * Lists all the support tickets for an Azure subscription. You can also filter the support tickets + * by _Status_ or _CreatedDate_ using the $filter parameter. Output will be a paged result with + * _nextLink_, using which you can retrieve the next set of support tickets.

    Support + * ticket data is available for 12 months after ticket creation. If a ticket was created more than + * 12 months ago, a request for data might cause an error. + * @param nextPageLink The NextLink from the previous successful call to List operation. + * @param [options] The optional parameters + * @returns Promise + */ + listNext(nextPageLink: string, options?: msRest.RequestOptionsBase): Promise; + /** + * @param nextPageLink The NextLink from the previous successful call to List operation. + * @param callback The callback + */ + listNext(nextPageLink: string, callback: msRest.ServiceCallback): void; + /** + * @param nextPageLink The NextLink from the previous successful call to List operation. + * @param options The optional parameters + * @param callback The callback + */ + listNext(nextPageLink: string, options: msRest.RequestOptionsBase, callback: msRest.ServiceCallback): void; + listNext(nextPageLink: string, options?: msRest.RequestOptionsBase | msRest.ServiceCallback, callback?: msRest.ServiceCallback): Promise { + return this.client.sendOperationRequest( + { + nextPageLink, + options + }, + listNextOperationSpec, + callback) as Promise; + } +} + +// Operation Specifications +const serializer = new msRest.Serializer(Mappers); +const checkNameAvailabilityOperationSpec: msRest.OperationSpec = { + httpMethod: "POST", + path: "subscriptions/{subscriptionId}/providers/Microsoft.Support/checkNameAvailability", + urlParameters: [ + Parameters.subscriptionId + ], + queryParameters: [ + Parameters.apiVersion + ], + headerParameters: [ + Parameters.acceptLanguage + ], + requestBody: { + parameterPath: "checkNameAvailabilityInput", + mapper: { + ...Mappers.CheckNameAvailabilityInput, + required: true + } + }, + responses: { + 200: { + bodyMapper: Mappers.CheckNameAvailabilityOutput + }, + default: { + bodyMapper: Mappers.ExceptionResponse + } + }, + serializer +}; + +const listOperationSpec: msRest.OperationSpec = { + httpMethod: "GET", + path: "subscriptions/{subscriptionId}/providers/Microsoft.Support/supportTickets", + urlParameters: [ + Parameters.subscriptionId + ], + queryParameters: [ + Parameters.top, + Parameters.filter, + Parameters.apiVersion + ], + headerParameters: [ + Parameters.acceptLanguage + ], + responses: { + 200: { + bodyMapper: Mappers.SupportTicketsListResult + }, + default: { + bodyMapper: Mappers.ExceptionResponse + } + }, + serializer +}; + +const getOperationSpec: msRest.OperationSpec = { + httpMethod: "GET", + path: "subscriptions/{subscriptionId}/providers/Microsoft.Support/supportTickets/{supportTicketName}", + urlParameters: [ + Parameters.supportTicketName, + Parameters.subscriptionId + ], + queryParameters: [ + Parameters.apiVersion + ], + headerParameters: [ + Parameters.acceptLanguage + ], + responses: { + 200: { + bodyMapper: Mappers.SupportTicketDetails + }, + default: { + bodyMapper: Mappers.ExceptionResponse + } + }, + serializer +}; + +const updateOperationSpec: msRest.OperationSpec = { + httpMethod: "PATCH", + path: "subscriptions/{subscriptionId}/providers/Microsoft.Support/supportTickets/{supportTicketName}", + urlParameters: [ + Parameters.supportTicketName, + Parameters.subscriptionId + ], + queryParameters: [ + Parameters.apiVersion + ], + headerParameters: [ + Parameters.acceptLanguage + ], + requestBody: { + parameterPath: "updateSupportTicket", + mapper: { + ...Mappers.UpdateSupportTicket, + required: true + } + }, + responses: { + 200: { + bodyMapper: Mappers.SupportTicketDetails + }, + default: { + bodyMapper: Mappers.ExceptionResponse + } + }, + serializer +}; + +const beginCreateOperationSpec: msRest.OperationSpec = { + httpMethod: "PUT", + path: "subscriptions/{subscriptionId}/providers/Microsoft.Support/supportTickets/{supportTicketName}", + urlParameters: [ + Parameters.supportTicketName, + Parameters.subscriptionId + ], + queryParameters: [ + Parameters.apiVersion + ], + headerParameters: [ + Parameters.acceptLanguage + ], + requestBody: { + parameterPath: "createSupportTicketParameters", + mapper: { + ...Mappers.SupportTicketDetails, + required: true + } + }, + responses: { + 200: { + bodyMapper: Mappers.SupportTicketDetails + }, + 202: {}, + default: { + bodyMapper: Mappers.ExceptionResponse + } + }, + serializer +}; + +const listNextOperationSpec: msRest.OperationSpec = { + httpMethod: "GET", + baseUrl: "https://management.azure.com", + path: "{nextLink}", + urlParameters: [ + Parameters.nextPageLink + ], + headerParameters: [ + Parameters.acceptLanguage + ], + responses: { + 200: { + bodyMapper: Mappers.SupportTicketsListResult + }, + default: { + bodyMapper: Mappers.ExceptionResponse + } + }, + serializer +}; diff --git a/sdk/support/arm-support/tsconfig.json b/sdk/support/arm-support/tsconfig.json new file mode 100644 index 000000000000..422b584abd5e --- /dev/null +++ b/sdk/support/arm-support/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "module": "es6", + "moduleResolution": "node", + "strict": true, + "target": "es5", + "sourceMap": true, + "declarationMap": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "forceConsistentCasingInFileNames": true, + "lib": ["es6", "dom"], + "declaration": true, + "outDir": "./esm", + "importHelpers": true + }, + "include": ["./src/**/*.ts"], + "exclude": ["node_modules"] +}