From b5d9fe5f866d88b20aca8dc38017127371f64021 Mon Sep 17 00:00:00 2001 From: XiaoningLiu Date: Tue, 26 May 2020 17:44:53 +0800 Subject: [PATCH 1/5] [Storage] Quick query --- common/config/rush/pnpm-lock.yaml | 117 ++-- sdk/storage/storage-blob/CHANGELOG.md | 4 + sdk/storage/storage-blob/package.json | 3 +- .../src/BlobQueryResponse.browser.ts | 499 +++++++++++++++++ .../storage-blob/src/BlobQueryResponse.ts | 506 ++++++++++++++++++ sdk/storage/storage-blob/src/Clients.ts | 259 ++++++++- .../storage-blob/src/generatedModels.ts | 1 + .../src/utils/BlobQuickQueryStream.ts | 165 ++++++ .../storage-blob/src/utils/utils.common.ts | 47 +- .../storage-blob/test/node/blobclient.spec.ts | 303 ++++++++++- sdk/storage/storage-blob/test/utils/index.ts | 22 +- .../storage-internal-avro/package.json | 4 +- 12 files changed, 1860 insertions(+), 70 deletions(-) create mode 100644 sdk/storage/storage-blob/src/BlobQueryResponse.browser.ts create mode 100644 sdk/storage/storage-blob/src/BlobQueryResponse.ts create mode 100644 sdk/storage/storage-blob/src/utils/BlobQuickQueryStream.ts diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index c9cfa0167905..b79e36ff9d82 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -107,6 +107,14 @@ packages: dev: false resolution: integrity: sha512-pkFCw6OiJrpR+aH1VQe6DYm3fK2KWCC5Jf3m/Pv1RxF08M1Xm08RCyQ5Qe0YyW5L16yYT2nnV48krVhYZ6SGFA== + /@azure/core-tracing/1.0.0-preview.8: + dependencies: + '@opencensus/web-types': 0.0.7 + '@opentelemetry/api': 0.6.1 + tslib: 1.11.1 + dev: false + resolution: + integrity: sha512-ZKUpCd7Dlyfn7bdc+/zC/sf0aRIaNQMDuSj2RhYRFe3p70hVAnYGp3TX4cnG2yoEALp/LTj/XnZGQ8Xzf6Ja/Q== /@azure/eslint-plugin-azure-sdk/2.0.1_9e31f0f459c1656d0a7ef30429cc70f8: dependencies: '@typescript-eslint/parser': 2.27.0_eslint@6.8.0+typescript@3.8.3 @@ -5684,6 +5692,12 @@ packages: dev: false resolution: integrity: sha512-qj4LSEykJ0SEYESQLg9Vee6VXH5xHN1pYj7ozPeUk+l+S1OaGKx1FugAu+g+3pPwK46WXV1PJD9XiRx8+tS4cw== + /rhea/1.0.21: + dependencies: + debug: 3.2.6 + dev: false + resolution: + integrity: sha512-9ddxyJR0nlWmynukzZTWN+bSYWu7KLHVMkIH/7PpFG5RHfV5t7zXIfZ6rqJSJe9wBAgnNr2Xz41KM2nPujWiFQ== /rimraf/2.6.3: dependencies: glob: 7.1.6 @@ -7303,12 +7317,12 @@ packages: dev: false name: '@rush-temp/abort-controller' resolution: - integrity: sha512-fucn99HAuY7tI+KhWCZJUEnokpmiMCibhQlvJ6PyhsYwNkvvmCN1LAqTPvbmNnqGgpzEpbiLUItaD+ao1C5hqQ== + integrity: sha512-+U50rbtOuP97e6o1LzJHG461q/ATB9T6B9X7nFmakGCLoaM+9IJ5Uy24i0Wl53FLewyKPt4JIgG74uSthuuFfQ== tarball: 'file:projects/abort-controller.tgz' version: 0.0.0 'file:projects/ai-form-recognizer.tgz': dependencies: - '@azure/core-tracing': 1.0.0-preview.7 + '@azure/core-tracing': 1.0.0-preview.8 '@microsoft/api-extractor': 7.7.11 '@opentelemetry/api': 0.6.1 '@opentelemetry/types': 0.2.0 @@ -7365,12 +7379,12 @@ packages: dev: false name: '@rush-temp/ai-form-recognizer' resolution: - integrity: sha512-drftp8AmeOVBMm6hfpBJnt/OziXXLqhNWfZo1oLqfQSBISJxghr/apNc4CKrcSp9qURbWaJkeANWuHxIVeYndQ== + integrity: sha512-S7DeKnzahpdWfC5DkYUYawPn+AZQoXPDRfNrVw9aAQ/Sx2EgXBl0kyPw1KjDEGx5gPfv3vX9d7I4MySf2gKBKw== tarball: 'file:projects/ai-form-recognizer.tgz' version: 0.0.0 'file:projects/ai-text-analytics.tgz': dependencies: - '@azure/core-tracing': 1.0.0-preview.7 + '@azure/core-tracing': 1.0.0-preview.8 '@microsoft/api-extractor': 7.7.11 '@opentelemetry/api': 0.6.1 '@opentelemetry/types': 0.2.0 @@ -7425,12 +7439,12 @@ packages: dev: false name: '@rush-temp/ai-text-analytics' resolution: - integrity: sha512-QcTeJNiCvAElVSE1bN60A0+dFQxse/nDD/RuVoKRPDx8cUaiyAg+CReDzqsULIHFxl7wK2haQJ50NkjZtnHMQA== + integrity: sha512-0h0t7T1G3FanBlSu7gQaeigaX8F4Q6p2e+zkeh73smykH9DXnNtkiaczzbosEODnaeq0Bnn4iEXDStk/mqrXnw== 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/core-tracing': 1.0.0-preview.8 '@microsoft/api-extractor': 7.7.11 '@opentelemetry/api': 0.6.1 '@opentelemetry/types': 0.2.0 @@ -7469,7 +7483,7 @@ packages: dev: false name: '@rush-temp/app-configuration' resolution: - integrity: sha512-QesKkz6aJwUt+oVkUzm4ESihvMHTjl1/Ims68Ue1i9H7jevsWAMDX+U1G+7rb0xCtx013PZdoedC9Qb6dsj/fg== + integrity: sha512-eqUTjcKmzKdmatFRLb/vYOOsUmvWygq+Simg4HuMa8E87xSm55mD2LFtqwRUhtYaWh0y739viBXjO5663aAgjQ== tarball: 'file:projects/app-configuration.tgz' version: 0.0.0 'file:projects/core-amqp.tgz': @@ -7519,7 +7533,7 @@ packages: prettier: 1.19.1 process: 0.11.10 puppeteer: 2.1.1 - rhea: 1.0.20 + rhea: 1.0.21 rhea-promise: 1.0.0 rimraf: 3.0.2 rollup: 1.32.1 @@ -7537,7 +7551,7 @@ packages: dev: false name: '@rush-temp/core-amqp' resolution: - integrity: sha512-WyiTsNG2N2Oo6otWDYC8d8JPb4JCIIOtNJ9yb0O9eieETT43yPLHego/gKATaIiNQLtM1nXytcT3j81Pg9qK8w== + integrity: sha512-NdEb9MmK1wTvolMck3O7SchTQRpdvTeJsWLPjaXj4lfhKiE+6UkHkiltM4Hz19Pse6P11CLQOfN0DEbV3kEEIA== tarball: 'file:projects/core-amqp.tgz' version: 0.0.0 'file:projects/core-arm.tgz': @@ -7570,7 +7584,7 @@ packages: dev: false name: '@rush-temp/core-arm' resolution: - integrity: sha512-YX/po4ul39/S1cXgGUdPGYN8uIIK+922fRiN80ruwna7ksfF8OJH46rJcUTUijU5INvWaRPq8YNVJcMQNLqXZw== + integrity: sha512-1qMPTU5XX+O3mEz4qLE0XDqGlnMxYZn/m6/6/Z5tx8b4Ax/SbMlhA0ZAc/HxZmGuOxVo//aN3rSWatio1drukg== tarball: 'file:projects/core-arm.tgz' version: 0.0.0 'file:projects/core-asynciterator-polyfill.tgz': @@ -7588,12 +7602,12 @@ packages: dev: false name: '@rush-temp/core-asynciterator-polyfill' resolution: - integrity: sha512-GeOzC1ryAneCbgzn+3oZeAN8nK5cyWWXEKepTXBESWzcAXy6mv0z0N7qmlK6XwRPGp4UuF9ZLSVbLGl4eNAiYA== + integrity: sha512-69f+DjfbfiipGVM6Vx0KXehWvmc5YstJn+Fayg8f1/OzRbqIBw1skZTIxQATkZUOOSFHhYg5ALjbkdNRTNgMng== tarball: 'file:projects/core-asynciterator-polyfill.tgz' version: 0.0.0 'file:projects/core-auth.tgz': dependencies: - '@azure/core-tracing': 1.0.0-preview.7 + '@azure/core-tracing': 1.0.0-preview.8 '@azure/eslint-plugin-azure-sdk': 2.0.1_9e31f0f459c1656d0a7ef30429cc70f8 '@microsoft/api-extractor': 7.7.11 '@opentelemetry/api': 0.6.1 @@ -7630,12 +7644,12 @@ packages: dev: false name: '@rush-temp/core-auth' resolution: - integrity: sha512-a8h+Fir7HdvM06N5A+BKxvig7SmvI2pQPUnVNoL2HWpuW5wdcelVyVpODYRiA43kNPsodP0AIlqWJSZOPnwtiw== + integrity: sha512-XOCLdTLVtEDNsvnHXQQ3g9x/3cuc0i+gPMzAVW07gtkfaGsad9L21SwkuDj56HOscIcUpBlY/RH+HS5wvZVBBA== tarball: 'file:projects/core-auth.tgz' version: 0.0.0 'file:projects/core-http.tgz': dependencies: - '@azure/core-tracing': 1.0.0-preview.7 + '@azure/core-tracing': 1.0.0-preview.8 '@azure/eslint-plugin-azure-sdk': 2.0.1_9e31f0f459c1656d0a7ef30429cc70f8 '@azure/logger-js': 1.3.2 '@microsoft/api-extractor': 7.7.11 @@ -7708,7 +7722,7 @@ packages: dev: false name: '@rush-temp/core-http' resolution: - integrity: sha512-o5d9utUNW5fOZiD50gQMojx/D99pkyzqaqVf5t//U57WVmNdQqS6NAfOZoOPGcR6ZLFONC99mr+ag9+NrXM0PQ== + integrity: sha512-C3Rzc3Ll8X1bMewSo78GuDbLJrBvlG934evHz110L/pbHgkIMpwCHZgeyCiu+4BX7+J5hvMr9n4+a9nO6o7MZQ== tarball: 'file:projects/core-http.tgz' version: 0.0.0 'file:projects/core-https.tgz': @@ -7759,7 +7773,7 @@ packages: dev: false name: '@rush-temp/core-https' resolution: - integrity: sha512-5s+/AXXkUm6xKyWG76eHP8iwXBMRODjucZlD/LKco8qQr6Ifq9z3FJvom0bXgF+9RxZvJR+jamClpANN87GeDQ== + integrity: sha512-ghT8B1K5CYEO7RLp2joI4uvwHB62Y5bUjRF2z8oZgK582YvdCWUJmiPVExpY8pGK+/5hnPRg49LSEbmWu6Vaeg== tarball: 'file:projects/core-https.tgz' version: 0.0.0 'file:projects/core-lro.tgz': @@ -7815,7 +7829,7 @@ packages: dev: false name: '@rush-temp/core-lro' resolution: - integrity: sha512-JWz73NQbYiVhdyucClGyfF5PugpQH362xVQUWoK06fTqj38XsKzDXXiixzUHcZ5Et+5y5B21XhbJS9591q3ERw== + integrity: sha512-rFIGXMcDoORghUnDOFgkyV1b9j22vLg2WtsAinDKtg8rspMwqYX7CJaSY+v1AIsfCrEwLtlueSitAQuE8NYpJQ== tarball: 'file:projects/core-lro.tgz' version: 0.0.0 'file:projects/core-paging.tgz': @@ -7833,7 +7847,7 @@ packages: dev: false name: '@rush-temp/core-paging' resolution: - integrity: sha512-/N5f2+zh/3u+Vzd2RO7LAN5bZDVNuSjwFG34BQ/WPODnc8xBrlwNySSh5RfDiPFGt5DmccPjFneKkYqHhA5aqg== + integrity: sha512-YM2ktK/Xu8ZPTM8d3yWvGN1SdHr+Ekw3j6bu8PcxVO8PjCqkkQZ/cetvFKWRJtiApxNjMRC+YnBbPRgRhn8nWA== tarball: 'file:projects/core-paging.tgz' version: 0.0.0 'file:projects/core-tracing.tgz': @@ -7874,7 +7888,7 @@ packages: dev: false name: '@rush-temp/core-tracing' resolution: - integrity: sha512-6lNv0IiPu0TlqcRff8jI141AEY7u/AQxmVK0+H8R+sFgB6T+ZW80B1sqOAW3GjHnKKtNQ8M8l+2SHtzHtF87PQ== + integrity: sha512-GAOODMqIJbrrtg5NvzYVmeU5oEyw31NbKJvcgXZJuCvoL+UjHxAXhhl5oShvgKx54Z6OqSDcmHgWJi2sWAKTHw== tarball: 'file:projects/core-tracing.tgz' version: 0.0.0 'file:projects/cosmos.tgz': @@ -7935,7 +7949,7 @@ packages: dev: false name: '@rush-temp/cosmos' resolution: - integrity: sha512-KcNatflolF0H1D+j96XYj3y8saZJ5+c8l5aQ6AjzViYSJJ4/Myc6OI2/NnFBxaz1udObzhBF2lPivJSCo7CQOA== + integrity: sha512-KQvL7GazobTLvfKydVCzGymriq5ryD4MWzKdofwEG0KYiQf/UANkqcVagjJjGJhUg6ivN9iLOKztCMvHyj0VFw== tarball: 'file:projects/cosmos.tgz' version: 0.0.0 'file:projects/eslint-plugin-azure-sdk.tgz': @@ -7968,12 +7982,12 @@ packages: dev: false name: '@rush-temp/eslint-plugin-azure-sdk' resolution: - integrity: sha512-NtNxYK027+o01SFWabyun9+UUx+C7GUrLMUyPLbDR055avcHRvYJA2lJdVJgSUuKq6VfsfKdMNMoSqtxUshHrA== + integrity: sha512-4rgkTckc9Q3R25MDEIybhrx1ZpMwK6PXTh2/cLjbpZ04+AVT0yctfN5HQuFKWOoJ/j/duroFLNoTs38/dsgtRA== 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/core-tracing': 1.0.0-preview.8 '@azure/eslint-plugin-azure-sdk': 2.0.1_9e31f0f459c1656d0a7ef30429cc70f8 '@microsoft/api-extractor': 7.7.11 '@opentelemetry/api': 0.6.1 @@ -8048,7 +8062,7 @@ packages: dev: false name: '@rush-temp/event-hubs' resolution: - integrity: sha512-SvD3McUNYWtff0uFqaogeUbmRCdqLiwVUsRO7SrkIjdVazSydRCU5cl5IUGRKCQ4IMPzGkbXN/yyN1tmG45uGw== + integrity: sha512-l6eRUH+L5Yh7Ddob1ydBnri28dFbcnPrIwuAl6D02yO7HcAKEJeAMvIMtHdsToxVyeccOsdKkXjcNhs1tUCtWg== tarball: 'file:projects/event-hubs.tgz' version: 0.0.0 'file:projects/event-processor-host.tgz': @@ -8105,7 +8119,7 @@ packages: dev: false name: '@rush-temp/event-processor-host' resolution: - integrity: sha512-Pb59V8L43tgDpEAVJucokv8rqlzzPXjPstVBV0S52R+/99013T2BJ0ow/OsTomhEZ3dgPtzuGrKvgese3sEbwQ== + integrity: sha512-o0lja9JmvdssBTAv5EqriAn2EQ3/xc3EQOFfbtHmwVSdlmXANyFx69xu7mcWnGd3dvDfJVCCYnlEC8ZN6LX5dw== tarball: 'file:projects/event-processor-host.tgz' version: 0.0.0 'file:projects/eventhubs-checkpointstore-blob.tgz': @@ -8169,12 +8183,12 @@ packages: dev: false name: '@rush-temp/eventhubs-checkpointstore-blob' resolution: - integrity: sha512-+JoABbLpCHclRXiaCyzgMO5d7BBpb7L9614AkI4JVFNLlomKjLchuwi49D0PwdRFCXCjjqAUXlvFbqecKFs1Xw== + integrity: sha512-UOWIkdN9h/drjlrM19TQ9n2F90F87ve76xhXc4vS6G9MgbDf4/DEJVghHMaavVkWRk8t4hzouyY86E7iwiHkLg== tarball: 'file:projects/eventhubs-checkpointstore-blob.tgz' version: 0.0.0 'file:projects/identity.tgz': dependencies: - '@azure/core-tracing': 1.0.0-preview.7 + '@azure/core-tracing': 1.0.0-preview.8 '@microsoft/api-extractor': 7.7.11 '@opentelemetry/api': 0.6.1 '@opentelemetry/types': 0.2.0 @@ -8226,12 +8240,12 @@ packages: dev: false name: '@rush-temp/identity' resolution: - integrity: sha512-bIsS2X1xbQBtKNKXnTjXoS4A3jP8JUw5hzuxKTpVuVzyrvZWc2KM0Wb9EdSayFWXPRVtS51MUP7NA9DgtfuMzQ== + integrity: sha512-ZMogVt0EVtES/40MkMWXhfAillgcml9v8rK4KtH9x5l5Q1rBdlfN2nwLA7/fGtWd6kwmY6KezRCrM4TaPnksNA== tarball: 'file:projects/identity.tgz' version: 0.0.0 'file:projects/keyvault-certificates.tgz': dependencies: - '@azure/core-tracing': 1.0.0-preview.7 + '@azure/core-tracing': 1.0.0-preview.8 '@azure/eslint-plugin-azure-sdk': 2.0.1_9e31f0f459c1656d0a7ef30429cc70f8 '@microsoft/api-extractor': 7.7.11 '@opentelemetry/api': 0.6.1 @@ -8292,12 +8306,12 @@ packages: dev: false name: '@rush-temp/keyvault-certificates' resolution: - integrity: sha512-Jn9KFNDBX87QBJ1Lcjp78dYcE+dsMg5rYpio/JeY0V2mWlNdh7qTKLvCNYDBGbLA74vByV3WeKwIQryHAi+05g== + integrity: sha512-8zMe3ibaW81OgzRunWJHW57GK5WXNhYwHEwrl4Z9/whvItZgPGuk9F7E+S5PIRk801MRfO8Ipq9YpL7co9Sa2A== 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/core-tracing': 1.0.0-preview.8 '@azure/eslint-plugin-azure-sdk': 2.0.1_9e31f0f459c1656d0a7ef30429cc70f8 '@microsoft/api-extractor': 7.7.11 '@opentelemetry/api': 0.6.1 @@ -8358,12 +8372,12 @@ packages: dev: false name: '@rush-temp/keyvault-keys' resolution: - integrity: sha512-eoe4wq30U9rGPBp+IAHTibqqR8RCZQ0ijWtJutDZ8WaiHbmIKaSFbQRY1A0+N/xSYHSKrcZDwTZC3cEpeWHa9A== + integrity: sha512-9qXoMnPOjenPLWHxP0t3sk59kTP7yTEHUkk+6G+RKB0R5ZJNf+nx0hRiD60O49zi3+dyOoiNkoQ1xPupOsXM9w== 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/core-tracing': 1.0.0-preview.8 '@azure/eslint-plugin-azure-sdk': 2.0.1_9e31f0f459c1656d0a7ef30429cc70f8 '@microsoft/api-extractor': 7.7.11 '@opentelemetry/api': 0.6.1 @@ -8424,7 +8438,7 @@ packages: dev: false name: '@rush-temp/keyvault-secrets' resolution: - integrity: sha512-PjKc2VsK3yhM2nLsw1SEAqNOeKw3mK5APQ/6AJC2QyHWP/lDYghPHH6ep/78LBThcm5K9uLFMwM49ZLnYsnY+A== + integrity: sha512-9cHdwrE0M3YNfql2qQG1mHrppa2CQEKFL3IqrnD7A61UQW2NBWWwCeuESIWXkTb9nkX0he+E8XW5hvIklyDH8A== tarball: 'file:projects/keyvault-secrets.tgz' version: 0.0.0 'file:projects/logger.tgz': @@ -8477,12 +8491,12 @@ packages: dev: false name: '@rush-temp/logger' resolution: - integrity: sha512-ut5SiKtmdY3S/GA/IShEHhh85rP1p2m/zlP2GrW/JSdKx2w/MNIFCNT/2JJmC2tfy6ye4hWvwIt2v95rWckI0g== + integrity: sha512-MhNFck4q25CPlZFkorhQezRsAXK9Z/TNs5GGF0Ng0JB1c2hOu3VzR9p3SQ8e1l9yKZpEriD009K1Sz9mn8tl7g== tarball: 'file:projects/logger.tgz' version: 0.0.0 'file:projects/search-documents.tgz': dependencies: - '@azure/core-tracing': 1.0.0-preview.7 + '@azure/core-tracing': 1.0.0-preview.8 '@microsoft/api-extractor': 7.7.11 '@opentelemetry/api': 0.6.1 '@opentelemetry/types': 0.2.0 @@ -8537,11 +8551,12 @@ packages: dev: false name: '@rush-temp/search-documents' resolution: - integrity: sha512-dSUxmkUpg7gC0P73FvSLK5Fr4E8WGpdgaycSvR4ZSA69VpYIodX65aU7uyfkHrpuFCkydY7fbBbaXYiWfmvLTA== + integrity: sha512-NZTV+lS2jZrfA1ylRg2mQ8BVZNF1709uChNt5oMi1x0A/+n0krX2Fox27sYf7tDrVdsM1IOt4nPIuopG49p7IQ== tarball: 'file:projects/search-documents.tgz' version: 0.0.0 'file:projects/service-bus.tgz': dependencies: + '@azure/core-tracing': 1.0.0-preview.8 '@azure/eslint-plugin-azure-sdk': 2.0.1_9e31f0f459c1656d0a7ef30429cc70f8 '@microsoft/api-extractor': 7.7.11 '@opentelemetry/api': 0.6.1 @@ -8616,12 +8631,12 @@ packages: dev: false name: '@rush-temp/service-bus' resolution: - integrity: sha512-ySmiDTgbTBpMYgXNSDGpIzuHs+5raDZenJw/jEBXhioOYpNXL+oDYK5EZQ5S+BKMCmGXOD7QvrpQortMrf3IrA== + integrity: sha512-b828xmJrpi/+VYklayvTWJdkjz/Qvl6R7n2VGqZ1FuRcrruU4guNtaXPLr2CbUonsWeihhv5qhXKR4lc+j5ciQ== 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/core-tracing': 1.0.0-preview.8 '@microsoft/api-extractor': 7.7.11 '@opentelemetry/api': 0.6.1 '@opentelemetry/types': 0.2.0 @@ -8678,12 +8693,12 @@ packages: dev: false name: '@rush-temp/storage-blob' resolution: - integrity: sha512-qNcuXw81uBfRLvrBeMcFs/hCXl6FCAglnFkswE98KtSV3Pj2rtxYPQWJxG4/O2GkzCRyVkgxYtfb9uDTYJXZ8w== + integrity: sha512-C9XlnxCE4aMhIOrRe5C9tT3/keD6MMAguq/1rUgruetoHeYnAd1q65NGI5YuKKIQ3BfGo3YYiLC/rOu1F3S8og== 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/core-tracing': 1.0.0-preview.8 '@microsoft/api-extractor': 7.7.11 '@opentelemetry/api': 0.6.1 '@opentelemetry/types': 0.2.0 @@ -8748,12 +8763,12 @@ packages: dev: false name: '@rush-temp/storage-file-datalake' resolution: - integrity: sha512-ax+/3IKbvusjZpXmloEUa+6cQrULHB6eHFpd6dNzQmQ0a1QOIg3n5uHIEY2X/1kQCOkcMWVITX+imkz6b9Gv2A== + integrity: sha512-JPD7i5Im/tbFEYgIQyG6j1tzyNcVXXHzWjGkX1Njz/UtVVKp+2zDGj2e7/WFv5z5Tjmq2h/L6H3M9hUIMHs/Lw== tarball: 'file:projects/storage-file-datalake.tgz' version: 0.0.0 'file:projects/storage-file-share.tgz': dependencies: - '@azure/core-tracing': 1.0.0-preview.7 + '@azure/core-tracing': 1.0.0-preview.8 '@microsoft/api-extractor': 7.7.11 '@opentelemetry/api': 0.6.1 '@opentelemetry/types': 0.2.0 @@ -8810,7 +8825,7 @@ packages: dev: false name: '@rush-temp/storage-file-share' resolution: - integrity: sha512-Kzv6N1reNbnI8qxNbwPIXTbMwBaDP9S43wPHoL8akm3ooY8ftesQtF6qCj39Tfw1HozCojH1QRdyahlqIAnTRg== + integrity: sha512-0b+vEmFEbM8wc0JboG10ufdwj5f4JdIWbHtznsOPa4/kgYZBmgPC7akvBkp0JjDpDIEYY94lxqIPIUAzNVPnhg== tarball: 'file:projects/storage-file-share.tgz' version: 0.0.0 'file:projects/storage-internal-avro.tgz': @@ -8868,12 +8883,12 @@ packages: dev: false name: '@rush-temp/storage-internal-avro' resolution: - integrity: sha512-xQ/8G7hl1NT0N067xLF7XdeD/Fhh+tiMlwEenqOHiUIZ8eMexdCJ5+MhnFOq33oqjMD8dcKku4BjyKQrXBFERg== + integrity: sha512-X0WQbD89y7TmMIh5RWV623zVmKy6McHbS7J5t8Du5xXcdA8p8Ry19Xgi5X74ho0Ib/56YEKHeo3QXpiZiteDcQ== tarball: 'file:projects/storage-internal-avro.tgz' version: 0.0.0 'file:projects/storage-queue.tgz': dependencies: - '@azure/core-tracing': 1.0.0-preview.7 + '@azure/core-tracing': 1.0.0-preview.8 '@microsoft/api-extractor': 7.7.11 '@opentelemetry/api': 0.6.1 '@opentelemetry/types': 0.2.0 @@ -8929,7 +8944,7 @@ packages: dev: false name: '@rush-temp/storage-queue' resolution: - integrity: sha512-WcfDHWEOKo17l6dpW4UFbHJgEzQd438F+F61uequ6uc6OH1XAQ/QUU5dWGYiJz+/SU11vyfIW81ZiRsiihQMdg== + integrity: sha512-wdtM6V3zw/sz6aAZgPG6aZMtVvfeTnzrtuc+SEB/pNQ5s2agzcVn1ilkYxL/KzIuy8J/gWBzK4BQcOY+232H0w== tarball: 'file:projects/storage-queue.tgz' version: 0.0.0 'file:projects/template.tgz': @@ -8980,7 +8995,7 @@ packages: dev: false name: '@rush-temp/template' resolution: - integrity: sha512-SrFwboYgyY0oI9obo6qPlcpZ0FiIxmcLg6ZycXRo1iFkJB0/vHSXLn1lZZvQK8VylbZm0j/78chAiP47Y1bNSg== + integrity: sha512-fez95uatWabkhoMC1/bs/GdMSbARJCHPDKBTvLYcriT92DHo483rgZhnrN+KK/KsdAtAAe+NQwc57kR3Axo4ug== tarball: 'file:projects/template.tgz' version: 0.0.0 'file:projects/test-utils-perfstress.tgz': @@ -9008,7 +9023,7 @@ packages: dev: false name: '@rush-temp/test-utils-perfstress' resolution: - integrity: sha512-imbSa1ojeg4y7eaJf9nHwEW7Cz/gPC6VypvpOS8vhqcu28SzLy81hclUOvEHDDzG1Nunfp73qhE3Eygirq2CFQ== + integrity: sha512-4ZLFBeVDO2cVkeIIQOdTOZx/9voPj+vrJuEvp18smNkJ36vFb0h7RqbNPVfzCO8F756AvCPsIpKcA7xvJnujwQ== tarball: 'file:projects/test-utils-perfstress.tgz' version: 0.0.0 'file:projects/test-utils-recorder.tgz': @@ -9069,7 +9084,7 @@ packages: dev: false name: '@rush-temp/test-utils-recorder' resolution: - integrity: sha512-76FDvsZfN/8ax+b5588w5VrnCxbTcCvlMJPaDzc5dFn4VaTKY38YH1MlLEHk2UpNuR+8oCBZ1LIlZfGWMCkVwA== + integrity: sha512-C1muEQ5h7AVq2wY2lR5FIYNg1dy8p8CZ+446TbVcN744D37bc7P4kI+Wn+MXB6twOyT390AVX8qN7TSSCuqRMQ== tarball: 'file:projects/test-utils-recorder.tgz' version: 0.0.0 'file:projects/testhub.tgz': @@ -9081,7 +9096,7 @@ packages: async-lock: 1.2.2 death: 1.1.0 debug: 4.1.1 - rhea: 1.0.20 + rhea: 1.0.21 rimraf: 3.0.2 tslib: 1.11.1 typescript: 3.8.3 @@ -9090,7 +9105,7 @@ packages: dev: false name: '@rush-temp/testhub' resolution: - integrity: sha512-SEdEggE9o4zPC0gonCdcDQ4mIEyVF5EwJsLshA0dkWW+oL5I8LMIFzy2NOiqo+CmIishltIA13FmX4SoSUw/BA== + integrity: sha512-Z6HTwhIIFE79DucKmLYyHiLO/5MWSxWLowoNyqcCIJgl0p54t1oRpa6SRRiVZwrTwzkLU5iA8vrYAnUmfSeGbQ== tarball: 'file:projects/testhub.tgz' version: 0.0.0 registry: '' diff --git a/sdk/storage/storage-blob/CHANGELOG.md b/sdk/storage/storage-blob/CHANGELOG.md index 79f80e115b68..6f3025ce9a2d 100644 --- a/sdk/storage/storage-blob/CHANGELOG.md +++ b/sdk/storage/storage-blob/CHANGELOG.md @@ -1,5 +1,9 @@ # Release History +## 12.2.0 (2020.06) + +- Supported quick query. Added a new API `BlockBlobClient.query()`. + ## 12.1.2 (2020.05) - Fix data corruption failure error [issue #6411](https://github.com/Azure/azure-sdk-for-js/issues/6411) when downloading compressed files. [PR #7993](https://github.com/Azure/azure-sdk-for-js/pull/7993) diff --git a/sdk/storage/storage-blob/package.json b/sdk/storage/storage-blob/package.json index cb34751f9145..52f6f685021c 100644 --- a/sdk/storage/storage-blob/package.json +++ b/sdk/storage/storage-blob/package.json @@ -12,6 +12,7 @@ "./dist-esm/storage-blob/test/utils/index.js": "./dist-esm/storage-blob/test/utils/index.browser.js", "./dist-esm/storage-blob/src/BatchUtils.js": "./dist-esm/storage-blob/src/BatchUtils.browser.js", "./dist-esm/storage-blob/src/BlobDownloadResponse.js": "./dist-esm/storage-blob/src/BlobDownloadResponse.browser.js", + "./dist-esm/storage-blob/src/BlobQueryResponse.js": "./dist-esm/storage-blob/src/BlobQueryResponse.browser.js", "fs": false, "os": false, "process": false @@ -162,4 +163,4 @@ "typescript": "~3.8.3", "util": "^0.12.1" } -} \ No newline at end of file +} diff --git a/sdk/storage/storage-blob/src/BlobQueryResponse.browser.ts b/sdk/storage/storage-blob/src/BlobQueryResponse.browser.ts new file mode 100644 index 000000000000..9ce68bff6747 --- /dev/null +++ b/sdk/storage/storage-blob/src/BlobQueryResponse.browser.ts @@ -0,0 +1,499 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +import { HttpResponse } from "@azure/core-http"; + +import { + BlobDownloadResponseModel, + BlobType, + CopyStatusType, + LeaseDurationType, + LeaseStateType, + LeaseStatusType, + BlobDownloadHeaders, + BlobQueryResponseModel +} from "./generatedModels"; +import { Metadata } from "./models"; +import { BlobQuickQueryStreamOptions } from "./utils/BlobQuickQueryStream"; + +/** + * ONLY AVAILABLE IN BROWSER RUNTIME. + * + * BlobQueryResponse implements BlobDownloadResponseModel interface, and in browser runtime it will + * parser avor data returned by blob query. + * + * @export + * @class BlobQueryResponse + * @implements {BlobDownloadResponseModel} + */ +export class BlobQueryResponse implements BlobDownloadResponseModel { + /** + * Indicates that the service supports + * requests for partial file content. + * + * @readonly + * @type {(string | undefined)} + * @memberof BlobQueryResponse + */ + public get acceptRanges(): string | undefined { + return this.originalResponse.acceptRanges; + } + + /** + * Returns if it was previously specified + * for the file. + * + * @readonly + * @type {(string | undefined)} + * @memberof BlobQueryResponse + */ + public get cacheControl(): string | undefined { + return this.originalResponse.cacheControl; + } + + /** + * Returns the value that was specified + * for the 'x-ms-content-disposition' header and specifies how to process the + * response. + * + * @readonly + * @type {(string | undefined)} + * @memberof BlobQueryResponse + */ + public get contentDisposition(): string | undefined { + return this.originalResponse.contentDisposition; + } + + /** + * Returns the value that was specified + * for the Content-Encoding request header. + * + * @readonly + * @type {(string | undefined)} + * @memberof BlobQueryResponse + */ + public get contentEncoding(): string | undefined { + return this.originalResponse.contentEncoding; + } + + /** + * Returns the value that was specified + * for the Content-Language request header. + * + * @readonly + * @type {(string | undefined)} + * @memberof BlobQueryResponse + */ + public get contentLanguage(): string | undefined { + return this.originalResponse.contentLanguage; + } + + /** + * The current sequence number for a + * page blob. This header is not returned for block blobs or append blobs. + * + * @readonly + * @type {(number | undefined)} + * @memberof BlobQueryResponse + */ + public get blobSequenceNumber(): number | undefined { + return this.originalResponse.blobSequenceNumber; + } + + /** + * The blob's type. Possible values include: + * 'BlockBlob', 'PageBlob', 'AppendBlob'. + * + * @readonly + * @type {(BlobType | undefined)} + * @memberof BlobQueryResponse + */ + public get blobType(): BlobType | undefined { + return this.originalResponse.blobType; + } + + /** + * The number of bytes present in the + * response body. + * + * @readonly + * @type {(number | undefined)} + * @memberof BlobQueryResponse + */ + public get contentLength(): number | undefined { + return this.originalResponse.contentLength; + } + + /** + * If the file has an MD5 hash and the + * request is to read the full file, this response header is returned so that + * the client can check for message content integrity. If the request is to + * read a specified range and the 'x-ms-range-get-content-md5' is set to + * true, then the request returns an MD5 hash for the range, as long as the + * range size is less than or equal to 4 MB. If neither of these sets of + * conditions is true, then no value is returned for the 'Content-MD5' + * header. + * + * @readonly + * @type {(Uint8Array | undefined)} + * @memberof BlobQueryResponse + */ + public get contentMD5(): Uint8Array | undefined { + return this.originalResponse.contentMD5; + } + + /** + * Indicates the range of bytes returned if + * the client requested a subset of the file by setting the Range request + * header. + * + * @readonly + * @type {(string | undefined)} + * @memberof BlobQueryResponse + */ + public get contentRange(): string | undefined { + return this.originalResponse.contentRange; + } + + /** + * The content type specified for the file. + * The default content type is 'application/octet-stream' + * + * @readonly + * @type {(string | undefined)} + * @memberof BlobQueryResponse + */ + public get contentType(): string | undefined { + return this.originalResponse.contentType; + } + + /** + * Conclusion time of the last attempted + * Copy File operation where this file was the destination file. This value + * can specify the time of a completed, aborted, or failed copy attempt. + * + * @readonly + * @type {(Date | undefined)} + * @memberof BlobQueryResponse + */ + public get copyCompletedOn(): Date | undefined { + return undefined; + } + + /** + * String identifier for the last attempted Copy + * File operation where this file was the destination file. + * + * @readonly + * @type {(string | undefined)} + * @memberof BlobQueryResponse + */ + public get copyId(): string | undefined { + return this.originalResponse.copyId; + } + + /** + * Contains the number of bytes copied and + * the total bytes in the source in the last attempted Copy File operation + * where this file was the destination file. Can show between 0 and + * Content-Length bytes copied. + * + * @readonly + * @type {(string | undefined)} + * @memberof BlobQueryResponse + */ + public get copyProgress(): string | undefined { + return this.originalResponse.copyProgress; + } + + /** + * URL up to 2KB in length that specifies the + * source file used in the last attempted Copy File operation where this file + * was the destination file. + * + * @readonly + * @type {(string | undefined)} + * @memberof BlobQueryResponse + */ + public get copySource(): string | undefined { + return this.originalResponse.copySource; + } + + /** + * State of the copy operation + * identified by 'x-ms-copy-id'. Possible values include: 'pending', + * 'success', 'aborted', 'failed' + * + * @readonly + * @type {(CopyStatusType | undefined)} + * @memberof BlobQueryResponse + */ + public get copyStatus(): CopyStatusType | undefined { + return this.originalResponse.copyStatus; + } + + /** + * Only appears when + * x-ms-copy-status is failed or pending. Describes cause of fatal or + * non-fatal copy operation failure. + * + * @readonly + * @type {(string | undefined)} + * @memberof BlobQueryResponse + */ + public get copyStatusDescription(): string | undefined { + return this.originalResponse.copyStatusDescription; + } + + /** + * When a blob is leased, + * specifies whether the lease is of infinite or fixed duration. Possible + * values include: 'infinite', 'fixed'. + * + * @readonly + * @type {(LeaseDurationType | undefined)} + * @memberof BlobQueryResponse + */ + public get leaseDuration(): LeaseDurationType | undefined { + return this.originalResponse.leaseDuration; + } + + /** + * Lease state of the blob. Possible + * values include: 'available', 'leased', 'expired', 'breaking', 'broken'. + * + * @readonly + * @type {(LeaseStateType | undefined)} + * @memberof BlobQueryResponse + */ + public get leaseState(): LeaseStateType | undefined { + return this.originalResponse.leaseState; + } + + /** + * The current lease status of the + * blob. Possible values include: 'locked', 'unlocked'. + * + * @readonly + * @type {(LeaseStatusType | undefined)} + * @memberof BlobQueryResponse + */ + public get leaseStatus(): LeaseStatusType | undefined { + return this.originalResponse.leaseStatus; + } + + /** + * A UTC date/time value generated by the service that + * indicates the time at which the response was initiated. + * + * @readonly + * @type {(Date | undefined)} + * @memberof BlobQueryResponse + */ + public get date(): Date | undefined { + return this.originalResponse.date; + } + + /** + * The number of committed blocks + * present in the blob. This header is returned only for append blobs. + * + * @readonly + * @type {(number | undefined)} + * @memberof BlobQueryResponse + */ + public get blobCommittedBlockCount(): number | undefined { + return this.originalResponse.blobCommittedBlockCount; + } + + /** + * The ETag contains a value that you can use to + * perform operations conditionally, in quotes. + * + * @readonly + * @type {(string | undefined)} + * @memberof BlobQueryResponse + */ + public get etag(): string | undefined { + return this.originalResponse.etag; + } + + /** + * The error code. + * + * @readonly + * @type {(string | undefined)} + * @memberof BlobQueryResponse + */ + public get errorCode(): string | undefined { + return this.originalResponse.errorCode; + } + + /** + * The value of this header is set to + * true if the file data and application metadata are completely encrypted + * using the specified algorithm. Otherwise, the value is set to false (when + * the file is unencrypted, or if only parts of the file/application metadata + * are encrypted). + * + * @readonly + * @type {(boolean | undefined)} + * @memberof BlobQueryResponse + */ + public get isServerEncrypted(): boolean | undefined { + return this.originalResponse.isServerEncrypted; + } + + /** + * If the blob has a MD5 hash, and if + * request contains range header (Range or x-ms-range), this response header + * is returned with the value of the whole blob's MD5 value. This value may + * or may not be equal to the value returned in Content-MD5 header, with the + * latter calculated from the requested range. + * + * @readonly + * @type {(Uint8Array | undefined)} + * @memberof BlobQueryResponse + */ + public get blobContentMD5(): Uint8Array | undefined { + return this.originalResponse.blobContentMD5; + } + + /** + * Returns the date and time the file was last + * modified. Any operation that modifies the file or its properties updates + * the last modified time. + * + * @readonly + * @type {(Date | undefined)} + * @memberof BlobQueryResponse + */ + public get lastModified(): Date | undefined { + return this.originalResponse.lastModified; + } + + /** + * A name-value pair + * to associate with a file storage object. + * + * @readonly + * @type {(Metadata | undefined)} + * @memberof BlobQueryResponse + */ + public get metadata(): Metadata | undefined { + return this.originalResponse.metadata; + } + + /** + * This header uniquely identifies the request + * that was made and can be used for troubleshooting the request. + * + * @readonly + * @type {(string | undefined)} + * @memberof BlobQueryResponse + */ + public get requestId(): string | undefined { + return this.originalResponse.requestId; + } + + /** + * If a client request id header is sent in the request, this header will be present in the + * response with the same value. + * + * @readonly + * @type {(string | undefined)} + * @memberof BlobQueryResponse + */ + public get clientRequestId(): string | undefined { + return this.originalResponse.clientRequestId; + } + + /** + * Indicates the version of the File service used + * to execute the request. + * + * @readonly + * @type {(string | undefined)} + * @memberof BlobQueryResponse + */ + public get version(): string | undefined { + return this.originalResponse.version; + } + + /** + * The SHA-256 hash of the encryption key used to encrypt the blob. This value is only returned + * when the blob was encrypted with a customer-provided key. + * + * @readonly + * @type {(string | undefined)} + * @memberof BlobQueryResponse + */ + public get encryptionKeySha256(): string | undefined { + return this.originalResponse.encryptionKeySha256; + } + + /** + * If the request is to read a specified range and the x-ms-range-get-content-crc64 is set to + * true, then the request returns a crc64 for the range, as long as the range size is less than + * or equal to 4 MB. If both x-ms-range-get-content-crc64 & x-ms-range-get-content-md5 is + * specified in the same request, it will fail with 400(Bad Request) + * + * @type {(Uint8Array | undefined)} + * @memberof BlobQueryResponse + */ + public get contentCrc64(): Uint8Array | undefined { + return this.originalResponse.contentCrc64; + } + + /** + * The response body as a browser Blob. + * Always undefined in node.js. + * + * @readonly + * @type {(Promise | undefined)} + * @memberof BlobQueryResponse + */ + public get blobBody(): Promise | undefined { + throw Error(`Quick query in browser is not supported yet.`); + } + + /** + * The response body as a node.js Readable stream. + * Always undefined in the browser. + * + * @readonly + * @type {(NodeJS.ReadableStream | undefined)} + * @memberof BlobQueryResponse + */ + public get readableStreamBody(): NodeJS.ReadableStream | undefined { + return undefined; + } + + /** + * The HTTP response. + * + * @type {HttpResponse} + * @memberof BlobQueryResponse + */ + public get _response(): HttpResponse & { + parsedHeaders: BlobDownloadHeaders; + } { + return this.originalResponse._response; + } + + private originalResponse: BlobQueryResponseModel; + + /** + * Creates an instance of BlobQueryResponse. + * + * @param {BlobQueryResponseModel} originalResponse + * @param {BlobQuickQueryStreamOptions} [options={}] + * @memberof BlobQueryResponse + */ + public constructor( + originalResponse: BlobQueryResponseModel, + _options: BlobQuickQueryStreamOptions = {} + ) { + this.originalResponse = originalResponse; + } +} diff --git a/sdk/storage/storage-blob/src/BlobQueryResponse.ts b/sdk/storage/storage-blob/src/BlobQueryResponse.ts new file mode 100644 index 000000000000..cdc9ff16694b --- /dev/null +++ b/sdk/storage/storage-blob/src/BlobQueryResponse.ts @@ -0,0 +1,506 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +import { HttpResponse, isNode } from "@azure/core-http"; + +import { + BlobDownloadResponseModel, + BlobType, + CopyStatusType, + LeaseDurationType, + LeaseStateType, + LeaseStatusType, + BlobDownloadHeaders, + BlobQueryResponseModel +} from "./generatedModels"; +import { Metadata } from "./models"; +import { BlobQuickQueryStream, BlobQuickQueryStreamOptions } from "./utils/BlobQuickQueryStream"; + +/** + * ONLY AVAILABLE IN NODE.JS RUNTIME. + * + * BlobQueryResponse implements BlobDownloadResponseModel interface, and in Node.js runtime it will + * parser avor data returned by blob query. + * + * @export + * @class BlobQueryResponse + * @implements {BlobDownloadResponseModel} + */ +export class BlobQueryResponse implements BlobDownloadResponseModel { + /** + * Indicates that the service supports + * requests for partial file content. + * + * @readonly + * @type {(string | undefined)} + * @memberof BlobQueryResponse + */ + public get acceptRanges(): string | undefined { + return this.originalResponse.acceptRanges; + } + + /** + * Returns if it was previously specified + * for the file. + * + * @readonly + * @type {(string | undefined)} + * @memberof BlobQueryResponse + */ + public get cacheControl(): string | undefined { + return this.originalResponse.cacheControl; + } + + /** + * Returns the value that was specified + * for the 'x-ms-content-disposition' header and specifies how to process the + * response. + * + * @readonly + * @type {(string | undefined)} + * @memberof BlobQueryResponse + */ + public get contentDisposition(): string | undefined { + return this.originalResponse.contentDisposition; + } + + /** + * Returns the value that was specified + * for the Content-Encoding request header. + * + * @readonly + * @type {(string | undefined)} + * @memberof BlobQueryResponse + */ + public get contentEncoding(): string | undefined { + return this.originalResponse.contentEncoding; + } + + /** + * Returns the value that was specified + * for the Content-Language request header. + * + * @readonly + * @type {(string | undefined)} + * @memberof BlobQueryResponse + */ + public get contentLanguage(): string | undefined { + return this.originalResponse.contentLanguage; + } + + /** + * The current sequence number for a + * page blob. This header is not returned for block blobs or append blobs. + * + * @readonly + * @type {(number | undefined)} + * @memberof BlobQueryResponse + */ + public get blobSequenceNumber(): number | undefined { + return this.originalResponse.blobSequenceNumber; + } + + /** + * The blob's type. Possible values include: + * 'BlockBlob', 'PageBlob', 'AppendBlob'. + * + * @readonly + * @type {(BlobType | undefined)} + * @memberof BlobQueryResponse + */ + public get blobType(): BlobType | undefined { + return this.originalResponse.blobType; + } + + /** + * The number of bytes present in the + * response body. + * + * @readonly + * @type {(number | undefined)} + * @memberof BlobQueryResponse + */ + public get contentLength(): number | undefined { + return this.originalResponse.contentLength; + } + + /** + * If the file has an MD5 hash and the + * request is to read the full file, this response header is returned so that + * the client can check for message content integrity. If the request is to + * read a specified range and the 'x-ms-range-get-content-md5' is set to + * true, then the request returns an MD5 hash for the range, as long as the + * range size is less than or equal to 4 MB. If neither of these sets of + * conditions is true, then no value is returned for the 'Content-MD5' + * header. + * + * @readonly + * @type {(Uint8Array | undefined)} + * @memberof BlobQueryResponse + */ + public get contentMD5(): Uint8Array | undefined { + return this.originalResponse.contentMD5; + } + + /** + * Indicates the range of bytes returned if + * the client requested a subset of the file by setting the Range request + * header. + * + * @readonly + * @type {(string | undefined)} + * @memberof BlobQueryResponse + */ + public get contentRange(): string | undefined { + return this.originalResponse.contentRange; + } + + /** + * The content type specified for the file. + * The default content type is 'application/octet-stream' + * + * @readonly + * @type {(string | undefined)} + * @memberof BlobQueryResponse + */ + public get contentType(): string | undefined { + return this.originalResponse.contentType; + } + + /** + * Conclusion time of the last attempted + * Copy File operation where this file was the destination file. This value + * can specify the time of a completed, aborted, or failed copy attempt. + * + * @readonly + * @type {(Date | undefined)} + * @memberof BlobQueryResponse + */ + public get copyCompletedOn(): Date | undefined { + return undefined; + } + + /** + * String identifier for the last attempted Copy + * File operation where this file was the destination file. + * + * @readonly + * @type {(string | undefined)} + * @memberof BlobQueryResponse + */ + public get copyId(): string | undefined { + return this.originalResponse.copyId; + } + + /** + * Contains the number of bytes copied and + * the total bytes in the source in the last attempted Copy File operation + * where this file was the destination file. Can show between 0 and + * Content-Length bytes copied. + * + * @readonly + * @type {(string | undefined)} + * @memberof BlobQueryResponse + */ + public get copyProgress(): string | undefined { + return this.originalResponse.copyProgress; + } + + /** + * URL up to 2KB in length that specifies the + * source file used in the last attempted Copy File operation where this file + * was the destination file. + * + * @readonly + * @type {(string | undefined)} + * @memberof BlobQueryResponse + */ + public get copySource(): string | undefined { + return this.originalResponse.copySource; + } + + /** + * State of the copy operation + * identified by 'x-ms-copy-id'. Possible values include: 'pending', + * 'success', 'aborted', 'failed' + * + * @readonly + * @type {(CopyStatusType | undefined)} + * @memberof BlobQueryResponse + */ + public get copyStatus(): CopyStatusType | undefined { + return this.originalResponse.copyStatus; + } + + /** + * Only appears when + * x-ms-copy-status is failed or pending. Describes cause of fatal or + * non-fatal copy operation failure. + * + * @readonly + * @type {(string | undefined)} + * @memberof BlobQueryResponse + */ + public get copyStatusDescription(): string | undefined { + return this.originalResponse.copyStatusDescription; + } + + /** + * When a blob is leased, + * specifies whether the lease is of infinite or fixed duration. Possible + * values include: 'infinite', 'fixed'. + * + * @readonly + * @type {(LeaseDurationType | undefined)} + * @memberof BlobQueryResponse + */ + public get leaseDuration(): LeaseDurationType | undefined { + return this.originalResponse.leaseDuration; + } + + /** + * Lease state of the blob. Possible + * values include: 'available', 'leased', 'expired', 'breaking', 'broken'. + * + * @readonly + * @type {(LeaseStateType | undefined)} + * @memberof BlobQueryResponse + */ + public get leaseState(): LeaseStateType | undefined { + return this.originalResponse.leaseState; + } + + /** + * The current lease status of the + * blob. Possible values include: 'locked', 'unlocked'. + * + * @readonly + * @type {(LeaseStatusType | undefined)} + * @memberof BlobQueryResponse + */ + public get leaseStatus(): LeaseStatusType | undefined { + return this.originalResponse.leaseStatus; + } + + /** + * A UTC date/time value generated by the service that + * indicates the time at which the response was initiated. + * + * @readonly + * @type {(Date | undefined)} + * @memberof BlobQueryResponse + */ + public get date(): Date | undefined { + return this.originalResponse.date; + } + + /** + * The number of committed blocks + * present in the blob. This header is returned only for append blobs. + * + * @readonly + * @type {(number | undefined)} + * @memberof BlobQueryResponse + */ + public get blobCommittedBlockCount(): number | undefined { + return this.originalResponse.blobCommittedBlockCount; + } + + /** + * The ETag contains a value that you can use to + * perform operations conditionally, in quotes. + * + * @readonly + * @type {(string | undefined)} + * @memberof BlobQueryResponse + */ + public get etag(): string | undefined { + return this.originalResponse.etag; + } + + /** + * The error code. + * + * @readonly + * @type {(string | undefined)} + * @memberof BlobQueryResponse + */ + public get errorCode(): string | undefined { + return this.originalResponse.errorCode; + } + + /** + * The value of this header is set to + * true if the file data and application metadata are completely encrypted + * using the specified algorithm. Otherwise, the value is set to false (when + * the file is unencrypted, or if only parts of the file/application metadata + * are encrypted). + * + * @readonly + * @type {(boolean | undefined)} + * @memberof BlobQueryResponse + */ + public get isServerEncrypted(): boolean | undefined { + return this.originalResponse.isServerEncrypted; + } + + /** + * If the blob has a MD5 hash, and if + * request contains range header (Range or x-ms-range), this response header + * is returned with the value of the whole blob's MD5 value. This value may + * or may not be equal to the value returned in Content-MD5 header, with the + * latter calculated from the requested range. + * + * @readonly + * @type {(Uint8Array | undefined)} + * @memberof BlobQueryResponse + */ + public get blobContentMD5(): Uint8Array | undefined { + return this.originalResponse.blobContentMD5; + } + + /** + * Returns the date and time the file was last + * modified. Any operation that modifies the file or its properties updates + * the last modified time. + * + * @readonly + * @type {(Date | undefined)} + * @memberof BlobQueryResponse + */ + public get lastModified(): Date | undefined { + return this.originalResponse.lastModified; + } + + /** + * A name-value pair + * to associate with a file storage object. + * + * @readonly + * @type {(Metadata | undefined)} + * @memberof BlobQueryResponse + */ + public get metadata(): Metadata | undefined { + return this.originalResponse.metadata; + } + + /** + * This header uniquely identifies the request + * that was made and can be used for troubleshooting the request. + * + * @readonly + * @type {(string | undefined)} + * @memberof BlobQueryResponse + */ + public get requestId(): string | undefined { + return this.originalResponse.requestId; + } + + /** + * If a client request id header is sent in the request, this header will be present in the + * response with the same value. + * + * @readonly + * @type {(string | undefined)} + * @memberof BlobQueryResponse + */ + public get clientRequestId(): string | undefined { + return this.originalResponse.clientRequestId; + } + + /** + * Indicates the version of the File service used + * to execute the request. + * + * @readonly + * @type {(string | undefined)} + * @memberof BlobQueryResponse + */ + public get version(): string | undefined { + return this.originalResponse.version; + } + + /** + * The SHA-256 hash of the encryption key used to encrypt the blob. This value is only returned + * when the blob was encrypted with a customer-provided key. + * + * @readonly + * @type {(string | undefined)} + * @memberof BlobQueryResponse + */ + public get encryptionKeySha256(): string | undefined { + return this.originalResponse.encryptionKeySha256; + } + + /** + * If the request is to read a specified range and the x-ms-range-get-content-crc64 is set to + * true, then the request returns a crc64 for the range, as long as the range size is less than + * or equal to 4 MB. If both x-ms-range-get-content-crc64 & x-ms-range-get-content-md5 is + * specified in the same request, it will fail with 400(Bad Request) + * + * @type {(Uint8Array | undefined)} + * @memberof BlobQueryResponse + */ + public get contentCrc64(): Uint8Array | undefined { + return this.originalResponse.contentCrc64; + } + + /** + * The response body as a browser Blob. + * Always undefined in node.js. + * + * @readonly + * @type {(Promise | undefined)} + * @memberof BlobQueryResponse + */ + public get blobBody(): Promise | undefined { + return undefined; + } + + /** + * The response body as a node.js Readable stream. + * Always undefined in the browser. + * + * It will parse avor data returned by blob query. + * + * @readonly + * @type {(NodeJS.ReadableStream | undefined)} + * @memberof BlobQueryResponse + */ + public get readableStreamBody(): NodeJS.ReadableStream | undefined { + return isNode ? this.blobDownloadStream : undefined; + } + + /** + * The HTTP response. + * + * @type {HttpResponse} + * @memberof BlobQueryResponse + */ + public get _response(): HttpResponse & { + parsedHeaders: BlobDownloadHeaders; + } { + return this.originalResponse._response; + } + + private originalResponse: BlobQueryResponseModel; + private blobDownloadStream?: BlobQuickQueryStream; + + /** + * Creates an instance of BlobQueryResponse. + * + * @param {BlobQueryResponseModel} originalResponse + * @param {BlobQuickQueryStreamOptions} [options={}] + * @memberof BlobQueryResponse + */ + public constructor( + originalResponse: BlobQueryResponseModel, + options: BlobQuickQueryStreamOptions = {} + ) { + this.originalResponse = originalResponse; + this.blobDownloadStream = new BlobQuickQueryStream( + this.originalResponse.readableStreamBody!, + options + ); + } +} diff --git a/sdk/storage/storage-blob/src/Clients.ts b/sdk/storage/storage-blob/src/Clients.ts index 44450c0db394..b517d17a4b65 100644 --- a/sdk/storage/storage-blob/src/Clients.ts +++ b/sdk/storage/storage-blob/src/Clients.ts @@ -53,7 +53,8 @@ import { import { setURLParameter, extractConnectionStringParts, - appendToURLPath + appendToURLPath, + toQuerySerialization } from "./utils/utils.common"; import { fsStat, readStreamToLocalFile, streamToBuffer } from "./utils/utils.node"; import { StorageSharedKeyCredential } from "./credentials/StorageSharedKeyCredential"; @@ -135,6 +136,7 @@ import { ETagNone } from "./utils/constants"; import { truncatedISO8061Date } from "./utils/utils.common"; import "@azure/core-paging"; import { PagedAsyncIterableIterator, PageSettings } from "@azure/core-paging"; +import { BlobQueryResponse } from "./BlobQueryResponse"; /** * Options to configure the {@link BlobClient.beginCopyFromURL} operation. @@ -862,6 +864,7 @@ export class BlobClient extends StorageClient { * @memberof BlobClient */ private blobContext: StorageBlob; + private _name: string; private _containerName: string; @@ -1237,7 +1240,7 @@ export class BlobClient extends StorageClient { } /** - * Returns true if the Azrue blob resource represented by this client exists; false otherwise. + * Returns true if the Azure blob resource represented by this client exists; false otherwise. * * NOTE: use this function with care since an existing blob might be deleted by other clients or * applications. Vice versa new blobs might be added by other clients or applications after this @@ -2647,6 +2650,177 @@ export interface BlockBlobUploadOptions extends CommonOptions { tier?: BlockBlobTier | string; } +/** + * Blob query error type. + * + * @export + * @interface BlobQueryError + */ +export interface BlobQueryError { + /** + * Whether error is fatal. Fatal error will stop query. + * + * @type {boolean} + * @memberof BlobQueryError + */ + isFatal: boolean; + /** + * Error name. + * + * @type {string} + * @memberof BlobQueryError + */ + name: string; + /** + * Position in bytes of the query. + * + * @type {number} + * @memberof BlobQueryError + */ + position: number; + /** + * Error description. + * + * @type {string} + * @memberof BlobQueryError + */ + description: string; +} + +/** + * Base type for options to query blob. + * + * @export + * @interface BlobQueryTextConfiguration + */ +export interface BlobQueryTextConfiguration { + /** + * Record separator. + * + * @type {string} + * @memberof BlobQueryTextConfiguration + */ + recordSeparator: string; +} + +/** + * Options to query blob with JSON format. + * + * @export + * @interface BlobQueryJsonTextConfiguration + */ +export interface BlobQueryJsonTextConfiguration extends BlobQueryTextConfiguration { + /** + * Query for a JSON format blob. + * + * @type {"json"} + * @memberof BlobQueryJsonTextConfiguration + */ + kind: "json"; +} + +/** + * Options to query blob with CSV format. + * + * @export + * @interface BlobQueryCsvTextConfiguration + */ +export interface BlobQueryCsvTextConfiguration extends BlobQueryTextConfiguration { + /** + * Query for a CSV format blob. + * + * @type {"csv"} + * @memberof BlobQueryCsvTextConfiguration + */ + kind: "csv"; + /** + * Column separator. + * + * @type {string} + * @memberof BlobQueryCsvTextConfiguration + */ + columnSeparator: string; + /** + * Field quote. + * + * @type {string} + * @memberof BlobQueryCsvTextConfiguration + */ + fieldQuote?: string; + /** + * Escape character. + * + * @type {string} + * @memberof BlobQueryCsvTextConfiguration + */ + escapeCharacter?: string; + /** + * Has headers. + * + * @type {boolean} + * @memberof BlobQueryCsvTextConfiguration + */ + hasHeaders: boolean; +} + +/** + * Options to configure {@link BlockBlobClient.query} operation. + * + * @export + * @interface BlockBlobQueryOptions + */ +export interface BlockBlobQueryOptions extends CommonOptions { + /** + * An implementation of the `AbortSignalLike` interface to signal the request to cancel the operation. + * For example, use the @azure/abort-controller to create an `AbortSignal`. + * + * @type {AbortSignalLike} + * @memberof BlockBlobUploadOptions + */ + abortSignal?: AbortSignalLike; + /** + * Configurations for the query input. + * + * @type {BlobQueryJsonTextConfiguration | BlobQueryCsvTextConfiguration} + * @memberof BlockBlobQueryOptions + */ + inputTextConfiguration?: BlobQueryJsonTextConfiguration | BlobQueryCsvTextConfiguration; + /** + * Configurations for the query output. + * + * @type {BlobQueryJsonTextConfiguration | BlobQueryCsvTextConfiguration} + * @memberof BlockBlobQueryOptions + */ + outputTextConfiguration?: BlobQueryJsonTextConfiguration | BlobQueryCsvTextConfiguration; + /** + * Callback to receive events on the progress of query operation. + * + * @type {(progress: TransferProgressEvent) => void} + * @memberof BlockBlobUploadOptions + */ + onProgress?: (progress: TransferProgressEvent) => void; + /** + * Callback to receive error events during the query operaiton. + * + * @memberof BlockBlobQueryOptions + */ + onError?: (error: BlobQueryError) => void; + /** + * Conditions to meet when uploading to the block blob. + * + * @type {BlobRequestConditions} + * @memberof BlockBlobUploadOptions + */ + conditions?: BlobRequestConditions; + /** + * Customer Provided Key Info. + * + * @type {CpkInfo} + * @memberof BlockBlobUploadOptions + */ + customerProvidedKey?: CpkInfo; +} + /** * Options to configure {@link BlockBlobClient.stageBlock} operation. * @@ -3046,6 +3220,18 @@ export type BlobUploadCommonResponse = BlockBlobUploadHeaders & { * @extends {BlobClient} */ export class BlockBlobClient extends BlobClient { + /** + * blobContext provided by protocol layer. + * + * Note. Ideally BlobClient should set BlobClient.blobContext to protected. However, API + * extractor has issue blocking that. Here we redecelare _blobContext in BlockBlobClient. + * + * @private + * @type {Blobs} + * @memberof BlobClient + */ + private _blobContext: StorageBlob; + /** * blockBlobContext provided by protocol layer. * @@ -3200,6 +3386,7 @@ export class BlockBlobClient extends BlobClient { } super(url, pipeline); this.blockBlobContext = new BlockBlob(this.storageClientContext); + this._blobContext = new StorageBlob(this.storageClientContext); } /** @@ -3222,6 +3409,72 @@ export class BlockBlobClient extends BlobClient { ); } + /** + * Quick query for a JSON or CSV formated blob. + * + * Example usage (Node.js): + * + * ```js + * // Query and convert a blob to a string + * const queryBlockBlobResponse = await blockBlobClient.query("select * from BlobStorage"); + * const downloaded = await streamToString(queryBlockBlobResponse.readableStreamBody); + * console.log("Query blob content:", downloaded); + * + * async function streamToString(readableStream) { + * return new Promise((resolve, reject) => { + * const chunks = []; + * readableStream.on("data", (data) => { + * chunks.push(data.toString()); + * }); + * readableStream.on("end", () => { + * resolve(chunks.join("")); + * }); + * readableStream.on("error", reject); + * }); + * } + * ``` + * + * @param {string} query + * @param {BlockBlobQueryOptions} [options={}] + * @returns {Promise} + * @memberof BlockBlobClient + */ + public async query( + query: string, + options: BlockBlobQueryOptions = {} + ): Promise { + ensureCpkIfSpecified(options.customerProvidedKey, this.isHttps); + + const { span, spanOptions } = createSpan("BlobClient-query", options.tracingOptions); + + try { + const response = await this._blobContext.query({ + abortSignal: options.abortSignal, + queryRequest: { + expression: query, + inputSerialization: toQuerySerialization(options.inputTextConfiguration), + outputSerialization: toQuerySerialization(options.outputTextConfiguration) + }, + leaseAccessConditions: options.conditions, + modifiedAccessConditions: options.conditions, + spanOptions + }); + return new BlobQueryResponse(response, { + abortSignal: options.abortSignal, + onProgress: options.onProgress, + onError: options.onError + }); + } catch (e) { + span.setStatus({ + code: CanonicalCode.UNKNOWN, + message: e.message + }); + throw e; + } finally { + span.end(); + } + } + /** * Creates a new block blob, or updates the content of an existing block blob. * Updating an existing block blob overwrites any existing metadata on the blob. @@ -5868,7 +6121,7 @@ export class ContainerClient extends StorageClient { } /** - * Returns true if the Azrue container resource represented by this client exists; false otherwise. + * Returns true if the Azure container resource represented by this client exists; false otherwise. * * NOTE: use this function with care since an existing container might be deleted by other clients or * applications. Vice versa new containers with the same name might be added by other clients or diff --git a/sdk/storage/storage-blob/src/generatedModels.ts b/sdk/storage/storage-blob/src/generatedModels.ts index 5af194232027..ebd570f16948 100644 --- a/sdk/storage/storage-blob/src/generatedModels.ts +++ b/sdk/storage/storage-blob/src/generatedModels.ts @@ -40,6 +40,7 @@ export { BlobItemInternal as BlobItem, BlobPrefix, BlobDownloadHeaders, + BlobQueryResponse as BlobQueryResponseModel, BlobDownloadResponse as BlobDownloadResponseModel, BlobType, BlobUndeleteHeaders, diff --git a/sdk/storage/storage-blob/src/utils/BlobQuickQueryStream.ts b/sdk/storage/storage-blob/src/utils/BlobQuickQueryStream.ts new file mode 100644 index 000000000000..723a716108a3 --- /dev/null +++ b/sdk/storage/storage-blob/src/utils/BlobQuickQueryStream.ts @@ -0,0 +1,165 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +import { Readable } from "stream"; + +import { AbortError, AbortSignal, AbortSignalLike } from "@azure/abort-controller"; +import { TransferProgressEvent } from "@azure/core-http"; + +import { AvroReadableFromStream, AvroReader } from "../../../storage-internal-avro/src"; +import { BlobQueryError } from "../Clients"; + +export interface BlobQuickQueryStreamOptions { + /** + * An implementation of the `AbortSignalLike` interface to signal the request to cancel the operation. + * For example, use the @azure/abort-controller to create an `AbortSignal`. + * + * @type {AbortSignalLike} + * @memberof BlobQuickQueryStreamOptions + */ + abortSignal?: AbortSignalLike; + + /** + * Read progress event handler + * + * @memberof BlobQuickQueryStreamOptions + */ + onProgress?: (progress: TransferProgressEvent) => void; + + /** + * Callback to receive error events during the query operaiton. + * + * @memberof BlockBlobQueryOptions + */ + onError?: (error: BlobQueryError) => void; +} + +const ABORT_ERROR = new AbortError("The operation was aborted."); + +/** + * ONLY AVAILABLE IN NODE.JS RUNTIME. + * + * A Node.js BlobQuickQueryStream will internally parser avor data stream for blob query. + * + * @class BlobQuickQueryStream + * @extends {Readable} + */ +export class BlobQuickQueryStream extends Readable { + private aborter: AbortSignalLike; + private source: NodeJS.ReadableStream; + private avroReader: AvroReader; + private avroIter: AsyncIterableIterator; + private onProgress?: (progress: TransferProgressEvent) => void; + private onError?: (error: BlobQueryError) => void; + private abortHandler = () => { + // Workaround before avor reader doesn't support aborter + this.source.pause(); + this.source.removeAllListeners(); + // TODO: Avor reader supports aborter + this.emit("error", ABORT_ERROR); + }; + + /** + * Creates an instance of BlobQuickQueryStream. + * + * @param {NodeJS.ReadableStream} source The current ReadableStream returned from getter + * @param {BlobQuickQueryStreamOptions} [options={}] + * @memberof BlobQuickQueryStream + */ + public constructor(source: NodeJS.ReadableStream, options: BlobQuickQueryStreamOptions = {}) { + super(); + this.aborter = options.abortSignal || AbortSignal.none; + this.source = source; + this.onProgress = options.onProgress; + this.onError = options.onError; + this.avroReader = new AvroReader(new AvroReadableFromStream(this.source)); + this.avroIter = this.avroReader.parseObjects(); + + this.aborter.addEventListener("abort", this.abortHandler); + } + + public _read() { + if (!this.aborter.aborted) { + this.readInternal().catch((err) => { + this.emit("error", err); + }); + } + } + + private async readInternal() { + for await (const obj of this.avroIter) { + if (this.aborter.aborted) { + break; + } + + const schema = (obj as any).$schema; + if (typeof schema !== "string") { + throw Error("Missing schema in avor record."); + } + + let exit = false; + switch (schema) { + case "com.microsoft.azure.storage.queryBlobContents.resultData": + const data = (obj as any).data; + if (data instanceof Uint8Array === false) { + throw Error("Invalid data in avor result record."); + } + if (!this.push(Buffer.from(data))) { + exit = true; + } + break; + case "com.microsoft.azure.storage.queryBlobContents.progress": + const bytesScanned = (obj as any).bytesScanned; + if (typeof bytesScanned !== "number") { + throw Error("Invalid bytesScanned in avor progress record."); + } + if (this.onProgress) { + this.onProgress({ loadedBytes: bytesScanned }); + } + break; + case "com.microsoft.azure.storage.queryBlobContents.end": + if (this.onProgress) { + const totalBytes = (obj as any).totalBytes; + if (typeof totalBytes !== "number") { + throw Error("Invalid totalBytes in avor end record."); + } + this.onProgress({ loadedBytes: totalBytes }); + } + this.push(null); + break; + case "com.microsoft.azure.storage.queryBlobContents.error": + if (this.onError) { + const fatal = (obj as any).fatal; + if (typeof fatal !== "boolean") { + throw Error("Invalid fatal in avor error record."); + } + const name = (obj as any).name; + if (typeof name !== "string") { + throw Error("Invalid name in avor error record."); + } + const description = (obj as any).description; + if (typeof description !== "string") { + throw Error("Invalid description in avor error record."); + } + const position = (obj as any).position; + if (typeof position !== "number") { + throw Error("Invalid position in avor error record."); + } + this.onError({ + position, + name, + isFatal: fatal, + description + }); + } + break; + default: + throw Error(`Unknown schema ${schema} in avor progress record.`); + } + + if (exit) { + break; + } + } + } +} diff --git a/sdk/storage/storage-blob/src/utils/utils.common.ts b/sdk/storage/storage-blob/src/utils/utils.common.ts index 1ee10e9b2d97..72fc4f6fef35 100644 --- a/sdk/storage/storage-blob/src/utils/utils.common.ts +++ b/sdk/storage/storage-blob/src/utils/utils.common.ts @@ -3,7 +3,10 @@ import { AbortSignalLike } from "@azure/abort-controller"; import { HttpHeaders, isNode, URLBuilder } from "@azure/core-http"; -import { HeaderConstants, URLConstants, DevelopmentConnectionString } from "./constants"; + +import { BlobQueryCsvTextConfiguration, BlobQueryJsonTextConfiguration } from "../Clients"; +import { QuerySerialization } from "../generated/src/models"; +import { DevelopmentConnectionString, HeaderConstants, URLConstants } from "./constants"; /** * Reserved URL characters must be properly escaped for Storage services like Blob or File. @@ -550,3 +553,45 @@ export function getAccountNameFromUrl(url: string): string { throw new Error("Unable to extract accountName with provided information."); } } + +/** + * Convert BlobQueryTextConfiguration to QuerySerialization type. + * + * @export + * @param {(BlobQueryJsonTextConfiguration | BlobQueryCsvTextConfiguration)} [textConfiguration] + * @returns {(QuerySerialization | undefined)} + */ +export function toQuerySerialization( + textConfiguration?: BlobQueryJsonTextConfiguration | BlobQueryCsvTextConfiguration +): QuerySerialization | undefined { + if (textConfiguration === undefined) { + return undefined; + } + + switch (textConfiguration.kind) { + case "csv": + return { + format: { + type: "delimited", + delimitedTextConfiguration: { + columnSeparator: textConfiguration.columnSeparator, + fieldQuote: textConfiguration.fieldQuote || "", + recordSeparator: textConfiguration.recordSeparator, + escapeChar: textConfiguration.escapeCharacter || "", + headersPresent: textConfiguration.hasHeaders + } + } + }; + case "json": + return { + format: { + type: "json", + jsonTextConfiguration: { + recordSeparator: textConfiguration.recordSeparator + } + } + }; + default: + throw Error("Invalid BlobQueryTextConfiguration."); + } +} diff --git a/sdk/storage/storage-blob/test/node/blobclient.spec.ts b/sdk/storage/storage-blob/test/node/blobclient.spec.ts index 9bbaa5fb972b..290c5165657e 100644 --- a/sdk/storage/storage-blob/test/node/blobclient.spec.ts +++ b/sdk/storage/storage-blob/test/node/blobclient.spec.ts @@ -1,26 +1,32 @@ import * as assert from "assert"; - -import { isNode } from "@azure/core-http"; import * as dotenv from "dotenv"; +import { readFileSync, unlinkSync } from "fs"; +import { join } from "path"; + +import { AbortController } from "@azure/abort-controller"; +import { isNode, TokenCredential } from "@azure/core-http"; +import { delay, record } from "@azure/test-utils-recorder"; + import { BlobClient, - newPipeline, - StorageSharedKeyCredential, - ContainerClient, + BlobSASPermissions, + BlobServiceClient, BlockBlobClient, + ContainerClient, generateBlobSASQueryParameters, - BlobSASPermissions, - BlobServiceClient + newPipeline, + StorageSharedKeyCredential } from "../../src"; import { bodyToString, + createRandomLocalFile, getBSU, getConnectionStringFromEnvironment, recorderEnvSetup } from "../utils"; -import { TokenCredential } from "@azure/core-http"; import { assertClientUsesTokenCredential } from "../utils/assert"; -import { record, delay } from "@azure/test-utils-recorder"; +import { readStreamToLocalFileWithLogs } from "../utils/testutils.node"; + dotenv.config(); describe("BlobClient Node.js only", () => { @@ -30,6 +36,7 @@ describe("BlobClient Node.js only", () => { let blobClient: BlobClient; let blockBlobClient: BlockBlobClient; const content = "Hello World"; + const tempFolderPath = "temp"; let recorder: any; @@ -339,4 +346,282 @@ describe("BlobClient Node.js only", () => { const result = await newClient.getProperties(); assert.deepStrictEqual(result.metadata, metadata); }); + + it.only("query should work", async () => { + recorder.skip("node", "TODO: Avor reader doesn't work with recorder yet."); + const csvContent = "100,200,300,400\n150,250,350,450\n"; + await blockBlobClient.upload(csvContent, csvContent.length); + + const response = await blockBlobClient.query("select * from BlobStorage"); + assert.deepStrictEqual(await bodyToString(response), csvContent); + }); + + it.only("query should work with access conditions", async () => { + recorder.skip("node", "TODO: Avor reader doesn't work with recorder yet."); + const csvContent = "100,200,300,400\n150,250,350,450\n"; + const uploadResponse = await blockBlobClient.upload(csvContent, csvContent.length); + + const response = await blockBlobClient.query("select * from BlobStorage", { + conditions: { + ifModifiedSince: new Date("2010/01/01"), + ifUnmodifiedSince: new Date("2100/01/01"), + ifMatch: uploadResponse.etag, + ifNoneMatch: "invalidetag" + } + }); + assert.deepStrictEqual(await bodyToString(response), csvContent); + }); + + it.only("query should not work with access conditions ifModifiedSince", async () => { + recorder.skip("node", "TODO: Avor reader doesn't work with recorder yet."); + const csvContent = "100,200,300,400\n150,250,350,450\n"; + await blockBlobClient.upload(csvContent, csvContent.length); + + try { + await blockBlobClient.query("select * from BlobStorage", { + conditions: { + ifModifiedSince: new Date("2100/01/01") + } + }); + } catch (err) { + assert.deepStrictEqual(err.statusCode, 304); + return; + } + assert.fail(); + }); + + it.only("query should not work with access conditions leaseId", async () => { + recorder.skip("node", "TODO: Avor reader doesn't work with recorder yet."); + const csvContent = "100,200,300,400\n150,250,350,450\n"; + await blockBlobClient.upload(csvContent, csvContent.length); + + try { + await blockBlobClient.query("select * from BlobStorage", { + conditions: { + leaseId: "invalid" + } + }); + } catch (err) { + assert.deepStrictEqual(err.statusCode, 400); + return; + } + assert.fail(); + }); + + it.only("query should work with snapshot", async () => { + recorder.skip("node", "TODO: Avor reader doesn't work with recorder yet."); + const csvContent = "100,200,300,400\n150,250,350,450\n"; + await blockBlobClient.upload(csvContent, csvContent.length); + const snapshotResponse = await blockBlobClient.createSnapshot(); + const blockBlobSnapshotClient = blockBlobClient.withSnapshot(snapshotResponse.snapshot!); + + const response = await blockBlobSnapshotClient.query("select * from BlobStorage"); + assert.deepStrictEqual(await bodyToString(response), csvContent); + }); + + it.only("query should work with where conditionals", async () => { + recorder.skip("node", "TODO: Avor reader doesn't work with recorder yet."); + const csvContent = "100,200,300,400\n150,250,350,450\n"; + await blockBlobClient.upload(csvContent, csvContent.length); + + const response = await blockBlobClient.query("select _2 from BlobStorage where _1 > 100"); + assert.deepStrictEqual(await bodyToString(response), "250\n"); + }); + + it.only("query should work with empty results", async () => { + recorder.skip("node", "TODO: Avor reader doesn't work with recorder yet."); + const csvContent = "100,200,300,400\n150,250,350,450\n"; + await blockBlobClient.upload(csvContent, csvContent.length); + + const response = await blockBlobClient.query("select _2 from BlobStorage where _1 > 200"); + + assert.deepStrictEqual(await bodyToString(response), ""); + }); + + it.only("query should work with blob properties", async () => { + recorder.skip("node", "TODO: Avor reader doesn't work with recorder yet."); + const csvContent = "100,200,300,400\n150,250,350,450\n"; + await blockBlobClient.upload(csvContent, csvContent.length); + + const response = await blockBlobClient.query("select * from BlobStorage"); + assert.deepStrictEqual(response.contentType, "avro/binary"); + assert.deepStrictEqual(typeof response.etag, "string"); + assert.deepStrictEqual(response.blobType, "BlockBlob"); + assert.deepStrictEqual(response.leaseState, "available"); + assert.deepStrictEqual(response.leaseStatus, "unlocked"); + assert.deepStrictEqual(response.acceptRanges, "bytes"); + assert.deepStrictEqual(typeof response.clientRequestId, "string"); + assert.deepStrictEqual(typeof response.requestId, "string"); + assert.deepStrictEqual(typeof response.version, "string"); + assert.deepStrictEqual(typeof response.date, "object"); + }); + + it.only("query should work with large file", async () => { + recorder.skip("node", "TODO: Avor reader doesn't work with recorder yet."); + // TODO: Avor reader emiter listener leak MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 error listeners added. Use emitter.setMaxListeners() to increase limit + const csvContentUnit = "100,200,300,400\n150,250,350,450\n"; + const tempFileLarge = await createRandomLocalFile( + tempFolderPath, + 1024 * 1024, + Buffer.from(csvContentUnit) + ); + await blockBlobClient.uploadFile(tempFileLarge); + + const response = await blockBlobClient.query("select * from BlobStorage"); + + const downloadedFile = join(tempFolderPath, recorder.getUniqueName("downloadfile.")); + await readStreamToLocalFileWithLogs(response.readableStreamBody!, downloadedFile); + + const downloadedData = await readFileSync(downloadedFile); + const uploadedData = await readFileSync(tempFileLarge); + + unlinkSync(downloadedFile); + unlinkSync(tempFileLarge); + + assert.ok(downloadedData.equals(uploadedData)); + }); + + it.only("query should work with aborter", async () => { + recorder.skip("node", "TODO: Avor reader doesn't work with recorder yet."); + const csvContentUnit = "100,200,300,400\n150,250,350,450\n"; + const tempFileLarge = await createRandomLocalFile( + tempFolderPath, + 1024 * 256 * 2, + Buffer.from(csvContentUnit) + ); + await blockBlobClient.uploadFile(tempFileLarge); + + const aborter = new AbortController(); + const response = await blockBlobClient.query("select * from BlobStorage", { + abortSignal: aborter.signal, + onProgress: () => { + // Abort parser when first progress event trigger (by default 4MB) + aborter.abort(); + } + }); + + const downloadedFile = join(tempFolderPath, recorder.getUniqueName("downloadfile.")); + + try { + await readStreamToLocalFileWithLogs(response.readableStreamBody!, downloadedFile); + } catch (error) { + // TODO: Avor reader should abort reading from internal stream + assert.deepStrictEqual(error.name, "AbortError"); + unlinkSync(downloadedFile); + return; + } + assert.fail(); + }); + + it.only("query should work with progress event", async () => { + recorder.skip("node", "TODO: Avor reader doesn't work with recorder yet."); + const csvContent = "100,200,300,400\n150,250,350,450\n"; + await blockBlobClient.upload(csvContent, csvContent.length); + + await new Promise((resolve, reject) => { + blockBlobClient + .query("select * from BlobStorage", { + onProgress: (progress) => { + assert.deepStrictEqual(progress.loadedBytes, csvContent.length); + resolve(); + } + }) + .then((response) => { + return bodyToString(response); + }) + .then((_data) => {}) + .catch(reject); + }); + }); + + it.only("query should work with fatal error event", async () => { + recorder.skip("node", "TODO: Avor reader doesn't work with recorder yet."); + const csvContent = "100,200,300,400\n150,250,350,450\n"; + await blockBlobClient.upload(csvContent, csvContent.length); + + const response = await blockBlobClient.query("select * from BlobStorage", { + inputTextConfiguration: { + kind: "json", + recordSeparator: "\n" + }, + onError: (err) => { + assert.deepStrictEqual(err.isFatal, true); + assert.deepStrictEqual(err.name, "ParseError"); + assert.deepStrictEqual(err.position, 0); + assert.deepStrictEqual( + err.description, + "Unexpected token ',' at [byte: 3]. Expecting tokens '{', or '['." + ); + return; + } + }); + assert.deepStrictEqual(await bodyToString(response), "\n"); + }); + + it.only("query should work with non fatal error event", async () => { + recorder.skip("node", "TODO: Avor reader doesn't work with recorder yet."); + const csvContent = "100,hello,300,400\n150,250,350,450\n"; + await blockBlobClient.upload(csvContent, csvContent.length); + + const response = await blockBlobClient.query("select _2 from BlobStorage where _2 > 100", { + onError: (err) => { + assert.deepStrictEqual(err.isFatal, false); + assert.deepStrictEqual(err.name, "InvalidTypeConversion"); + assert.deepStrictEqual(err.position, 0); + assert.deepStrictEqual(err.description, "Invalid type conversion."); + return; + } + }); + assert.deepStrictEqual(await bodyToString(response), "250\n"); + }); + + it.only("query should work with CSV input and output configurations", async () => { + recorder.skip("node", "TODO: Avor reader doesn't work with recorder yet."); + const csvContent = "100.200.300.400!150.250.350.450!180.280.380.480!"; + await blockBlobClient.upload(csvContent, csvContent.length); + + const response = await blockBlobClient.query("select _1 from BlobStorage", { + inputTextConfiguration: { + kind: "csv", + recordSeparator: "!", + columnSeparator: ".", + // escapeCharacter: "\\", // What does this do? + // fieldQuote: '"', // What does this do? + hasHeaders: true + }, + outputTextConfiguration: { + kind: "csv", + recordSeparator: "!", + columnSeparator: ".", + // escapeCharacter: "\\", + // fieldQuote: '"', + hasHeaders: false + } + }); + assert.deepStrictEqual(await bodyToString(response), "150!180!"); + }); + + it.only("query should work with JSON input and output configurations", async () => { + recorder.skip("node", "TODO: Avor reader doesn't work with recorder yet."); + const recordSeparator = "\n"; + const jsonContent = + [ + JSON.stringify({ _1: "100", _2: "200", _3: "300", _4: "400" }), + JSON.stringify({ _1: "150", _2: "250", _3: "350", _4: "450" }), + JSON.stringify({ _1: "180", _2: "280", _3: "380", _4: "480" }) + ].join(recordSeparator) + recordSeparator; + await blockBlobClient.upload(jsonContent, jsonContent.length); + + const response = await blockBlobClient.query("select * from BlobStorage", { + inputTextConfiguration: { + kind: "json", + recordSeparator + }, + outputTextConfiguration: { + kind: "json", + recordSeparator + } + }); + assert.deepStrictEqual(await bodyToString(response), jsonContent); + }); }); diff --git a/sdk/storage/storage-blob/test/utils/index.ts b/sdk/storage/storage-blob/test/utils/index.ts index 7a633cda4dab..9102fee4d381 100644 --- a/sdk/storage/storage-blob/test/utils/index.ts +++ b/sdk/storage/storage-blob/test/utils/index.ts @@ -145,17 +145,33 @@ export async function bodyToString( }); } +export async function createRandomLocalFile( + folder: string, + blockNumber: number, + blockContent: Buffer +): Promise; export async function createRandomLocalFile( folder: string, blockNumber: number, blockSize: number +): Promise; +export async function createRandomLocalFile( + folder: string, + blockNumber: number, + blockSizeOrContent: number | Buffer ): Promise { return new Promise((resolve, reject) => { const destFile = path.join(folder, getUniqueName("tempfile.")); const ws = fs.createWriteStream(destFile); let offsetInMB = 0; - function randomValueHex(len = blockSize) { + function randomValueHex() { + if (blockSizeOrContent instanceof Buffer) { + return blockSizeOrContent; + } + + const len = blockSizeOrContent; + return randomBytes(Math.ceil(len / 2)) .toString("hex") // convert to hexadecimal format .slice(0, len); // return required number of characters @@ -163,7 +179,7 @@ export async function createRandomLocalFile( ws.on("open", () => { // tslint:disable-next-line:no-empty - while (offsetInMB++ < blockNumber && ws.write(randomValueHex())) { } + while (offsetInMB++ < blockNumber && ws.write(randomValueHex())) {} if (offsetInMB >= blockNumber) { ws.end(); } @@ -171,7 +187,7 @@ export async function createRandomLocalFile( ws.on("drain", () => { // tslint:disable-next-line:no-empty - while (offsetInMB++ < blockNumber && ws.write(randomValueHex())) { } + while (offsetInMB++ < blockNumber && ws.write(randomValueHex())) {} if (offsetInMB >= blockNumber) { ws.end(); } diff --git a/sdk/storage/storage-internal-avro/package.json b/sdk/storage/storage-internal-avro/package.json index 9e628455afe0..9305da527195 100644 --- a/sdk/storage/storage-internal-avro/package.json +++ b/sdk/storage/storage-internal-avro/package.json @@ -68,7 +68,7 @@ "puppeteer": "^2.0.0", "rimraf": "^3.0.0", "rollup": "^1.16.3", - "@rollup/plugin-commonjs": "^11.0.1", + "@rollup/plugin-commonjs": "11.0.2", "@rollup/plugin-node-resolve": "^7.0.0", "rollup-plugin-shim": "^1.0.0", "rollup-plugin-sourcemaps": "^0.4.2", @@ -79,4 +79,4 @@ "typescript": "~3.8.3", "util": "^0.12.1" } -} \ No newline at end of file +} From 399d54da31b58c0d9439f4a6655f6d81312d29cf Mon Sep 17 00:00:00 2001 From: XiaoningLiu Date: Tue, 26 May 2020 18:54:36 +0800 Subject: [PATCH 2/5] [Storage] Quick query API review --- .../storage-blob/review/storage-blob.api.md | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/sdk/storage/storage-blob/review/storage-blob.api.md b/sdk/storage/storage-blob/review/storage-blob.api.md index 838547c2083c..a347c7889a19 100644 --- a/sdk/storage/storage-blob/review/storage-blob.api.md +++ b/sdk/storage/storage-blob/review/storage-blob.api.md @@ -769,6 +769,44 @@ export interface BlobProperties { tagCount?: number; } +// @public +export interface BlobQueryCsvTextConfiguration extends BlobQueryTextConfiguration { + columnSeparator: string; + escapeCharacter?: string; + fieldQuote?: string; + hasHeaders: boolean; + kind: "csv"; +} + +// @public +export interface BlobQueryError { + description: string; + isFatal: boolean; + name: string; + position: number; +} + +// @public +export interface BlobQueryJsonTextConfiguration extends BlobQueryTextConfiguration { + kind: "json"; +} + +// Warning: (ae-forgotten-export) The symbol "BlobQueryHeaders" needs to be exported by the entry point index.d.ts +// +// @public +export type BlobQueryResponseModel = BlobQueryHeaders & { + blobBody?: Promise; + readableStreamBody?: NodeJS.ReadableStream; + _response: coreHttp.HttpResponse & { + parsedHeaders: BlobQueryHeaders; + }; +}; + +// @public +export interface BlobQueryTextConfiguration { + recordSeparator: string; +} + // @public export interface BlobReleaseLeaseOptions extends CommonOptions { abortSignal?: AbortSignalLike; @@ -1023,6 +1061,7 @@ export class BlockBlobClient extends BlobClient { constructor(url: string, pipeline: Pipeline); commitBlockList(blocks: string[], options?: BlockBlobCommitBlockListOptions): Promise; getBlockList(listType: BlockListType, options?: BlockBlobGetBlockListOptions): Promise; + query(query: string, options?: BlockBlobQueryOptions): Promise; stageBlock(blockId: string, body: HttpRequestBody, contentLength: number, options?: BlockBlobStageBlockOptions): Promise; stageBlockFromURL(blockId: string, sourceURL: string, offset?: number, count?: number, options?: BlockBlobStageBlockFromURLOptions): Promise; upload(body: HttpRequestBody, contentLength: number, options?: BlockBlobUploadOptions): Promise; @@ -1112,6 +1151,17 @@ export interface BlockBlobParallelUploadOptions extends CommonOptions { onProgress?: (progress: TransferProgressEvent) => void; } +// @public +export interface BlockBlobQueryOptions extends CommonOptions { + abortSignal?: AbortSignalLike; + conditions?: BlobRequestConditions; + customerProvidedKey?: CpkInfo; + inputTextConfiguration?: BlobQueryJsonTextConfiguration | BlobQueryCsvTextConfiguration; + onError?: (error: BlobQueryError) => void; + onProgress?: (progress: TransferProgressEvent) => void; + outputTextConfiguration?: BlobQueryJsonTextConfiguration | BlobQueryCsvTextConfiguration; +} + // @public export interface BlockBlobStageBlockFromURLHeaders { clientRequestId?: string; From 3426d3ac99cbf976a364c0e87273efc8bdeab205 Mon Sep 17 00:00:00 2001 From: XiaoningLiu Date: Tue, 26 May 2020 18:54:53 +0800 Subject: [PATCH 3/5] [Storage] Quick query recording --- ..._with_access_conditions_ifmodifiedsince.js | 66 +++++++++++++++++++ ...not_work_with_access_conditions_leaseid.js | 66 +++++++++++++++++++ .../recording_query_should_work.js | 66 +++++++++++++++++++ ...ecording_query_should_work_with_aborter.js | 66 +++++++++++++++++++ ...uery_should_work_with_access_conditions.js | 66 +++++++++++++++++++ ..._query_should_work_with_blob_properties.js | 66 +++++++++++++++++++ ...ith_csv_input_and_output_configurations.js | 66 +++++++++++++++++++ ...ng_query_should_work_with_empty_results.js | 66 +++++++++++++++++++ ...uery_should_work_with_fatal_error_event.js | 66 +++++++++++++++++++ ...th_json_input_and_output_configurations.js | 66 +++++++++++++++++++ ...rding_query_should_work_with_large_file.js | 66 +++++++++++++++++++ ..._should_work_with_non_fatal_error_event.js | 66 +++++++++++++++++++ ...g_query_should_work_with_progress_event.js | 66 +++++++++++++++++++ ...cording_query_should_work_with_snapshot.js | 66 +++++++++++++++++++ ...ery_should_work_with_where_conditionals.js | 66 +++++++++++++++++++ 15 files changed, 990 insertions(+) create mode 100644 sdk/storage/storage-blob/recordings/node/blobclient_nodejs_only/recording_query_should_not_work_with_access_conditions_ifmodifiedsince.js create mode 100644 sdk/storage/storage-blob/recordings/node/blobclient_nodejs_only/recording_query_should_not_work_with_access_conditions_leaseid.js create mode 100644 sdk/storage/storage-blob/recordings/node/blobclient_nodejs_only/recording_query_should_work.js create mode 100644 sdk/storage/storage-blob/recordings/node/blobclient_nodejs_only/recording_query_should_work_with_aborter.js create mode 100644 sdk/storage/storage-blob/recordings/node/blobclient_nodejs_only/recording_query_should_work_with_access_conditions.js create mode 100644 sdk/storage/storage-blob/recordings/node/blobclient_nodejs_only/recording_query_should_work_with_blob_properties.js create mode 100644 sdk/storage/storage-blob/recordings/node/blobclient_nodejs_only/recording_query_should_work_with_csv_input_and_output_configurations.js create mode 100644 sdk/storage/storage-blob/recordings/node/blobclient_nodejs_only/recording_query_should_work_with_empty_results.js create mode 100644 sdk/storage/storage-blob/recordings/node/blobclient_nodejs_only/recording_query_should_work_with_fatal_error_event.js create mode 100644 sdk/storage/storage-blob/recordings/node/blobclient_nodejs_only/recording_query_should_work_with_json_input_and_output_configurations.js create mode 100644 sdk/storage/storage-blob/recordings/node/blobclient_nodejs_only/recording_query_should_work_with_large_file.js create mode 100644 sdk/storage/storage-blob/recordings/node/blobclient_nodejs_only/recording_query_should_work_with_non_fatal_error_event.js create mode 100644 sdk/storage/storage-blob/recordings/node/blobclient_nodejs_only/recording_query_should_work_with_progress_event.js create mode 100644 sdk/storage/storage-blob/recordings/node/blobclient_nodejs_only/recording_query_should_work_with_snapshot.js create mode 100644 sdk/storage/storage-blob/recordings/node/blobclient_nodejs_only/recording_query_should_work_with_where_conditionals.js diff --git a/sdk/storage/storage-blob/recordings/node/blobclient_nodejs_only/recording_query_should_not_work_with_access_conditions_ifmodifiedsince.js b/sdk/storage/storage-blob/recordings/node/blobclient_nodejs_only/recording_query_should_not_work_with_access_conditions_ifmodifiedsince.js new file mode 100644 index 000000000000..9d20893531fa --- /dev/null +++ b/sdk/storage/storage-blob/recordings/node/blobclient_nodejs_only/recording_query_should_not_work_with_access_conditions_ifmodifiedsince.js @@ -0,0 +1,66 @@ +let nock = require('nock'); + +module.exports.hash = "d3a5406b3267717674c01d19eb9a0cd6"; + +module.exports.testInfo = {"uniqueName":{"container":"container159049020600707788","blob":"blob159049020631105138"},"newDate":{}} + +nock('https://fakestorageaccount.blob.core.windows.net:443', {"encodedQueryParams":true}) + .put('/container159049020600707788') + .query(true) + .reply(201, "", [ 'Transfer-Encoding', + 'chunked', + 'Last-Modified', + 'Tue, 26 May 2020 10:50:06 GMT', + 'ETag', + '"0x8D801628D44D052"', + 'Server', + 'Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0', + 'x-ms-request-id', + '1abc43fc-101e-000c-484b-3321fe000000', + 'x-ms-client-request-id', + 'd44ec425-3d70-4b48-bbfd-500af3079a59', + 'x-ms-version', + '2019-12-12', + 'Date', + 'Tue, 26 May 2020 10:50:05 GMT' ]); + +nock('https://fakestorageaccount.blob.core.windows.net:443', {"encodedQueryParams":true}) + .put('/container159049020600707788/blob159049020631105138', "Hello World") + .reply(201, "", [ 'Transfer-Encoding', + 'chunked', + 'Content-MD5', + 'sQqNsWTgdUEFt6mb5y4/5Q==', + 'Last-Modified', + 'Tue, 26 May 2020 10:50:06 GMT', + 'ETag', + '"0x8D801628D735F65"', + 'Server', + 'Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0', + 'x-ms-request-id', + '1abc43fe-101e-000c-494b-3321fe000000', + 'x-ms-client-request-id', + 'c0445632-63ee-4873-85d8-3ec4ffd1c1af', + 'x-ms-version', + '2019-12-12', + 'x-ms-content-crc64', + 'YeJLfssylmU=', + 'x-ms-request-server-encrypted', + 'true', + 'Date', + 'Tue, 26 May 2020 10:50:06 GMT' ]); + +nock('https://fakestorageaccount.blob.core.windows.net:443', {"encodedQueryParams":true}) + .delete('/container159049020600707788') + .query(true) + .reply(202, "", [ 'Transfer-Encoding', + 'chunked', + 'Server', + 'Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0', + 'x-ms-request-id', + '1abc43ff-101e-000c-4a4b-3321fe000000', + 'x-ms-client-request-id', + 'c5e790f4-af27-4916-b2cd-73923c270e28', + 'x-ms-version', + '2019-12-12', + 'Date', + 'Tue, 26 May 2020 10:50:06 GMT' ]); diff --git a/sdk/storage/storage-blob/recordings/node/blobclient_nodejs_only/recording_query_should_not_work_with_access_conditions_leaseid.js b/sdk/storage/storage-blob/recordings/node/blobclient_nodejs_only/recording_query_should_not_work_with_access_conditions_leaseid.js new file mode 100644 index 000000000000..0b06ff09f2ff --- /dev/null +++ b/sdk/storage/storage-blob/recordings/node/blobclient_nodejs_only/recording_query_should_not_work_with_access_conditions_leaseid.js @@ -0,0 +1,66 @@ +let nock = require('nock'); + +module.exports.hash = "fdc46dc8a237d6cac45f8cfb615e46d8"; + +module.exports.testInfo = {"uniqueName":{"container":"container159049020712800511","blob":"blob159049020739900152"},"newDate":{}} + +nock('https://fakestorageaccount.blob.core.windows.net:443', {"encodedQueryParams":true}) + .put('/container159049020712800511') + .query(true) + .reply(201, "", [ 'Transfer-Encoding', + 'chunked', + 'Last-Modified', + 'Tue, 26 May 2020 10:50:07 GMT', + 'ETag', + '"0x8D801628DEFAE67"', + 'Server', + 'Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0', + 'x-ms-request-id', + '1abc4400-101e-000c-4b4b-3321fe000000', + 'x-ms-client-request-id', + '1d191125-143c-44e9-88d9-f32d56e36f07', + 'x-ms-version', + '2019-12-12', + 'Date', + 'Tue, 26 May 2020 10:50:06 GMT' ]); + +nock('https://fakestorageaccount.blob.core.windows.net:443', {"encodedQueryParams":true}) + .put('/container159049020712800511/blob159049020739900152', "Hello World") + .reply(201, "", [ 'Transfer-Encoding', + 'chunked', + 'Content-MD5', + 'sQqNsWTgdUEFt6mb5y4/5Q==', + 'Last-Modified', + 'Tue, 26 May 2020 10:50:07 GMT', + 'ETag', + '"0x8D801628E193334"', + 'Server', + 'Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0', + 'x-ms-request-id', + '1abc4403-101e-000c-4c4b-3321fe000000', + 'x-ms-client-request-id', + '80421d4f-2379-421e-93de-296fa2093306', + 'x-ms-version', + '2019-12-12', + 'x-ms-content-crc64', + 'YeJLfssylmU=', + 'x-ms-request-server-encrypted', + 'true', + 'Date', + 'Tue, 26 May 2020 10:50:07 GMT' ]); + +nock('https://fakestorageaccount.blob.core.windows.net:443', {"encodedQueryParams":true}) + .delete('/container159049020712800511') + .query(true) + .reply(202, "", [ 'Transfer-Encoding', + 'chunked', + 'Server', + 'Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0', + 'x-ms-request-id', + '1abc4404-101e-000c-4d4b-3321fe000000', + 'x-ms-client-request-id', + '1a7d9c26-1258-45d6-b9dc-6a93fe8a06db', + 'x-ms-version', + '2019-12-12', + 'Date', + 'Tue, 26 May 2020 10:50:07 GMT' ]); diff --git a/sdk/storage/storage-blob/recordings/node/blobclient_nodejs_only/recording_query_should_work.js b/sdk/storage/storage-blob/recordings/node/blobclient_nodejs_only/recording_query_should_work.js new file mode 100644 index 000000000000..a95c400c9aea --- /dev/null +++ b/sdk/storage/storage-blob/recordings/node/blobclient_nodejs_only/recording_query_should_work.js @@ -0,0 +1,66 @@ +let nock = require('nock'); + +module.exports.hash = "b15cda823b3c55e24298837de650620e"; + +module.exports.testInfo = {"uniqueName":{"container":"container159049020245804719","blob":"blob159049020444905391"},"newDate":{}} + +nock('https://fakestorageaccount.blob.core.windows.net:443', {"encodedQueryParams":true}) + .put('/container159049020245804719') + .query(true) + .reply(201, "", [ 'Transfer-Encoding', + 'chunked', + 'Last-Modified', + 'Tue, 26 May 2020 10:50:04 GMT', + 'ETag', + '"0x8D801628C2B64CF"', + 'Server', + 'Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0', + 'x-ms-request-id', + '1abc43f1-101e-000c-424b-3321fe000000', + 'x-ms-client-request-id', + '3e11d2ab-1a6f-4b8f-8c69-e072dcdf0dc8', + 'x-ms-version', + '2019-12-12', + 'Date', + 'Tue, 26 May 2020 10:50:03 GMT' ]); + +nock('https://fakestorageaccount.blob.core.windows.net:443', {"encodedQueryParams":true}) + .put('/container159049020245804719/blob159049020444905391', "Hello World") + .reply(201, "", [ 'Transfer-Encoding', + 'chunked', + 'Content-MD5', + 'sQqNsWTgdUEFt6mb5y4/5Q==', + 'Last-Modified', + 'Tue, 26 May 2020 10:50:04 GMT', + 'ETag', + '"0x8D801628C58465E"', + 'Server', + 'Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0', + 'x-ms-request-id', + '1abc43f4-101e-000c-434b-3321fe000000', + 'x-ms-client-request-id', + '463390ef-7745-4351-a496-80ee2e8534b9', + 'x-ms-version', + '2019-12-12', + 'x-ms-content-crc64', + 'YeJLfssylmU=', + 'x-ms-request-server-encrypted', + 'true', + 'Date', + 'Tue, 26 May 2020 10:50:04 GMT' ]); + +nock('https://fakestorageaccount.blob.core.windows.net:443', {"encodedQueryParams":true}) + .delete('/container159049020245804719') + .query(true) + .reply(202, "", [ 'Transfer-Encoding', + 'chunked', + 'Server', + 'Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0', + 'x-ms-request-id', + '1abc43f6-101e-000c-444b-3321fe000000', + 'x-ms-client-request-id', + '6a16e255-713e-4358-a91b-514c044c7a56', + 'x-ms-version', + '2019-12-12', + 'Date', + 'Tue, 26 May 2020 10:50:04 GMT' ]); diff --git a/sdk/storage/storage-blob/recordings/node/blobclient_nodejs_only/recording_query_should_work_with_aborter.js b/sdk/storage/storage-blob/recordings/node/blobclient_nodejs_only/recording_query_should_work_with_aborter.js new file mode 100644 index 000000000000..109a26357882 --- /dev/null +++ b/sdk/storage/storage-blob/recordings/node/blobclient_nodejs_only/recording_query_should_work_with_aborter.js @@ -0,0 +1,66 @@ +let nock = require('nock'); + +module.exports.hash = "ae6e959f9b0a752f4a32b51177efba69"; + +module.exports.testInfo = {"uniqueName":{"container":"container159049021276301570","blob":"blob159049021303507117"},"newDate":{}} + +nock('https://fakestorageaccount.blob.core.windows.net:443', {"encodedQueryParams":true}) + .put('/container159049021276301570') + .query(true) + .reply(201, "", [ 'Transfer-Encoding', + 'chunked', + 'Last-Modified', + 'Tue, 26 May 2020 10:50:12 GMT', + 'ETag', + '"0x8D80162914B8427"', + 'Server', + 'Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0', + 'x-ms-request-id', + '1abc4420-101e-000c-614b-3321fe000000', + 'x-ms-client-request-id', + 'b39c7bca-7bfd-4c76-b6ed-aec46794d5f1', + 'x-ms-version', + '2019-12-12', + 'Date', + 'Tue, 26 May 2020 10:50:11 GMT' ]); + +nock('https://fakestorageaccount.blob.core.windows.net:443', {"encodedQueryParams":true}) + .put('/container159049021276301570/blob159049021303507117', "Hello World") + .reply(201, "", [ 'Transfer-Encoding', + 'chunked', + 'Content-MD5', + 'sQqNsWTgdUEFt6mb5y4/5Q==', + 'Last-Modified', + 'Tue, 26 May 2020 10:50:13 GMT', + 'ETag', + '"0x8D80162917555A9"', + 'Server', + 'Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0', + 'x-ms-request-id', + '1abc4422-101e-000c-624b-3321fe000000', + 'x-ms-client-request-id', + '79def682-2bcd-4b40-abf8-9158cfecf0ae', + 'x-ms-version', + '2019-12-12', + 'x-ms-content-crc64', + 'YeJLfssylmU=', + 'x-ms-request-server-encrypted', + 'true', + 'Date', + 'Tue, 26 May 2020 10:50:13 GMT' ]); + +nock('https://fakestorageaccount.blob.core.windows.net:443', {"encodedQueryParams":true}) + .delete('/container159049021276301570') + .query(true) + .reply(202, "", [ 'Transfer-Encoding', + 'chunked', + 'Server', + 'Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0', + 'x-ms-request-id', + '1abc4423-101e-000c-634b-3321fe000000', + 'x-ms-client-request-id', + 'd97b06c9-2230-4cf6-a478-d719585cd306', + 'x-ms-version', + '2019-12-12', + 'Date', + 'Tue, 26 May 2020 10:50:13 GMT' ]); diff --git a/sdk/storage/storage-blob/recordings/node/blobclient_nodejs_only/recording_query_should_work_with_access_conditions.js b/sdk/storage/storage-blob/recordings/node/blobclient_nodejs_only/recording_query_should_work_with_access_conditions.js new file mode 100644 index 000000000000..10aa5cecce0c --- /dev/null +++ b/sdk/storage/storage-blob/recordings/node/blobclient_nodejs_only/recording_query_should_work_with_access_conditions.js @@ -0,0 +1,66 @@ +let nock = require('nock'); + +module.exports.hash = "96fefb99c10994e85cd42d9cabfa032c"; + +module.exports.testInfo = {"uniqueName":{"container":"container159049020501200734","blob":"blob159049020528500202"},"newDate":{}} + +nock('https://fakestorageaccount.blob.core.windows.net:443', {"encodedQueryParams":true}) + .put('/container159049020501200734') + .query(true) + .reply(201, "", [ 'Transfer-Encoding', + 'chunked', + 'Last-Modified', + 'Tue, 26 May 2020 10:50:05 GMT', + 'ETag', + '"0x8D801628CACE171"', + 'Server', + 'Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0', + 'x-ms-request-id', + '1abc43f7-101e-000c-454b-3321fe000000', + 'x-ms-client-request-id', + '19fa1705-8ee5-4a18-8d95-7607f1c830f0', + 'x-ms-version', + '2019-12-12', + 'Date', + 'Tue, 26 May 2020 10:50:04 GMT' ]); + +nock('https://fakestorageaccount.blob.core.windows.net:443', {"encodedQueryParams":true}) + .put('/container159049020501200734/blob159049020528500202', "Hello World") + .reply(201, "", [ 'Transfer-Encoding', + 'chunked', + 'Content-MD5', + 'sQqNsWTgdUEFt6mb5y4/5Q==', + 'Last-Modified', + 'Tue, 26 May 2020 10:50:05 GMT', + 'ETag', + '"0x8D801628CD6B4F4"', + 'Server', + 'Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0', + 'x-ms-request-id', + '1abc43f9-101e-000c-464b-3321fe000000', + 'x-ms-client-request-id', + '454eb1f6-660a-4559-838b-49dead83fff0', + 'x-ms-version', + '2019-12-12', + 'x-ms-content-crc64', + 'YeJLfssylmU=', + 'x-ms-request-server-encrypted', + 'true', + 'Date', + 'Tue, 26 May 2020 10:50:05 GMT' ]); + +nock('https://fakestorageaccount.blob.core.windows.net:443', {"encodedQueryParams":true}) + .delete('/container159049020501200734') + .query(true) + .reply(202, "", [ 'Transfer-Encoding', + 'chunked', + 'Server', + 'Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0', + 'x-ms-request-id', + '1abc43fa-101e-000c-474b-3321fe000000', + 'x-ms-client-request-id', + '959877c7-f652-4648-a15b-a6430ce10b18', + 'x-ms-version', + '2019-12-12', + 'Date', + 'Tue, 26 May 2020 10:50:05 GMT' ]); diff --git a/sdk/storage/storage-blob/recordings/node/blobclient_nodejs_only/recording_query_should_work_with_blob_properties.js b/sdk/storage/storage-blob/recordings/node/blobclient_nodejs_only/recording_query_should_work_with_blob_properties.js new file mode 100644 index 000000000000..077822eab309 --- /dev/null +++ b/sdk/storage/storage-blob/recordings/node/blobclient_nodejs_only/recording_query_should_work_with_blob_properties.js @@ -0,0 +1,66 @@ +let nock = require('nock'); + +module.exports.hash = "8a0c2d5fbf6f1d5b0f8147a26e784795"; + +module.exports.testInfo = {"uniqueName":{"container":"container159049021092104171","blob":"blob159049021119406322"},"newDate":{}} + +nock('https://fakestorageaccount.blob.core.windows.net:443', {"encodedQueryParams":true}) + .put('/container159049021092104171') + .query(true) + .reply(201, "", [ 'Transfer-Encoding', + 'chunked', + 'Last-Modified', + 'Tue, 26 May 2020 10:50:11 GMT', + 'ETag', + '"0x8D8016290328DE9"', + 'Server', + 'Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0', + 'x-ms-request-id', + '1abc4412-101e-000c-574b-3321fe000000', + 'x-ms-client-request-id', + '2db43313-3255-490a-a1dd-d9e32d7b23d6', + 'x-ms-version', + '2019-12-12', + 'Date', + 'Tue, 26 May 2020 10:50:10 GMT' ]); + +nock('https://fakestorageaccount.blob.core.windows.net:443', {"encodedQueryParams":true}) + .put('/container159049021092104171/blob159049021119406322', "Hello World") + .reply(201, "", [ 'Transfer-Encoding', + 'chunked', + 'Content-MD5', + 'sQqNsWTgdUEFt6mb5y4/5Q==', + 'Last-Modified', + 'Tue, 26 May 2020 10:50:11 GMT', + 'ETag', + '"0x8D80162905C5FDD"', + 'Server', + 'Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0', + 'x-ms-request-id', + '1abc4415-101e-000c-584b-3321fe000000', + 'x-ms-client-request-id', + '7ef7505d-cd8e-4e86-a350-4775105f82b3', + 'x-ms-version', + '2019-12-12', + 'x-ms-content-crc64', + 'YeJLfssylmU=', + 'x-ms-request-server-encrypted', + 'true', + 'Date', + 'Tue, 26 May 2020 10:50:10 GMT' ]); + +nock('https://fakestorageaccount.blob.core.windows.net:443', {"encodedQueryParams":true}) + .delete('/container159049021092104171') + .query(true) + .reply(202, "", [ 'Transfer-Encoding', + 'chunked', + 'Server', + 'Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0', + 'x-ms-request-id', + '1abc441a-101e-000c-5d4b-3321fe000000', + 'x-ms-client-request-id', + '2b9441d3-d5c6-4776-8aa6-d8fd8f52b1d4', + 'x-ms-version', + '2019-12-12', + 'Date', + 'Tue, 26 May 2020 10:50:10 GMT' ]); diff --git a/sdk/storage/storage-blob/recordings/node/blobclient_nodejs_only/recording_query_should_work_with_csv_input_and_output_configurations.js b/sdk/storage/storage-blob/recordings/node/blobclient_nodejs_only/recording_query_should_work_with_csv_input_and_output_configurations.js new file mode 100644 index 000000000000..8b7c7de87605 --- /dev/null +++ b/sdk/storage/storage-blob/recordings/node/blobclient_nodejs_only/recording_query_should_work_with_csv_input_and_output_configurations.js @@ -0,0 +1,66 @@ +let nock = require('nock'); + +module.exports.hash = "e040650f63a5e8c56babaa30c78ad498"; + +module.exports.testInfo = {"uniqueName":{"container":"container159049021675907732","blob":"blob159049021703108035"},"newDate":{}} + +nock('https://fakestorageaccount.blob.core.windows.net:443', {"encodedQueryParams":true}) + .put('/container159049021675907732') + .query(true) + .reply(201, "", [ 'Transfer-Encoding', + 'chunked', + 'Last-Modified', + 'Tue, 26 May 2020 10:50:16 GMT', + 'ETag', + '"0x8D8016293AD3BE9"', + 'Server', + 'Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0', + 'x-ms-request-id', + '1abc4432-101e-000c-6d4b-3321fe000000', + 'x-ms-client-request-id', + 'd4c5bfcf-fe11-4814-979b-a7935de0bf46', + 'x-ms-version', + '2019-12-12', + 'Date', + 'Tue, 26 May 2020 10:50:16 GMT' ]); + +nock('https://fakestorageaccount.blob.core.windows.net:443', {"encodedQueryParams":true}) + .put('/container159049021675907732/blob159049021703108035', "Hello World") + .reply(201, "", [ 'Transfer-Encoding', + 'chunked', + 'Content-MD5', + 'sQqNsWTgdUEFt6mb5y4/5Q==', + 'Last-Modified', + 'Tue, 26 May 2020 10:50:17 GMT', + 'ETag', + '"0x8D8016293D70C51"', + 'Server', + 'Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0', + 'x-ms-request-id', + '1abc4434-101e-000c-6e4b-3321fe000000', + 'x-ms-client-request-id', + 'dd58bf1e-8f3e-4ea4-a435-25ce1f415d63', + 'x-ms-version', + '2019-12-12', + 'x-ms-content-crc64', + 'YeJLfssylmU=', + 'x-ms-request-server-encrypted', + 'true', + 'Date', + 'Tue, 26 May 2020 10:50:16 GMT' ]); + +nock('https://fakestorageaccount.blob.core.windows.net:443', {"encodedQueryParams":true}) + .delete('/container159049021675907732') + .query(true) + .reply(202, "", [ 'Transfer-Encoding', + 'chunked', + 'Server', + 'Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0', + 'x-ms-request-id', + '1abc4436-101e-000c-6f4b-3321fe000000', + 'x-ms-client-request-id', + '5aa69791-9d06-47ed-b819-e866995b91af', + 'x-ms-version', + '2019-12-12', + 'Date', + 'Tue, 26 May 2020 10:50:17 GMT' ]); diff --git a/sdk/storage/storage-blob/recordings/node/blobclient_nodejs_only/recording_query_should_work_with_empty_results.js b/sdk/storage/storage-blob/recordings/node/blobclient_nodejs_only/recording_query_should_work_with_empty_results.js new file mode 100644 index 000000000000..99a3d11e1e31 --- /dev/null +++ b/sdk/storage/storage-blob/recordings/node/blobclient_nodejs_only/recording_query_should_work_with_empty_results.js @@ -0,0 +1,66 @@ +let nock = require('nock'); + +module.exports.hash = "70b8f4226da6556dbebcefbdb4064498"; + +module.exports.testInfo = {"uniqueName":{"container":"container159049021000203886","blob":"blob159049021030405476"},"newDate":{}} + +nock('https://fakestorageaccount.blob.core.windows.net:443', {"encodedQueryParams":true}) + .put('/container159049021000203886') + .query(true) + .reply(201, "", [ 'Transfer-Encoding', + 'chunked', + 'Last-Modified', + 'Tue, 26 May 2020 10:50:10 GMT', + 'ETag', + '"0x8D801628FA660F9"', + 'Server', + 'Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0', + 'x-ms-request-id', + '1abc440e-101e-000c-544b-3321fe000000', + 'x-ms-client-request-id', + 'b4fd15a8-a9d4-4c39-b4c9-470ff29d060d', + 'x-ms-version', + '2019-12-12', + 'Date', + 'Tue, 26 May 2020 10:50:09 GMT' ]); + +nock('https://fakestorageaccount.blob.core.windows.net:443', {"encodedQueryParams":true}) + .put('/container159049021000203886/blob159049021030405476', "Hello World") + .reply(201, "", [ 'Transfer-Encoding', + 'chunked', + 'Content-MD5', + 'sQqNsWTgdUEFt6mb5y4/5Q==', + 'Last-Modified', + 'Tue, 26 May 2020 10:50:10 GMT', + 'ETag', + '"0x8D801628FD4C7DF"', + 'Server', + 'Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0', + 'x-ms-request-id', + '1abc4410-101e-000c-554b-3321fe000000', + 'x-ms-client-request-id', + '32720e51-f5e8-4dbf-974f-7c7032388c1e', + 'x-ms-version', + '2019-12-12', + 'x-ms-content-crc64', + 'YeJLfssylmU=', + 'x-ms-request-server-encrypted', + 'true', + 'Date', + 'Tue, 26 May 2020 10:50:09 GMT' ]); + +nock('https://fakestorageaccount.blob.core.windows.net:443', {"encodedQueryParams":true}) + .delete('/container159049021000203886') + .query(true) + .reply(202, "", [ 'Transfer-Encoding', + 'chunked', + 'Server', + 'Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0', + 'x-ms-request-id', + '1abc4411-101e-000c-564b-3321fe000000', + 'x-ms-client-request-id', + 'fb170e2c-ef6a-49ab-a830-a4a26aebbcc3', + 'x-ms-version', + '2019-12-12', + 'Date', + 'Tue, 26 May 2020 10:50:10 GMT' ]); diff --git a/sdk/storage/storage-blob/recordings/node/blobclient_nodejs_only/recording_query_should_work_with_fatal_error_event.js b/sdk/storage/storage-blob/recordings/node/blobclient_nodejs_only/recording_query_should_work_with_fatal_error_event.js new file mode 100644 index 000000000000..58954bf45f69 --- /dev/null +++ b/sdk/storage/storage-blob/recordings/node/blobclient_nodejs_only/recording_query_should_work_with_fatal_error_event.js @@ -0,0 +1,66 @@ +let nock = require('nock'); + +module.exports.hash = "47d65e9ec5bdaa1fcb744c9fe2908c22"; + +module.exports.testInfo = {"uniqueName":{"container":"container159049021461101822","blob":"blob159049021491407229"},"newDate":{}} + +nock('https://fakestorageaccount.blob.core.windows.net:443', {"encodedQueryParams":true}) + .put('/container159049021461101822') + .query(true) + .reply(201, "", [ 'Transfer-Encoding', + 'chunked', + 'Last-Modified', + 'Tue, 26 May 2020 10:50:14 GMT', + 'ETag', + '"0x8D8016292658C0A"', + 'Server', + 'Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0', + 'x-ms-request-id', + '1abc4428-101e-000c-674b-3321fe000000', + 'x-ms-client-request-id', + '43c8cc19-c939-46c7-b515-bbe586e28c57', + 'x-ms-version', + '2019-12-12', + 'Date', + 'Tue, 26 May 2020 10:50:14 GMT' ]); + +nock('https://fakestorageaccount.blob.core.windows.net:443', {"encodedQueryParams":true}) + .put('/container159049021461101822/blob159049021491407229', "Hello World") + .reply(201, "", [ 'Transfer-Encoding', + 'chunked', + 'Content-MD5', + 'sQqNsWTgdUEFt6mb5y4/5Q==', + 'Last-Modified', + 'Tue, 26 May 2020 10:50:15 GMT', + 'ETag', + '"0x8D801629293F1B9"', + 'Server', + 'Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0', + 'x-ms-request-id', + '1abc442b-101e-000c-684b-3321fe000000', + 'x-ms-client-request-id', + 'ce2eec59-39e9-40dc-a801-f5c4b04c251f', + 'x-ms-version', + '2019-12-12', + 'x-ms-content-crc64', + 'YeJLfssylmU=', + 'x-ms-request-server-encrypted', + 'true', + 'Date', + 'Tue, 26 May 2020 10:50:14 GMT' ]); + +nock('https://fakestorageaccount.blob.core.windows.net:443', {"encodedQueryParams":true}) + .delete('/container159049021461101822') + .query(true) + .reply(202, "", [ 'Transfer-Encoding', + 'chunked', + 'Server', + 'Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0', + 'x-ms-request-id', + '1abc442c-101e-000c-694b-3321fe000000', + 'x-ms-client-request-id', + 'c1aee181-8417-46dc-9d91-c1bd009ef136', + 'x-ms-version', + '2019-12-12', + 'Date', + 'Tue, 26 May 2020 10:50:15 GMT' ]); diff --git a/sdk/storage/storage-blob/recordings/node/blobclient_nodejs_only/recording_query_should_work_with_json_input_and_output_configurations.js b/sdk/storage/storage-blob/recordings/node/blobclient_nodejs_only/recording_query_should_work_with_json_input_and_output_configurations.js new file mode 100644 index 000000000000..6afecc7316b2 --- /dev/null +++ b/sdk/storage/storage-blob/recordings/node/blobclient_nodejs_only/recording_query_should_work_with_json_input_and_output_configurations.js @@ -0,0 +1,66 @@ +let nock = require('nock'); + +module.exports.hash = "da303cb0dbbe576967bf0bcc9e842496"; + +module.exports.testInfo = {"uniqueName":{"container":"container159049021779702431","blob":"blob159049021829309285"},"newDate":{}} + +nock('https://fakestorageaccount.blob.core.windows.net:443', {"encodedQueryParams":true}) + .put('/container159049021779702431') + .query(true) + .reply(201, "", [ 'Transfer-Encoding', + 'chunked', + 'Last-Modified', + 'Tue, 26 May 2020 10:50:17 GMT', + 'ETag', + '"0x8D80162944B94A2"', + 'Server', + 'Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0', + 'x-ms-request-id', + '1abc443e-101e-000c-704b-3321fe000000', + 'x-ms-client-request-id', + '88178f2d-8867-4fac-beeb-ebb683a757fb', + 'x-ms-version', + '2019-12-12', + 'Date', + 'Tue, 26 May 2020 10:50:17 GMT' ]); + +nock('https://fakestorageaccount.blob.core.windows.net:443', {"encodedQueryParams":true}) + .put('/container159049021779702431/blob159049021829309285', "Hello World") + .reply(201, "", [ 'Transfer-Encoding', + 'chunked', + 'Content-MD5', + 'sQqNsWTgdUEFt6mb5y4/5Q==', + 'Last-Modified', + 'Tue, 26 May 2020 10:50:18 GMT', + 'ETag', + '"0x8D8016294980E2D"', + 'Server', + 'Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0', + 'x-ms-request-id', + '1abc4440-101e-000c-714b-3321fe000000', + 'x-ms-client-request-id', + 'f71ba7cb-4550-40e9-818f-46c284b43b5f', + 'x-ms-version', + '2019-12-12', + 'x-ms-content-crc64', + 'YeJLfssylmU=', + 'x-ms-request-server-encrypted', + 'true', + 'Date', + 'Tue, 26 May 2020 10:50:17 GMT' ]); + +nock('https://fakestorageaccount.blob.core.windows.net:443', {"encodedQueryParams":true}) + .delete('/container159049021779702431') + .query(true) + .reply(202, "", [ 'Transfer-Encoding', + 'chunked', + 'Server', + 'Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0', + 'x-ms-request-id', + '1abc4441-101e-000c-724b-3321fe000000', + 'x-ms-client-request-id', + 'a350bf6e-9386-453c-b604-305901974f51', + 'x-ms-version', + '2019-12-12', + 'Date', + 'Tue, 26 May 2020 10:50:18 GMT' ]); diff --git a/sdk/storage/storage-blob/recordings/node/blobclient_nodejs_only/recording_query_should_work_with_large_file.js b/sdk/storage/storage-blob/recordings/node/blobclient_nodejs_only/recording_query_should_work_with_large_file.js new file mode 100644 index 000000000000..f0371a810809 --- /dev/null +++ b/sdk/storage/storage-blob/recordings/node/blobclient_nodejs_only/recording_query_should_work_with_large_file.js @@ -0,0 +1,66 @@ +let nock = require('nock'); + +module.exports.hash = "287ce57afa2ca683639ffe149024b36f"; + +module.exports.testInfo = {"uniqueName":{"container":"container159049021180905322","blob":"blob159049021214800367"},"newDate":{}} + +nock('https://fakestorageaccount.blob.core.windows.net:443', {"encodedQueryParams":true}) + .put('/container159049021180905322') + .query(true) + .reply(201, "", [ 'Transfer-Encoding', + 'chunked', + 'Last-Modified', + 'Tue, 26 May 2020 10:50:11 GMT', + 'ETag', + '"0x8D8016290B9FF0D"', + 'Server', + 'Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0', + 'x-ms-request-id', + '1abc441b-101e-000c-5e4b-3321fe000000', + 'x-ms-client-request-id', + '6b414e74-84d3-4442-9307-12df0cd21261', + 'x-ms-version', + '2019-12-12', + 'Date', + 'Tue, 26 May 2020 10:50:11 GMT' ]); + +nock('https://fakestorageaccount.blob.core.windows.net:443', {"encodedQueryParams":true}) + .put('/container159049021180905322/blob159049021214800367', "Hello World") + .reply(201, "", [ 'Transfer-Encoding', + 'chunked', + 'Content-MD5', + 'sQqNsWTgdUEFt6mb5y4/5Q==', + 'Last-Modified', + 'Tue, 26 May 2020 10:50:12 GMT', + 'ETag', + '"0x8D8016290EE0BCC"', + 'Server', + 'Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0', + 'x-ms-request-id', + '1abc441d-101e-000c-5f4b-3321fe000000', + 'x-ms-client-request-id', + '0885e6eb-9a93-40c5-ae01-66b9cddbc98c', + 'x-ms-version', + '2019-12-12', + 'x-ms-content-crc64', + 'YeJLfssylmU=', + 'x-ms-request-server-encrypted', + 'true', + 'Date', + 'Tue, 26 May 2020 10:50:11 GMT' ]); + +nock('https://fakestorageaccount.blob.core.windows.net:443', {"encodedQueryParams":true}) + .delete('/container159049021180905322') + .query(true) + .reply(202, "", [ 'Transfer-Encoding', + 'chunked', + 'Server', + 'Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0', + 'x-ms-request-id', + '1abc441f-101e-000c-604b-3321fe000000', + 'x-ms-client-request-id', + 'a829abf7-d4fd-458b-8550-b2048199004e', + 'x-ms-version', + '2019-12-12', + 'Date', + 'Tue, 26 May 2020 10:50:11 GMT' ]); diff --git a/sdk/storage/storage-blob/recordings/node/blobclient_nodejs_only/recording_query_should_work_with_non_fatal_error_event.js b/sdk/storage/storage-blob/recordings/node/blobclient_nodejs_only/recording_query_should_work_with_non_fatal_error_event.js new file mode 100644 index 000000000000..a63fc13948e6 --- /dev/null +++ b/sdk/storage/storage-blob/recordings/node/blobclient_nodejs_only/recording_query_should_work_with_non_fatal_error_event.js @@ -0,0 +1,66 @@ +let nock = require('nock'); + +module.exports.hash = "f0edc0e711ff5ae5f1607685a9fb6a14"; + +module.exports.testInfo = {"uniqueName":{"container":"container159049021552904014","blob":"blob159049021613506786"},"newDate":{}} + +nock('https://fakestorageaccount.blob.core.windows.net:443', {"encodedQueryParams":true}) + .put('/container159049021552904014') + .query(true) + .reply(201, "", [ 'Transfer-Encoding', + 'chunked', + 'Last-Modified', + 'Tue, 26 May 2020 10:50:15 GMT', + 'ETag', + '"0x8D8016292F191DF"', + 'Server', + 'Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0', + 'x-ms-request-id', + '1abc442d-101e-000c-6a4b-3321fe000000', + 'x-ms-client-request-id', + 'a3249b89-a823-4be2-b04d-e384942336d3', + 'x-ms-version', + '2019-12-12', + 'Date', + 'Tue, 26 May 2020 10:50:15 GMT' ]); + +nock('https://fakestorageaccount.blob.core.windows.net:443', {"encodedQueryParams":true}) + .put('/container159049021552904014/blob159049021613506786', "Hello World") + .reply(201, "", [ 'Transfer-Encoding', + 'chunked', + 'Content-MD5', + 'sQqNsWTgdUEFt6mb5y4/5Q==', + 'Last-Modified', + 'Tue, 26 May 2020 10:50:16 GMT', + 'ETag', + '"0x8D80162934E62B2"', + 'Server', + 'Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0', + 'x-ms-request-id', + '1abc4430-101e-000c-6b4b-3321fe000000', + 'x-ms-client-request-id', + '375c202b-6210-4766-a0dc-62b6fa9cfc33', + 'x-ms-version', + '2019-12-12', + 'x-ms-content-crc64', + 'YeJLfssylmU=', + 'x-ms-request-server-encrypted', + 'true', + 'Date', + 'Tue, 26 May 2020 10:50:15 GMT' ]); + +nock('https://fakestorageaccount.blob.core.windows.net:443', {"encodedQueryParams":true}) + .delete('/container159049021552904014') + .query(true) + .reply(202, "", [ 'Transfer-Encoding', + 'chunked', + 'Server', + 'Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0', + 'x-ms-request-id', + '1abc4431-101e-000c-6c4b-3321fe000000', + 'x-ms-client-request-id', + '437212b5-c10f-4bc0-9dc5-cfe38a80f109', + 'x-ms-version', + '2019-12-12', + 'Date', + 'Tue, 26 May 2020 10:50:16 GMT' ]); diff --git a/sdk/storage/storage-blob/recordings/node/blobclient_nodejs_only/recording_query_should_work_with_progress_event.js b/sdk/storage/storage-blob/recordings/node/blobclient_nodejs_only/recording_query_should_work_with_progress_event.js new file mode 100644 index 000000000000..211a559c0fae --- /dev/null +++ b/sdk/storage/storage-blob/recordings/node/blobclient_nodejs_only/recording_query_should_work_with_progress_event.js @@ -0,0 +1,66 @@ +let nock = require('nock'); + +module.exports.hash = "b9b98d2d88adaa863394f90d3f62a4a2"; + +module.exports.testInfo = {"uniqueName":{"container":"container159049021368504809","blob":"blob159049021399100385"},"newDate":{}} + +nock('https://fakestorageaccount.blob.core.windows.net:443', {"encodedQueryParams":true}) + .put('/container159049021368504809') + .query(true) + .reply(201, "", [ 'Transfer-Encoding', + 'chunked', + 'Last-Modified', + 'Tue, 26 May 2020 10:50:13 GMT', + 'ETag', + '"0x8D8016291D84D74"', + 'Server', + 'Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0', + 'x-ms-request-id', + '1abc4424-101e-000c-644b-3321fe000000', + 'x-ms-client-request-id', + 'ed28d154-752e-4375-9dce-59adfbb20cea', + 'x-ms-version', + '2019-12-12', + 'Date', + 'Tue, 26 May 2020 10:50:13 GMT' ]); + +nock('https://fakestorageaccount.blob.core.windows.net:443', {"encodedQueryParams":true}) + .put('/container159049021368504809/blob159049021399100385', "Hello World") + .reply(201, "", [ 'Transfer-Encoding', + 'chunked', + 'Content-MD5', + 'sQqNsWTgdUEFt6mb5y4/5Q==', + 'Last-Modified', + 'Tue, 26 May 2020 10:50:14 GMT', + 'ETag', + '"0x8D80162920728A7"', + 'Server', + 'Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0', + 'x-ms-request-id', + '1abc4426-101e-000c-654b-3321fe000000', + 'x-ms-client-request-id', + 'd97c5bdb-2072-453d-8ef8-f535c541da65', + 'x-ms-version', + '2019-12-12', + 'x-ms-content-crc64', + 'YeJLfssylmU=', + 'x-ms-request-server-encrypted', + 'true', + 'Date', + 'Tue, 26 May 2020 10:50:14 GMT' ]); + +nock('https://fakestorageaccount.blob.core.windows.net:443', {"encodedQueryParams":true}) + .delete('/container159049021368504809') + .query(true) + .reply(202, "", [ 'Transfer-Encoding', + 'chunked', + 'Server', + 'Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0', + 'x-ms-request-id', + '1abc4427-101e-000c-664b-3321fe000000', + 'x-ms-client-request-id', + '123a03fa-22cd-4b9a-82b6-da84c0279b74', + 'x-ms-version', + '2019-12-12', + 'Date', + 'Tue, 26 May 2020 10:50:14 GMT' ]); diff --git a/sdk/storage/storage-blob/recordings/node/blobclient_nodejs_only/recording_query_should_work_with_snapshot.js b/sdk/storage/storage-blob/recordings/node/blobclient_nodejs_only/recording_query_should_work_with_snapshot.js new file mode 100644 index 000000000000..07ef970d2191 --- /dev/null +++ b/sdk/storage/storage-blob/recordings/node/blobclient_nodejs_only/recording_query_should_work_with_snapshot.js @@ -0,0 +1,66 @@ +let nock = require('nock'); + +module.exports.hash = "9fa6b4b0cffd24ba43af3867c4547ad6"; + +module.exports.testInfo = {"uniqueName":{"container":"container159049020812502198","blob":"blob159049020846207708"},"newDate":{}} + +nock('https://fakestorageaccount.blob.core.windows.net:443', {"encodedQueryParams":true}) + .put('/container159049020812502198') + .query(true) + .reply(201, "", [ 'Transfer-Encoding', + 'chunked', + 'Last-Modified', + 'Tue, 26 May 2020 10:50:08 GMT', + 'ETag', + '"0x8D801628E881292"', + 'Server', + 'Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0', + 'x-ms-request-id', + '1abc4405-101e-000c-4e4b-3321fe000000', + 'x-ms-client-request-id', + '87e35f42-6c44-44e1-9dce-a7880229747a', + 'x-ms-version', + '2019-12-12', + 'Date', + 'Tue, 26 May 2020 10:50:07 GMT' ]); + +nock('https://fakestorageaccount.blob.core.windows.net:443', {"encodedQueryParams":true}) + .put('/container159049020812502198/blob159049020846207708', "Hello World") + .reply(201, "", [ 'Transfer-Encoding', + 'chunked', + 'Content-MD5', + 'sQqNsWTgdUEFt6mb5y4/5Q==', + 'Last-Modified', + 'Tue, 26 May 2020 10:50:08 GMT', + 'ETag', + '"0x8D801628EBB5CDE"', + 'Server', + 'Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0', + 'x-ms-request-id', + '1abc4407-101e-000c-4f4b-3321fe000000', + 'x-ms-client-request-id', + 'fdea3e39-5f7b-4d3a-8224-db0c21c046c6', + 'x-ms-version', + '2019-12-12', + 'x-ms-content-crc64', + 'YeJLfssylmU=', + 'x-ms-request-server-encrypted', + 'true', + 'Date', + 'Tue, 26 May 2020 10:50:08 GMT' ]); + +nock('https://fakestorageaccount.blob.core.windows.net:443', {"encodedQueryParams":true}) + .delete('/container159049020812502198') + .query(true) + .reply(202, "", [ 'Transfer-Encoding', + 'chunked', + 'Server', + 'Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0', + 'x-ms-request-id', + '1abc4408-101e-000c-504b-3321fe000000', + 'x-ms-client-request-id', + '8907ba07-dd16-4215-b804-df32904dc08b', + 'x-ms-version', + '2019-12-12', + 'Date', + 'Tue, 26 May 2020 10:50:08 GMT' ]); diff --git a/sdk/storage/storage-blob/recordings/node/blobclient_nodejs_only/recording_query_should_work_with_where_conditionals.js b/sdk/storage/storage-blob/recordings/node/blobclient_nodejs_only/recording_query_should_work_with_where_conditionals.js new file mode 100644 index 000000000000..acdfcce09666 --- /dev/null +++ b/sdk/storage/storage-blob/recordings/node/blobclient_nodejs_only/recording_query_should_work_with_where_conditionals.js @@ -0,0 +1,66 @@ +let nock = require('nock'); + +module.exports.hash = "e1455a2b0a5b90c635259b595020e8ad"; + +module.exports.testInfo = {"uniqueName":{"container":"container159049020904409655","blob":"blob159049020931607154"},"newDate":{}} + +nock('https://fakestorageaccount.blob.core.windows.net:443', {"encodedQueryParams":true}) + .put('/container159049020904409655') + .query(true) + .reply(201, "", [ 'Transfer-Encoding', + 'chunked', + 'Last-Modified', + 'Tue, 26 May 2020 10:50:09 GMT', + 'ETag', + '"0x8D801628F143F82"', + 'Server', + 'Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0', + 'x-ms-request-id', + '1abc4409-101e-000c-514b-3321fe000000', + 'x-ms-client-request-id', + 'd0949da6-1973-4501-9474-fa90a511b293', + 'x-ms-version', + '2019-12-12', + 'Date', + 'Tue, 26 May 2020 10:50:08 GMT' ]); + +nock('https://fakestorageaccount.blob.core.windows.net:443', {"encodedQueryParams":true}) + .put('/container159049020904409655/blob159049020931607154', "Hello World") + .reply(201, "", [ 'Transfer-Encoding', + 'chunked', + 'Content-MD5', + 'sQqNsWTgdUEFt6mb5y4/5Q==', + 'Last-Modified', + 'Tue, 26 May 2020 10:50:09 GMT', + 'ETag', + '"0x8D801628F3E11F6"', + 'Server', + 'Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0', + 'x-ms-request-id', + '1abc440b-101e-000c-524b-3321fe000000', + 'x-ms-client-request-id', + '6ee721f2-34e6-41bb-89e7-c47d9647ccba', + 'x-ms-version', + '2019-12-12', + 'x-ms-content-crc64', + 'YeJLfssylmU=', + 'x-ms-request-server-encrypted', + 'true', + 'Date', + 'Tue, 26 May 2020 10:50:08 GMT' ]); + +nock('https://fakestorageaccount.blob.core.windows.net:443', {"encodedQueryParams":true}) + .delete('/container159049020904409655') + .query(true) + .reply(202, "", [ 'Transfer-Encoding', + 'chunked', + 'Server', + 'Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0', + 'x-ms-request-id', + '1abc440c-101e-000c-534b-3321fe000000', + 'x-ms-client-request-id', + '826fbcca-21d7-41f1-8cd8-6229c2d4be2f', + 'x-ms-version', + '2019-12-12', + 'Date', + 'Tue, 26 May 2020 10:50:09 GMT' ]); From c228dfc6c884c488ba32bc782d95e55db29d89ff Mon Sep 17 00:00:00 2001 From: XiaoningLiu Date: Tue, 26 May 2020 19:04:09 +0800 Subject: [PATCH 4/5] [Storage] Quick query test case update --- .../storage-blob/test/node/blobclient.spec.ts | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/sdk/storage/storage-blob/test/node/blobclient.spec.ts b/sdk/storage/storage-blob/test/node/blobclient.spec.ts index 290c5165657e..6ec3d1b89656 100644 --- a/sdk/storage/storage-blob/test/node/blobclient.spec.ts +++ b/sdk/storage/storage-blob/test/node/blobclient.spec.ts @@ -347,7 +347,7 @@ describe("BlobClient Node.js only", () => { assert.deepStrictEqual(result.metadata, metadata); }); - it.only("query should work", async () => { + it("query should work", async () => { recorder.skip("node", "TODO: Avor reader doesn't work with recorder yet."); const csvContent = "100,200,300,400\n150,250,350,450\n"; await blockBlobClient.upload(csvContent, csvContent.length); @@ -356,7 +356,7 @@ describe("BlobClient Node.js only", () => { assert.deepStrictEqual(await bodyToString(response), csvContent); }); - it.only("query should work with access conditions", async () => { + it("query should work with access conditions", async () => { recorder.skip("node", "TODO: Avor reader doesn't work with recorder yet."); const csvContent = "100,200,300,400\n150,250,350,450\n"; const uploadResponse = await blockBlobClient.upload(csvContent, csvContent.length); @@ -372,7 +372,7 @@ describe("BlobClient Node.js only", () => { assert.deepStrictEqual(await bodyToString(response), csvContent); }); - it.only("query should not work with access conditions ifModifiedSince", async () => { + it("query should not work with access conditions ifModifiedSince", async () => { recorder.skip("node", "TODO: Avor reader doesn't work with recorder yet."); const csvContent = "100,200,300,400\n150,250,350,450\n"; await blockBlobClient.upload(csvContent, csvContent.length); @@ -390,7 +390,7 @@ describe("BlobClient Node.js only", () => { assert.fail(); }); - it.only("query should not work with access conditions leaseId", async () => { + it("query should not work with access conditions leaseId", async () => { recorder.skip("node", "TODO: Avor reader doesn't work with recorder yet."); const csvContent = "100,200,300,400\n150,250,350,450\n"; await blockBlobClient.upload(csvContent, csvContent.length); @@ -408,7 +408,7 @@ describe("BlobClient Node.js only", () => { assert.fail(); }); - it.only("query should work with snapshot", async () => { + it("query should work with snapshot", async () => { recorder.skip("node", "TODO: Avor reader doesn't work with recorder yet."); const csvContent = "100,200,300,400\n150,250,350,450\n"; await blockBlobClient.upload(csvContent, csvContent.length); @@ -419,7 +419,7 @@ describe("BlobClient Node.js only", () => { assert.deepStrictEqual(await bodyToString(response), csvContent); }); - it.only("query should work with where conditionals", async () => { + it("query should work with where conditionals", async () => { recorder.skip("node", "TODO: Avor reader doesn't work with recorder yet."); const csvContent = "100,200,300,400\n150,250,350,450\n"; await blockBlobClient.upload(csvContent, csvContent.length); @@ -428,7 +428,7 @@ describe("BlobClient Node.js only", () => { assert.deepStrictEqual(await bodyToString(response), "250\n"); }); - it.only("query should work with empty results", async () => { + it("query should work with empty results", async () => { recorder.skip("node", "TODO: Avor reader doesn't work with recorder yet."); const csvContent = "100,200,300,400\n150,250,350,450\n"; await blockBlobClient.upload(csvContent, csvContent.length); @@ -438,7 +438,7 @@ describe("BlobClient Node.js only", () => { assert.deepStrictEqual(await bodyToString(response), ""); }); - it.only("query should work with blob properties", async () => { + it("query should work with blob properties", async () => { recorder.skip("node", "TODO: Avor reader doesn't work with recorder yet."); const csvContent = "100,200,300,400\n150,250,350,450\n"; await blockBlobClient.upload(csvContent, csvContent.length); @@ -456,7 +456,7 @@ describe("BlobClient Node.js only", () => { assert.deepStrictEqual(typeof response.date, "object"); }); - it.only("query should work with large file", async () => { + it("query should work with large file", async () => { recorder.skip("node", "TODO: Avor reader doesn't work with recorder yet."); // TODO: Avor reader emiter listener leak MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 error listeners added. Use emitter.setMaxListeners() to increase limit const csvContentUnit = "100,200,300,400\n150,250,350,450\n"; @@ -481,7 +481,7 @@ describe("BlobClient Node.js only", () => { assert.ok(downloadedData.equals(uploadedData)); }); - it.only("query should work with aborter", async () => { + it("query should work with aborter", async () => { recorder.skip("node", "TODO: Avor reader doesn't work with recorder yet."); const csvContentUnit = "100,200,300,400\n150,250,350,450\n"; const tempFileLarge = await createRandomLocalFile( @@ -513,7 +513,7 @@ describe("BlobClient Node.js only", () => { assert.fail(); }); - it.only("query should work with progress event", async () => { + it("query should work with progress event", async () => { recorder.skip("node", "TODO: Avor reader doesn't work with recorder yet."); const csvContent = "100,200,300,400\n150,250,350,450\n"; await blockBlobClient.upload(csvContent, csvContent.length); @@ -534,7 +534,7 @@ describe("BlobClient Node.js only", () => { }); }); - it.only("query should work with fatal error event", async () => { + it("query should work with fatal error event", async () => { recorder.skip("node", "TODO: Avor reader doesn't work with recorder yet."); const csvContent = "100,200,300,400\n150,250,350,450\n"; await blockBlobClient.upload(csvContent, csvContent.length); @@ -558,7 +558,7 @@ describe("BlobClient Node.js only", () => { assert.deepStrictEqual(await bodyToString(response), "\n"); }); - it.only("query should work with non fatal error event", async () => { + it("query should work with non fatal error event", async () => { recorder.skip("node", "TODO: Avor reader doesn't work with recorder yet."); const csvContent = "100,hello,300,400\n150,250,350,450\n"; await blockBlobClient.upload(csvContent, csvContent.length); @@ -575,7 +575,7 @@ describe("BlobClient Node.js only", () => { assert.deepStrictEqual(await bodyToString(response), "250\n"); }); - it.only("query should work with CSV input and output configurations", async () => { + it("query should work with CSV input and output configurations", async () => { recorder.skip("node", "TODO: Avor reader doesn't work with recorder yet."); const csvContent = "100.200.300.400!150.250.350.450!180.280.380.480!"; await blockBlobClient.upload(csvContent, csvContent.length); @@ -601,7 +601,7 @@ describe("BlobClient Node.js only", () => { assert.deepStrictEqual(await bodyToString(response), "150!180!"); }); - it.only("query should work with JSON input and output configurations", async () => { + it("query should work with JSON input and output configurations", async () => { recorder.skip("node", "TODO: Avor reader doesn't work with recorder yet."); const recordSeparator = "\n"; const jsonContent = From 5dca4a6485d38455c9432914469bc49fec25a683 Mon Sep 17 00:00:00 2001 From: XiaoningLiu Date: Wed, 27 May 2020 22:22:43 +0800 Subject: [PATCH 5/5] [Storage] Resolve comments --- sdk/storage/storage-blob/src/BlobQueryResponse.browser.ts | 2 +- sdk/storage/storage-blob/src/BlobQueryResponse.ts | 2 +- sdk/storage/storage-blob/src/Clients.ts | 2 +- sdk/storage/storage-blob/src/utils/BlobQuickQueryStream.ts | 2 +- sdk/storage/storage-blob/test/node/blobclient.spec.ts | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/sdk/storage/storage-blob/src/BlobQueryResponse.browser.ts b/sdk/storage/storage-blob/src/BlobQueryResponse.browser.ts index 9ce68bff6747..aa07827a13b0 100644 --- a/sdk/storage/storage-blob/src/BlobQueryResponse.browser.ts +++ b/sdk/storage/storage-blob/src/BlobQueryResponse.browser.ts @@ -20,7 +20,7 @@ import { BlobQuickQueryStreamOptions } from "./utils/BlobQuickQueryStream"; * ONLY AVAILABLE IN BROWSER RUNTIME. * * BlobQueryResponse implements BlobDownloadResponseModel interface, and in browser runtime it will - * parser avor data returned by blob query. + * parse avor data returned by blob query. * * @export * @class BlobQueryResponse diff --git a/sdk/storage/storage-blob/src/BlobQueryResponse.ts b/sdk/storage/storage-blob/src/BlobQueryResponse.ts index cdc9ff16694b..e90bc74321b5 100644 --- a/sdk/storage/storage-blob/src/BlobQueryResponse.ts +++ b/sdk/storage/storage-blob/src/BlobQueryResponse.ts @@ -20,7 +20,7 @@ import { BlobQuickQueryStream, BlobQuickQueryStreamOptions } from "./utils/BlobQ * ONLY AVAILABLE IN NODE.JS RUNTIME. * * BlobQueryResponse implements BlobDownloadResponseModel interface, and in Node.js runtime it will - * parser avor data returned by blob query. + * parse avor data returned by blob query. * * @export * @class BlobQueryResponse diff --git a/sdk/storage/storage-blob/src/Clients.ts b/sdk/storage/storage-blob/src/Clients.ts index b517d17a4b65..d236b9477985 100644 --- a/sdk/storage/storage-blob/src/Clients.ts +++ b/sdk/storage/storage-blob/src/Clients.ts @@ -3410,7 +3410,7 @@ export class BlockBlobClient extends BlobClient { } /** - * Quick query for a JSON or CSV formated blob. + * Quick query for a JSON or CSV formatted blob. * * Example usage (Node.js): * diff --git a/sdk/storage/storage-blob/src/utils/BlobQuickQueryStream.ts b/sdk/storage/storage-blob/src/utils/BlobQuickQueryStream.ts index 723a716108a3..ad7ac101532c 100644 --- a/sdk/storage/storage-blob/src/utils/BlobQuickQueryStream.ts +++ b/sdk/storage/storage-blob/src/utils/BlobQuickQueryStream.ts @@ -39,7 +39,7 @@ const ABORT_ERROR = new AbortError("The operation was aborted."); /** * ONLY AVAILABLE IN NODE.JS RUNTIME. * - * A Node.js BlobQuickQueryStream will internally parser avor data stream for blob query. + * A Node.js BlobQuickQueryStream will internally parse avor data stream for blob query. * * @class BlobQuickQueryStream * @extends {Readable} diff --git a/sdk/storage/storage-blob/test/node/blobclient.spec.ts b/sdk/storage/storage-blob/test/node/blobclient.spec.ts index 6ec3d1b89656..483530b83806 100644 --- a/sdk/storage/storage-blob/test/node/blobclient.spec.ts +++ b/sdk/storage/storage-blob/test/node/blobclient.spec.ts @@ -495,7 +495,7 @@ describe("BlobClient Node.js only", () => { const response = await blockBlobClient.query("select * from BlobStorage", { abortSignal: aborter.signal, onProgress: () => { - // Abort parser when first progress event trigger (by default 4MB) + // Abort parse when first progress event trigger (by default 4MB) aborter.abort(); } });